Spring声明式事务提供给 Javaer 们方便的线上效事务配置方式,再搭配Spring Boot自动配置,问题务居基本只需在方法上添加@Transactional注解,事迹数据即可瞬间开启方法的库事事务性配置。 你就以为这就够了吗?然都 事务未被正确处理,一般不会导致停止服务,没生更不易在测试阶段复现。线上效但随系统业务越来越复杂,问题务居就会带来大量数据不一致问题,事迹数据随后就是库事大量线上问题而后人工排查检修数据。 1 你的然都Spring事务怎么才算生效? 使用@Transactional开启声明式事务时, 灵魂发问:事务生效了吗?没生 案例 用户表实体类 DAO 层 根据username查询所有数据 Service层 UserService类 负责业务逻辑处理,包括如下方法: createUserWrong1调用private方法: createUserPrivate,线上效被@Transactional注解。问题务居当传入的事迹数据用户名包含test则抛异常,让用户的创建操作失败,期望事务回滚: getUserCount Controller层 调用一下刚才定义的UserService中的入口方法createUserWrong1。 测试结果 即便用户名不合法,用户也能创建成功。刷新浏览器,多次发现非法用户注册。 2 @Transactional怎么确保生效? 除非特殊配置(比如使用AspectJ静态织入实现AOP),否则只有定义在public方法上的@Transactional才能生效。 Spring默认通过动态代理实现AOP,对目标方法增强,香港云服务器private方法无法代理到,自然也无法动态增强事务处理逻辑。 那简单,把createUserPrivate方法改为public即可。 在UserService中再建一个入口方法createUserWrong2,来调用这个public方法再次尝试: 新的createUserWrong2方法事务同样不生效。 必须通过代理过的类从外部调用目标方法 要调用增强过的方法必然是调用代理后的对象。 尝试修改UserService,注入一个self,然后再通过self实例调用标记有@Transactional注解的createUserPublic方法。设置断点可以看到,self是由Spring通过CGLIB方式增强过的类。 CGLIB通过继承方式实现代理类,private方法在子类不可见,自然也就无法进行事务增强; this指针代表对象自己,Spring不可能注入this,所以通过this访问方法必然不是代理。 把this改为self,在Controller中调用createUserRight方法可以验证事务生效了:非法的用户注册操作可以回滚。 虽然在UserService内部注入自己调用自己的createUserPublic可以正确实现事务,但这不符合习惯用法。网站模板更合理的实现方式是,让Controller直接调用之前定义的UserService的createUserPublic方法。 this自调用/self调用/Controller调用UserService 无法走到Spring代理类 调用的Spring注入的UserService,通过代理调用才有机会对createUserPublic方法进行动态增强。 推荐在开发时打开相关Debug日志,以了解Spring事务实现的细节。 比如JPA数据库访问,可以这么开启Debug日志: logging.level.org.springframework.orm.jpa=DEBUG 开启日志后再比较下在UserService中this调用、Controller中通过注入的UserService Bean调用createUserPublic的区别。 很明显,this调用因没走代理,事务没有在createUserPublic生效,只在Repository的save生效: 这种实现在Controller里处理异常显得繁琐,还不如直接把createUserWrong2加@Transactional注解,然后在Controller中直接调用该方法。 这既能从外部(Controller中)调用UserService方法,方法又是public的能够被动态代理AOP增强。要不你试试?看看效果如何,下回分解~
this自调用