导致spring事务失效的几种场景
分享架构师全套视频,50G资料,涵盖所有框架知识点,最牛逼技术栈!
(关注“白码啸西风”,回复“0104”获得:网盘密码)
1.事务失效的7种情况
1)未启用spring事务管理功能
2)方法不是public类型的
3)数据源未配置事务管理器
4)自身调用问题
5)异常类型错误
6)异常被捕获
7)业务和spring事务代码必须在一个线程中
1.1未启用spring事务管理功能
@EnableTransactionManagement 注解用于启用spring事务自动管理功能
如果引入了autoconfigure,TransactionAutoConfiguration.class会自动启用事务管理功能。
1.2方法不是public类型
@Transactional 注解可以用到类上、接口上和public方法上,如果是非public方法,事务将不会生效,主要是spring代理机制导致
1.3数据源未配置事务管理器
spring是通过事务管理器来管理事务的,一定要配置事务管理器
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
如果引入了autoconfigure,DataSourceTransactionManagerAutoConfiguration.class会自动创建事务管理器
1.4自身调用问题
spring是通过aop的方式实现的事务管理,为每个需要事务管理的bean创建代理对象, 然后通过代理对象拦截了目标方法的执行,在方法前后添加了事务的功能,所以必须通过代理对象调用目标方法的时候,事务才会起效。
@Service
public class TransactionDemoService {
public void m1(){
this.m2();;
}
@Transactional(rollbackFor = Exception.class)
public void m2(){
//db操作
}
}
以上代码中,调用m1方法,m2方法中的事务是不会生效的。因为m1中调用m2是通过this调用的,不是通过代理对象。 this.m2()不会被拦截,所以事务是无效的,如果外部直接调用通过TransactionDemoService这个bean来调用m2方法,事务是有效的,上面代码可以做一下调整,如下,在TransactionDemoService中注入了自己,此时m1中的m2事务是生效的 。
@Service
public class TransactionDemoService {
@Autowired
private TransactionDemoService self;
public void m1(){
self.m2();;
}
@Transactional(rollbackFor = Exception.class)
public void m2(){
//db操作
}
}
或者,使用AopContext也能解决这个问题,如下:
@Service
public class TransactionDemoService {
public void m1(){
((TransactionDemoService)AopContext.currentProxy()).m2();
}
@Transactional(rollbackFor = Exception.class)
public void m2(){
//db操作
}
}
注意,当使用AopContext时, 需要使用@EnableAspectJAutoProxy(exposeProxy = true)来暴露AOP的Proxy对象才行,否则会报异常。
1.5异常类型错误
spring的事务回滚机制为,对代理的方法进行try catch 当捕获到有指定的异常时,spring自动对事务进行回滚。但是, 并不是任何异常情况下,spring都会回滚事务,默认情况下,RuntimeException和Error的情况下,spring事务才会回滚。
同时,也可以自定义回滚得异常类型:
@Transactional(rollbackFor = AccessException.class)
public void m2(){
//db操作
}
1.6异常被捕获
当业务代码抛出异常,spring感知到异常的时候,才会进行回滚,如果在业务方法里面捕获了异常,spring无法感知到异常,就不会进行回滚,如下:
@Transactional(rollbackFor = Exception.class)
public void m2(){
try{
//db操作
}catch (Exception e){
logger.error("方法执行出现异常",e);
}
}
1.7业务和spring事务代码必须在一个线程中
spring事务实现中使用了ThreadLocal,ThreadLocal可以实现同一个线程中数据共享,必须是同一个线程的时候,数据才可以共享,这就要求业务代码必须和spring事务的源码执行过程必须在一个线程中,才会受spring事务的控制,比如下面代码,方法内部的子线程内部执行的事务操作将不受m1方法上spring事务的控制。
@Transactional(rollbackFor = Exception.class)
public void m1() {
new Thread() {
//db操作
}.start();
}
·END·