最近在看spring事务源码,时不时回想起前几年面试YY的场景,面试官拿出下面的一道面试题问我,updateStatus方法会不会生成事务。我心想,这么简单的问题还要问我,这是瞧不起我的水平吗?但是我仔细看了看,想了想,这道题还真不容易回答,如果以前没有特别注意或者研究过,很容易掉坑里。还好我当时认真想了想,回答了不会生成事务,但后面面试官继续追问为什么不产生事务,我就把具体的原因说明了一下,事后回到家我再想了想这个问题,发现我回答的并不是很好。摘要:最近在看spring事务源码,时不时回想起前几年面试YY的场景,面试官拿出下面的一道面试题问我,updateStatus方法会不会生成事务。我心想,这么简单的问题还要问我,这是瞧不起我的水平吗?但是我仔细看了看,想了想,这道题还真不容易回答,如果以前没有特别注
一、异常被捕获后没有抛出
如果想要 spring 事务能够正常回滚,必须抛出它能够处理的异常。如果没有抛异常,或者自己吞掉了异常没有抛出,则 spring 认为程序是正常的。因此也不会回滚。
二、抛出非运行时异常
spring 事务,默认情况下只会回滚RuntimeException(运行时异常)和Error(错误),对于普通的 Exception(非运行时异常),它不会回滚。
如果事务注解使用的是@Transactional,即使异常抛出了,但是抛出的是非RuntimeException类型异常,同样也不会回滚。
如果事务注解使用的是@Transactional(rollbackFor = Exception.class),那么抛出的是非RuntimeException类型异常是可以回滚的。
注:也就是说,如果抛的异常不正确,spring 事务也不会回滚。
比如,方法上的事务是:@Transactional(rollbackFor = BusinessException.class),而实际执行业务逻辑时,程序报错,抛了 SqlException、DuplicateKeyException 等异常。而 BusinessException 是我们自定义的异常,报错的异常不属于 BusinessException,所以事务也不会回滚。
所以,建议一般情况下,将该参数设置成:Exception 或 Throwable。
@ServicepublicclassUserService{@Autowiredprivate UserMapper userMapper;publicvoidadd(UserModel userModel){ userMapper.insertUser(userModel); updateStatus(userModel); }@TransactionalpublicvoidupdateStatus(UserModel userModel){ doSameThing; }}这种场景很常见,方法A调用方法B,其中方法A未使用事务,而方法B使用了事务,此时方法B的事务是不生效的,例子如下,因为updateStatus 方法拥有事务的能力是因为 spring aop 生成代理了对象,但是这种方法直接调用了 this 对象的方法,所以 updateStatus 方法不会生成事务。解决办法:
@ServciepublicclassServiceA{@Autowired prvate ServiceB serviceB;publicvoidsave(User user){ queryData1; queryData2; serviceB.doSave(user); } }@ServciepublicclassServiceB{@Transactional(rollbackFor=Exception.class)publicvoiddoSave(User user){ addData1; updateData2; } }四、访问权限问题
众所周知,java 的访问权限主要有四种:private、default、protected、public,它们的权限从左到右,依次变大。
如果方法的访问权限被定义成了private,这样会导致事务失效,spring 要求被代理方法必须是public的。
五、未被Spring管理
在我们平时开发过程中,有个细节很容易被忽略,即使用 spring 事务的前提是:对象要被 spring 管理,需要创建 bean 实例。
通常情况下,我们通过 @Controller、@Service、@Component、@Repository 等注解,可以自动实现 bean 实例化和依赖注入的功能。
如果有一天,你匆匆忙忙地开发了一个 Service 类,但忘了加 @Service 注解,那么该类不会交给 spring 管理,所以它的 add 方法也不会生成事务。
六、新开启一个线程
在实际项目开发中,多线程的使用场景还是挺多的。如果 spring 事务用在多线程场景中,会有问题吗?
如果两个方法不在同一个线程中,那么获取到的数据库连接不一样,从而是两个不同的事务。
spring 的事务是通过数据库连接来实现的。当前线程中保存了一个 map,key 是数据源,value 是数据库连接。
我们说的同一个事务,其实是指同一个数据库连接,只有拥有同一个数据库连接才能同时提交和回滚。如果在不同的线程,拿到的数据库连接肯定是不一样的,所以是不同的事务。
来源:健柏教育