场景

明明使用Spring的AOP托管所有事务,在每个Service的函数中也加上了@Transactional注解,可依然还是出现数据不一致,事务不符合预期的情况呢?代码没报错,运行日志也无异常,怎么办呢?

答案

也许不是你的语法没掌握好,不是注解没选对,而只是你使用的姿势不太对!没错,就是使用姿势不对。毕竟Aop这玩意都是动态代理干的活,动态代理不懂的,欢迎咨询度娘或者谷歌。动态代理带来的坏处就是,IDE(不论eclipse或idea)对动态运行的程序无法进行静态差错,因此只会进行常规的语法检查(这也不能怪IDE,毕竟AOP不是它擅长的)。

Spring 事务运行机制如下图所示:

Spring里面@Transactional失效场景_java

【坑点】
        坑一:@Transactional放在非public方法上。

原因:AbstractFallbackTransactionAttributeSource类调用computeTransactionAttribute()时,过滤了非public方法上事务配置信息(相当于没有配置无事务运行机制)。

Spring里面@Transactional失效场景_运行机制_02

所以:对于protected和private上定义的事务注解,spring Aop会自动无视的,这是最容易忽略的点。

坑二:不注意事务上下文环境

原因:错误配置事务。

TransactionDefinition.PROPAGATION_SUPPORTS

如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行

TransactionDefinition.PROPAGATION_NOT_SUPPORTED

以非事务方式运行,如果当前存在事务,则把当前事务挂起

TransactionDefinition.PROPAGATION_NEVER

以非事务方式运行,如果当前存在事务,则抛出异常

所以:如果存在调用链的,请务必注意所有方式是否应该在一个事务环境中,如非必要,不要修改默认的传播特性。

坑三:未正确设置rollbackfor

原因:spring默认会对Error和RuntimeException进行事务回滚,但是其他异常则不会处理。放一个图你就知道了

Spring里面@Transactional失效场景_数据库_03

所以:对于一些明确回滚的异常,直接在rollbackfor里面指定好。

Spring里面@Transactional失效场景_运行机制_04

坑四:try...catch...滥用

原因:spring Aop回滚的机制是捕获了应用程序抛出的指定异常,如果你try...catch...了,那Spring 会错误的认为当前运行正常,事务应该Commit。所以,此时的try...catch...就是画蛇添足了。

所以:尽量不要在业务方法中使用try...catch来捕获你的异常,防止影响了事务。

坑五:跨了线程的事务

       原因:如果事务方法内,开启了新线程去执行其他事务方法也是不受当前事务方法控制的。因为不同线程拥有的threadlocal 不一样。

所以:当你需要明确开启新线程,请分开处理。

坑六:数据库不支持

原因:这种情况出现的概率并不高,事务能否生效数据库引擎是否支持事务是关键。常用的MySQL数据库默认使用支持事务的innodb引擎。一旦数据库引擎切换成不支持事务的myisam,那事务就从根本上失效了。

以上6种方式,是日常开发当中比较容易出现的坑,请避免出现。还有其它的坑点,欢迎留言补充,谢谢。