Spring事务系列--编程

Spring事务失效场景

其他网址

[Java]Spring事务失效的几种原因_Java_菜鸟腾飞-CSDN博客
浅谈Spring中的事务回滚 - zeng1994 - 博客园
Spring事务管理——回滚(rollback-for)控制_Java_ryelqy的博客-CSDN博客

异常类型错误

        声明式事务和注解事务回滚的原理:当被切面切中或者是加了注解的方法中抛出了unchecked exception异常(默认情况)时,Spring会进行事务回滚。

不回滚的情况

  1. 把异常给try catch了,没有手动抛出RuntimeException异常
  2. 抛出的异常不属于运行时异常(如IO异常),因为Spring默认情况下是捕获到运行时异常就回滚

会回滚的情况

  1. 用了try catch,在catch里面再抛出一个 RuntimeException异常。(一般不需要在方法中catch异常)
  2. 在catch后面写一句回滚代码(TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();)来实现回滚,这样的话,就可以在抛异常后也能return 返回值;比较适合需要拿到返回值的场景
  3. 如果让抛出不属于运行时异常也要能回滚,那么可以将Spring默认的回滚时的异常修改为Exception,这样就可以保证碰到什么异常都可以回滚。

情况2示例:

       /** TransactionAspectSupport手动回滚事务:*/
       @Transactional(rollbackFor = { Exception.class })  
       public boolean test() {  
            try {  
               doDbSomeThing();    
            } catch (Exception e) {  
                 e.printStackTrace();     
                 //加上之后抛了异常就能回滚(有这句代码就不需要再手动抛出运行时异常了)
                 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();  
                 return false;
            }  
           return true;
      }

情况3示例:

(1)注解事务,直接在注解上面指定:@Transactional(rollbackFor=Exception.class)
(2)声明式事务,在配置里面添加一个rollback-for:  

示例:


    
        
    
 
    
    
        
        
            
            
            
            
            
            
            
            
            
        
    
    
    
        
    

自调用

另见:《深入浅出SpringBoot2.x》=> 6.5 @Transactional自调用失效问题

会失效的代码

​
public class UserServiceImpl implements UserService {
	@Transactional
	public void modify(Order order) { modifyOrder(order); }
	@Transactional
	public void modifyOrder(Order order) { // update order}
}

        如上所示,如A方法调用B方法,B方法上有注解,AB方法在同一个类中,B方法的注解就不会生效。因为没有经过 Spring 的代理类,默认只有在外部调用事务才会生效,这是最经典事务失效问题了。代码的调用中要求使用代理对象去调用即可。

解决方法的代码

参考:《深入浅出SpringBoot2.x》=> 6.5 @Transactional自调用失效问题

​
public class UserServiceImpl implements UserService,ApplicationContextAware {
	private ApplicationContext applicationContext = null;
	
	// 实现生命周期方法,设置 IoC 容器
	@Override
	public void setApplicationContext(ApplicationContext applicationContext)throws BeansException {
		this.applicationContext = applicationContext;
	}
	
	@Transactional
	public void modify(Order order) {
		UserService userService = applicationContext.getBean(UserService.class);
		userService.modifyOrder(order);
	}
	
	@Transactional
	public void modifyOrder(Order order) { 
		//update order
	}
}

​

原理

        Spring的AopProxy.java,通过调用getProxy,获取代理,目前Spring实现动态代理的方式有两种,一种是cglib,一种是jdk的。两个的实现方式不一样,但是事务失效原因是一样的。cglib要实现代理,就要实现MethodInterceptor接口,例如DynamicAdvisedInterceptor.java,最后通过反射执行方法时传的是目标类,不是代理类,也就是说我们通过aop执行A方法的时候,我们的通过反射调用的实例换成了目标类,这个就不会触发Spring的aop了。所以B方法的事务不会生效。

Spring事务系列--编程_第1张图片

其他场景

失效原因 说明
只读事务 非只读事务才能回滚的,只读事务是不会回滚的
切入点表达式书写错误 如果采用声明式事务,一定要确保切入点表达式书写正确
方法的权限修饰 @Transactional 注解只能应用到 public 的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错,事务也会失效。
数据库引擎 如使用mysql且引擎是MyISAM,则事务会不起作用,原因是MyISAM不支持事务,可以改成InnoDB
加载配置 @Transactional 注解开启配置,必须放到listener里加载,如果放到DispatcherServlet的配置里,事务也是不起作用的。
忘记配置bean spring忘记配置扫描包,bean不在spring容器管理下

你可能感兴趣的:(Spring事务系列--编程)