背景
在controller方法上增加了@Transactional注解,实际运行过程中方法出现了Exception导致方法执行失败,但是发现已经执行的insert操作并没有回滚
@Transactional可能失效的场景
-
一个有@Transactional的方法被没有@Transactional方法调用时,会导致Transactional作用失效
产生原因: Spring AOP代理造成,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。 -
对非public方法进行事务注解。@Transactional 将会失效
产生原因: Spring AOP代理时,事务拦截器在目标方法前后进行拦截,DynamicAdvisedInterceptor的intercept 方法会获取Transactional注解的事务配置信息,因为在Spring AOP 代理时, TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute 方法会间接调用 AbstractFallbackTransactionAttributeSource的 computeTransactionAttribute 方法,这个方法会获取Transactional 注解的事务配置信息。他会首先校验事务方法的修饰符是不是public,不是 public则不会获取@Transactional 的属性配置信息。 -
Transactional 事务配置属性中的propagation 属性配置的问题
产生原因:
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常 -
异常被catch之后没有throw
-
数据库引擎不支持事务
-
没有被 Spring 管理
-
数据源没有配置事务管理器
-
异常类型错误
-
没有开启事务管理
问题排查解决
- 经排查以上情况均没问题
- 移动@Transactional注解至service接口,发现事务生效
- 经分析确认是因为注解位置导致事务失效
原因分析
- spring中,@Transactional注解仅对spring的context生效,如果class没有注册到spring的context中,则事务管理器将无法生效
- spring中context和spring mvc的上下文容器是隔离开的,mvc容器是spring容器的一个子容器,子容器中可以访问父容器,但父容器无法访问子容器
- controller会注册到mvc子容器中,而@Transactional对spring父容器生效,故当@Transactional在controller时无法回滚事务
- controller能否注册到spring父容器而非mvc子容器,答案:不能,因为当controller注册到spring父容器后mvc子容器中就没有,http的请求path就无法正确映射,请求接口时就会404
- 能否只使用mvc容器而不使用spring容器,答案:可以,mvc容器更加轻量级,但是spring容器扩展性更好,可以方便的集成其他框架
小短腿的个人公众号上线啦,后续技术分享将优先在公众号进行哈,大家可以关注公众号,小短腿有什么新的分享会及时同步哈