spring事务失效原因及解决方案

1. 需求示例

需求伪代码如下:

@Service
public class JysdService {
    public String test1(args) {
        try {
            test2(args);
        } catch (Exception e) {
            return "异常";
        }
        ....
    }
    
    @Transactional(rollbackFor = Exception.class)
	public Integer test2(args) {
        数据库操作
    }
}

如上代码,需要在方法1(test1)中调用方法2(test2),其中方法2启用事务。

2. spring中事务的注意事项

spring的声明式事务是基于代理模式的。其实代理模式相当简单, 就是将另一个类包裹在我们的类外面, 在调用我们创建的方法之前, 先经过外面的方法, 进行一些处理, 返回之前, 再进行一些操作。

2.1 声明事务的方法不能用private、static、final修饰

2.2 调用事务方法时,不能用this.xxx

原因是由于JDK的动态代理。 在SpringIoC容器中返回的调用的对象是代理对象而不是真实的对象,只有被动态代理直接调用的才会产生事务。这里的this是(JysdService)真实对象而不是代理对象,所以事务会失效,不会回滚。

同理,上面的示例代码中,在方法1中调用方法2,实际上就是this.xxx调用方式,所以这样方法2的事务会失效

2.3 不要用try在事务方法内部处理异常,导致没有异常抛出

例如

@Transactional(rollbackFor = Exception.class)
public String test2() {
    try {
        数据入库
    } catch (Exception e) {
        return "异常";
    }
    return "";
}

上面这种方法事务是不会回滚的,因为你在方法内部已经把异常处理掉了,所以代理对象是捕获不到异常的,也就不会回滚。

你可以针对异常做一些处理,比如打印日志等,但是一定要把异常抛出去,例如

@Transactional(rollbackFor = Exception.class)
public String test2() throws Exception {
    try {
        数据入库
    } catch (Exception e) {
        logger.info(e.toString());
        throw new Exception();  // 抛出异常
    }
    return "";
}

3. 针对同一个类中其他方法调用时,事务不生效的解决方案

不要在同一个类里面直接调用事务方法,即不要在service类的方法1中直接调用方法2(事务),这样是不会生效的。事务方法必须从外部调用。例如可以直接在controller中调用service中的事务方法。或者在service中创建一个当前类的外部代理对象,然后通过这个代理对象来调用当前类中的事务方法

外部代理解决方法示例:

//解决方法
public String test1(args) {
    try {
        JysdService proxy =(JysdService) AopContext.currentProxy();
        proxy.test2(atrs);
    } catch (Exception e) {
        return "异常";
    }
    ....
}

但是我这样使用时报错了:Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available

解决方法参考(亲测有效):

JysdService jysdService = SpringUtil.getBean(this.getClass());   //SpringUtil工具类见下面代码

SpringUtil.java

@Component
public class SpringUtil implements ApplicationContextAware {

	private static ApplicationContext applicationContext = null;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		SpringUtil.applicationContext = applicationContext;
	}

	public static <T> T getBean(Class<T> cla) {
		return applicationContext.getBean(cla);
	}

	public static <T> T getBean(String name, Class<T> cal) {
		return applicationContext.getBean(name, cal);
	}

	public static Object getBean(String name){
		return applicationContext.getBean(name);
	}

	public static String getProperty(String key) {
		return applicationContext.getBean(Environment.class).getProperty(key);
	}
}

参考文章

https://blog.csdn.net/mameng1988/article/details/85548812

你可能感兴趣的:(Java,spring,事务,java)