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

2022-08-20 09:36:58

1. 需求示例

需求伪代码如下:

@ServicepublicclassJysdService{publicStringtest1(args){try{test2(args);}catch(Exception e){return"异常";}....}@Transactional(rollbackFor=Exception.class)publicIntegertest2(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)publicStringtest2(){try{
        数据入库}catch(Exception e){return"异常";}return"";}

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

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

@Transactional(rollbackFor=Exception.class)publicStringtest2()throwsException{try{
        数据入库}catch(Exception e){
        logger.info(e.toString());thrownewException();// 抛出异常}return"";}

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

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

外部代理解决方法示例:

//解决方法publicStringtest1(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

@ComponentpublicclassSpringUtilimplementsApplicationContextAware{privatestaticApplicationContext applicationContext=null;@OverridepublicvoidsetApplicationContext(ApplicationContext applicationContext)throwsBeansException{SpringUtil.applicationContext= applicationContext;}publicstatic<T>TgetBean(Class<T> cla){return applicationContext.getBean(cla);}publicstatic<T>TgetBean(String name,Class<T> cal){return applicationContext.getBean(name, cal);}publicstaticObjectgetBean(String name){return applicationContext.getBean(name);}publicstaticStringgetProperty(String key){return applicationContext.getBean(Environment.class).getProperty(key);}}

参考文章

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

  • 作者:乙壳虫
  • 原文链接:https://blog.csdn.net/wtwcsdn123/article/details/122982714
    更新时间:2022-08-20 09:36:58