事务不生效

前言

支付模块是事务操作,如果在支付逻辑执行的过程中有异常,需要做回滚。比如你支付=>订单表里插入了订单数据=>成功=>然后调起支付失败,此时需要回滚订单表数据。

transactional注解不生效的原因

1、检查你的方法是不是public的。@Transactional注解只能应用到public可见度的方法上,如果应用在protected、private或者package可见度的方法上,也不会报错,不过事务设置不会起作用。

2、检查你的异常类型是不是unchecked异常。默认情况下,Spring会对unchecked异常进行事务回滚,如果是checked异常则不回滚。如空指针异常、算术异常等,会被回滚;文件读写、网络出问题,spring就没法回滚了。如果你想check异常也回滚怎么办,注解上面写明异常类型即可:

@Transactional(rollbackFor = Exception.class)

类型的还有norollbackFor,自定义不回滚的异常。

3、是否在service中进行了try…catch的操作,由于已经被捕获异常,故事务也不会回滚。如果非要在service中try…catch异常,又想要事务回滚,可在catch块中抛出运行时异常:

try{
    ....  
}catch(Exception e){
    logger.error("",e);
    throw new RuntimeException;
}

这种方法有个不足之处,就是不能在catch块中存在return子句,若想捕获异常时回滚事务,同时返回提示信息,可以使用手动回滚:

try{
    ...
}catch(Exception e){
    logger.error("",e);
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    return ERROR_MESSAGE;
}

另外说明一下,在controller层捕获了service层的异常,事务还会回滚吗?答案是会的,只要你service层抛出了异常,并且你加的事务可以处理这个异常,也就是rollbackFor = Exception.class这个符合你抛出的异常,不管外面有没有捕获都可以回滚。

4、是否开启了对注解的解析:

<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
          id="transactionManager">
        <property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

5、数据库引擎要支持事务,如果是mysql,注意表要使用支持事务的引擎,比如innodb,如果是myisam,事务是不起作用的。

6、spring是否扫描到你这个包,如下是扫描到org.test下面的包:

<context:component-scan base-package="org.test" ></context:component-scan>

7、检查是不是同一个类中的方法调用(如A方法无@Transactional注解,调用了一个有@Transactional注解的方法),这样事务也是不生效的。

原因:我们知道spring事务的原理是采用aop,其实质是给你的service对象另外造了一个代理对象。这个代理对象和你的service对象有同样的方法名,但是额外在你的 方法前后加了对事务的处理,处理完后再通过this调用你自己service对象的相关方法。
所以,当调用方是处于你的service的外面,你拿到的其实不是你写的service的实例,是spring加上事务处理特性后的代理对象,这样子调用事务是能够生效的。
但是,你用service 内部方法调用注解了@Transactional的方法, 内部方法得通过this调用这个注解了@Transactional的方法,此时this对象并不是spring的代理对象,而是你自己写的service的实例.而你自己写的service实例的所有方法都没有事务特性,所以事务必然不生效了。

8、请确保你的业务和事务入口在同一个线程里,否则事务也是不生效的,比如下面代码事务不生效:

@Transactional
@Override
public void save(User user1, User user2) {
    new Thread(() -> {
          saveError(user1, user2);
          System.out.println(1 / 0);
    }).start();
}

总结

  1. 数据库不支持事务
  2. 注解放在了私有方法上
  3. 类内部调用,类里面使用this调用本类方法(this通常省略)
  4. 未捕获异常
  5. 没有被Spring管理(没加@Service等注解)
  6. 多线程场景

这些场景都会导致@Transactional失效

Transactional注解的使用场景和注意点

分布式事务

分布式事务,能避免尽量避免,使用分布式事务中间件不是变慢这么简单,比如你用了事务包围里的一个函数是有一个数据库的写操作,spring会通过jdbc进行锁表的,而此时你又要RPC去调用其他服务,其他服务同样又要都这个表进行写入操作,此时就会死锁。所以如果是分布式事务的话,能避免就避免,因为不同的service是不同的人做,设计到的数据读写,你不是很清楚的情况下,很容易这样的。

单一表写操作

单一表的写操作,就没必要加事务了,会降低性能,多表才需要加事务

mysql会帮你做好并发处理的,单表操作,对于程序逻辑而言是原子性的,不用加事务处理。

pagehelper github上有给这个中间件,分页用的,很好用,SQL拦截的

待补充。。。


   转载规则


《事务不生效》 锦泉 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录