【场景】
明明使用Spring的AOP托管所有事务,在每个Service的函数中也加上了@Transactional注解,可依然还是出现数据不一致,事务不符合预期的情况呢?代码没报错,运行日志也无异常,怎么办呢?
【答案】
也许不是你的语法没掌握好,不是注解没选对,而只是你使用的姿势不太对!没错,就是使用姿势不对。毕竟Aop这玩意都是动态代理干的活,动态代理不懂的,欢迎咨询度娘或者谷歌。动态代理带来的坏处就是,IDE(不论eclipse或idea)对动态运行的程序无法进行静态差错,因此只会进行常规的语法检查(这也不能怪IDE,毕竟AOP不是它擅长的)。
Spring 事务运行机制如下图所示:
【坑点】
坑一:@Transactional放在非public方法上。
原因:AbstractFallbackTransactionAttributeSource类调用computeTransactionAttribute()时,过滤了非public方法上事务配置信息(相当于没有配置无事务运行机制)。
所以:对于protected和private上定义的事务注解,spring Aop会自动无视的,这是最容易忽略的点。
坑二:不注意事务上下文环境
原因:错误配置事务。
TransactionDefinition.PROPAGATION_SUPPORTS |
如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行 |
TransactionDefinition.PROPAGATION_NOT_SUPPORTED |
以非事务方式运行,如果当前存在事务,则把当前事务挂起 |
TransactionDefinition.PROPAGATION_NEVER |
以非事务方式运行,如果当前存在事务,则抛出异常 |
所以:如果存在调用链的,请务必注意所有方式是否应该在一个事务环境中,如非必要,不要修改默认的传播特性。
坑三:未正确设置rollbackfor
原因:spring默认会对Error和RuntimeException进行事务回滚,但是其他异常则不会处理。放一个图你就知道了
所以:对于一些明确回滚的异常,直接在rollbackfor里面指定好。
坑四:try...catch...滥用
原因:spring Aop回滚的机制是捕获了应用程序抛出的指定异常,如果你try...catch...了,那Spring 会错误的认为当前运行正常,事务应该Commit。所以,此时的try...catch...就是画蛇添足了。
所以:尽量不要在业务方法中使用try...catch来捕获你的异常,防止影响了事务。
坑五:跨了线程的事务
原因:如果事务方法内,开启了新线程去执行其他事务方法也是不受当前事务方法控制的。因为不同线程拥有的threadlocal 不一样。
所以:当你需要明确开启新线程,请分开处理。
坑六:数据库不支持
原因:这种情况出现的概率并不高,事务能否生效数据库引擎是否支持事务是关键。常用的MySQL数据库默认使用支持事务的innodb引擎。一旦数据库引擎切换成不支持事务的myisam,那事务就从根本上失效了。
以上6种方式,是日常开发当中比较容易出现的坑,请避免出现。