@Transactional 失效的原因
- 前言
- 一、同一个类里的方法调用
- 二、方法不是public修饰
- 三、方法中使用try catch
- 四、rollbackFor设置原因
-
- 1、rollbackFor默认是捕获RunTimeException,其他异常不会回滚
- 2、propagation配置错误 这种失效是由于配置错误,若是错误的配置以下三种 propagation,事务将不会发生回滚。
- 五、数据库引擎不支持事务
- 扩展 :避免长事务
-
- 1、何为长事务?
- 2、避免长事务
- 总结
前言
这里要说明下,以下五点并不是我自己总结出来的,而是参考了网上众多大佬解答。
我自己仅是用代码写了个demo,验证了一下,并记录下来以便工作学习之用。
一、同一个类里的方法调用
原因:
spring 在扫描bean的时候会扫描方法上是否包含@Transactional注解,如果包含,spring会为这个bean动态地生成一个子类(即代理类,proxy),代理类是继承原来那个bean的。此时,当这个有注解的方法被调用的时候,实际上是由代理类来调用的,代理类在调用之前就会启动transaction。然而,如果这个有注解的方法是被同一个类中的其他方法调用的,那么该方法的调用并没有通过代理类,而是直接通过原来的那个bean,所以就不会启动transaction,我们看到的现象就是@Transactional注解无效。
解决方法一,代码如下:
@Override@Transactional(rollbackFor = Exception.class)public void testTraOne(UserDemoEntity model) throws Exception {
this.save(model);throw new Exception();}//从上下文中获取bean 事务生效@Autowiredprivate ApplicationContext applicationContext;@Overridepublic void testTraThree(UserDemoEntity model) throws Exception {
UserDemoServiceImpl impl = applicationContext.getBean(UserDemoServiceImpl.class);impl.testTraOne(model);}
解决方法二:
启动类添加@EnableAspectJAutoProxy(exposeProxy = true),方法内使用AopContext.currentProxy()获得代理类,使用事务。
//启动类上(SpringBootApplication.java)加上注解@EnableAspectJAutoProxy(exposeProxy = true)
@SpringBootApplication
public class SpringBootApplication {
//xxx
}//实现类(UserDemoServiceImpl)获取代理@Overridepublic void testTraThree(UserDemoEntity model) throws Exception {
UserDemoServiceImpl impl = (UserDemoServiceImpl)AopContext.currentProxy();;impl.testTraOne(model);}
二、方法不是public修饰
computeTransactionAttribute方法在获取Transactional注解信息时, 会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息
备注:在idea的语法检查中就会报错,所以这点…
三、方法中使用try catch
如果catch了异常 处理完没有抛出 那么也会导致事务失效
解决方法:catch处理了之后,再throw 出去…(用了catch又throw,感觉多此一举呢)
解决代码如下:
@Override@Transactional(rollbackFor = Exception.class)public void testTraFour(UserDemoEntity model) throws Exception {
try {
this.save(model);throw new Exception("手动抛出异常");}catch (Exception e){
log.error("手动抛出异常");throw new Exception("手动抛出异常");}}
四、rollbackFor设置原因
1、rollbackFor默认是捕获RunTimeException,其他异常不会回滚
解决方法: @Transactional(rollbackFor = Exception.class)
自定义什么异常会回滚
2、propagation配置错误 这种失效是由于配置错误,若是错误的配置以下三种 propagation,事务将不会发生回滚。
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
五、数据库引擎不支持事务
如:mysql的 innodb支持 但是myisam不支持
扩展 :避免长事务
1、何为长事务?
顾名思义就是运行时间比较长,长时间未提交的事务,也可以称之为大事务 。
长事务引发的常见危害有:
1:数据库连接池被占满,应用无法获取连接资源;
2:容易引发数据库死锁;
3:数据库回滚时间长;
4:在主从架构中会导致主从延时变大。
2、避免长事务
解决方法一:手动管理事务的开启、提交、回滚等操作,使用TransactionTemplate 。
@Autowired
private TransactionTemplate transactionTemplate; ... public void save(RequestBill requestBill) {
transactionTemplate.execute(transactionStatus -> {
requestBillDao.save(requestBill);//保存明细表requestDetailDao.save(requestBill.getDetail());return Boolean.TRUE; });
}
解决方法二:对方法进行拆分,将不需要事务管理的逻辑与事务操作分开 。
要注意避免发生第一点:同一个类里的方法调用。
总结
暂时从网上总结到以上几点,后续有收集到新的原因,也会更新到这里。