事务失效的7种情况
1. 未启用spring事务管理功能
@EnableTransactionManagement
注解用来启用spring事务自动管理事务的功能,这个注解千万不要忘记写了。
2. 方法不是public类型的
@Transaction
可以用在类上、接口上、public方法上,如果将@Trasaction
用在了非public方法上,事务将无效。
3. 数据源未配置事务管理器
spring是通过事务管理器了来管理事务的,一定不要忘记配置事务管理器了,要注意为每个数据源配置一个事务管理器:
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
4. 自身调用问题
spring是通过Aop的方式,对需要spring管理事务的bean生成了代理对象,然后通过代理对象拦截了目标方法的执行,在方法前后添加了事务的功能,所以必须通过代理对象调用目标方法的时候,事务才会起效。
看下面代码,大家思考一个问题:当外部直接调用s1的时候,s2方法的事务会生效么?
@Component
public class UserService {
public void s1(){
this.s2();
}
@Transactional
public void s2(){
//执行db操作
}
}
显然不会生效,因为s1中通过this的方式调用了s2方法,而this并不是代理对象,this.s2()不会被事务拦截,所以事务是无效的,如果外部直接调用通过UserService这个bean来调用s2方法,事务是有效的,上面代码可以做一下调整,如下,@1在UserService中注入了自己,此时s1中的s2事务是生效的
@Component
public class UserService {
@Autowired //@1
private UserService userService;
public void s1() {
this.userService.s2();
}
@Transactional
public void s2() {
//执行db操作
}
}
重点:必须通过代理对象访问方法,事务才会生效。
5. 异常类型错误
spring事务回滚的机制:对业务方法进行try catch,当捕获到有指定的异常时,spring自动对事务进行回滚,那么问题来了,哪些异常spring会回滚事务呢?
并不是任何异常情况下,spring都会回滚事务,默认情况下,RuntimeException和Error的情况下,spring事务才会回滚。
也可以自定义回滚的异常类型:
@Transactional(rollbackFor = {异常类型列表})
6. 异常被吞了
当业务方法抛出异常,spring感知到异常的时候,才会做事务回滚的操作,若方法内部将异常给吞了,那么事务无法感知到异常了,事务就不会回滚了。
如下代码,事务操作2发生了异常,但是被捕获了,此时事务并不会被回滚
@Transactional
public void s1(){
//事务操作1
try{
//事务操作2,内部抛出了异常
}catch(Exception e){
}
}
7. 业务和spring事务代码必须在一个线程中
spring事务实现中使用了ThreadLocal,ThreadLocal可以实现同一个线程中数据共享,必须是同一个线程的时候,数据才可以共享,这就要求业务代码必须和spring事务的源码执行过程必须在一个线程中,才会受spring事务的控制,比如下面代码,方法内部的子线程内部执行的事务操作将不受s1方法上spring事务的控制
@Transactional
public void s1() {
new Thread() {
//事务操作
}.start();
}