事物失效的常见的几种场景
- 数据库不支持事务
- 注解放在了私有方法上
- 类内部调用
- 未捕获异常
- 多线程场景
- 传播属性设置问题
- 访问权限问题
- 方法用final修饰
- 未被spring管理
数据库不支持事务
使用 MYSQL 数据库引擎都默认使用 Innodb。如果你选用的是其他引擎如:MyISAM 数据库引擎不支持事务。
注解放在了私有方法上
public class TestService{
@Transactional(rollbackFor = Exception.class)
private String ceshi(){
return "测试事务";
}
}
类内部调用
示例1:
@Transactional
public void addStudent() {
Student student = new Student();
student.setAge(20);
student.setName("学生");
studentMapper.insert(student);
int i = 1 / 0;
}
public void addStudent2() {
addStudent();
}
SpringBoot 会扫描@Transactional的类和方法,并通过动态代理实现启动事务的代理方法。
在一个Service内部,事务方法之间的嵌套调用,普通方法和事务方法之间的嵌套调用,都不会开启
新的事务. 是由于Spring采用动态代理机制来实现事务控制,而动态代理最终都是要调用原始对象的,
而原始对象在去调用方法时,是不会再触发代理了!
总结:同一个类中,非事务方法(addStudent2)调用事务方法(addStudent),事务是无法生效的.
示例2:
/**
* 事务生效
*/
public class TestService{
@Transactional(rollbackFor = Exception.class)
public String ceshi1(Long userId){
return getUserByUserId.getUserName();
}
public User getUserByUserId(Long userId){
return new User();
}
}
/**
* 事务不生效
*/
public class TestService{
public String ceshi(Long userId){
return getUserByUserId.getUserName();
}
@Transactional(rollbackFor = Exception.class)
public User getUserByUserId(Long userId){
return new User();
}
}
总结: 同一个类中两个方法相互调用,事务注解应该添加在入口的方法上。如:同一个一个类中 A 方法调用 B,那么注解就应该添加在 A 方法上,如果 B 方法调拨 A 那么注解就应该添加在 B 方法上。
未捕获异常
public class TestService{
@Transactional(rollbackFor = Exception.class)
public String ceshi(Long userId){
try{
return getUserByUserId.getUserName();
}catch(Exception e){
}
}
}
总结: 异常不能自己捕捉,否则 SpringBoot 事务管理器就没法捕捉到异常。无法回滚。如果必须要添加异常捕捉必定要抛出异常。
示例:
public class TestService{
@Transactional(rollbackFor = Exception.class)
public String ceshi(Long userId){
try{
return getUserByUserId.getUserName();
}catch(Exception e){
throw new RuntimeException(); // 手动抛出异常
}
}
}
多线程场景
spring的事务是通过数据库连接来实现的。当前线程中保存了一个map,key是数据源,value是数据库连接。
我们说的同一个事务,实则是指同一个数据库连接,只有拥有同一个数据库连接才能同时提交和回滚。
如果在不同的线程,拿到的数据库连接肯定是不一样的,所以是不同的事务。
事务传播属性设置问题
@Transactional(propagation=Propagation.REQUIRED)
如果有事务, 那么加入事务, 没有的话新建一个(@Transactional 默认使用这种方式)
@Transactional(propagation=Propagation.NOT_SUPPORTED)
容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY)
必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER)
必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS)
如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
访问权限问题
众所周知,java的访问权限主要有四种:private、default、protected、public,它们的权限从左到右,依次变大。
spring要求被代理方法必须是public的。
在AbstractFallbackTransactionAttributeSource类的computeTransactionAttribute方法中有个判断
,如果目标方法不是public,则TransactionAttribute返回null,即不支持事务。
方法用final修饰
spring事务底层使用了aop,也就是通过jdk动态代理或者cglib,帮我们生成了代理类,
在代理类中实现的事务功能。
但如果某个方法用final修饰了,那么在它的代理类中,就无法重写该方法,而添加事务功能。
注意:如果某个方法是static的,同样无法通过动态代理,变成事务方法。
未被spring管理
用spring事务的前提是:对象要被spring管理,需要创建bean实例。
一般情况下,我们通过@Controller、@Service、@Component、@Repository等注解,
可以自动实现bean实例化和依赖注入的功能。
© 版权声明
文章版权归作者所有,未经允许请勿转载。


收藏了,感谢分享