2成的业务代码的Spring声明式事务,可能都没处理正确

张开发
2026/4/21 4:46:06 15 分钟阅读

分享文章

2成的业务代码的Spring声明式事务,可能都没处理正确
用了这么久Transactional你确定事务真的生效了吗说实话据我观察至少 20% 的业务代码的事务都没处理正确。平时跑得好好的一到高并发或者出问题的时候数据不一致的 bug 就冒出来了。今天聊三个最常见的坑。坑一事务根本没生效private 方法上的 TransactionalServicepublicclassUserService{publicintcreateUser(Stringname){try{this.createUserPrivate(newUserEntity(name));}catch(Exceptionex){log.error(创建失败,ex);}returnuserRepository.findByName(name).size();}Transactional// 不生效private 方法无法被代理privatevoidcreateUserPrivate(UserEntityentity){userRepository.save(entity);if(entity.getName().contains(test))thrownewRuntimeException(用户名不合法);}}结果用户名不合法但用户创建成功了。原因Spring 默认用动态代理实现 AOPprivate 方法压根代理不了。修复改成 public 方法。内部 this 调用ServicepublicclassUserService{publicintcreateUser(Stringname){try{this.createUserPublic(newUserEntity(name));//this 不是代理对象}catch(Exceptionex){log.error(创建失败,ex);}returnuserRepository.findByName(name).size();}Transactional// 不生效publicvoidcreateUserPublic(UserEntityentity){userRepository.save(entity);if(entity.getName().contains(test))thrownewRuntimeException(用户名不合法);}}原因this代表对象自己Spring 注入的是代理对象this 调用跳过了代理。修复从 Controller 注入调用或者在 Service 里注入自己ServicepublicclassUserService{AutowiredprivateUserServiceself;// 注入代理对象publicintcreateUser(Stringname){try{self.createUserPublic(newUserEntity(name));// 走代理}catch(Exceptionex){log.error(创建失败,ex);}returnuserRepository.findByName(name).size();}TransactionalpublicvoidcreateUserPublic(UserEntityentity){userRepository.save(entity);if(entity.getName().contains(test))thrownewRuntimeException(用户名不合法);}}坑二事务生效了但没回滚自己 catch 了异常TransactionalpublicvoidcreateUser(Stringname){try{userRepository.save(newUserEntity(name));thrownewRuntimeException(error);}catch(Exceptionex){log.error(创建失败,ex);// 异常被吞了}}原因Spring 用 try-catch 包裹事务方法异常被 catch 了没传播出去Spring 不知道要回滚。修复手动标记回滚或者重新抛出异常TransactionalpublicvoidcreateUser(Stringname){try{userRepository.save(newUserEntity(name));thrownewRuntimeException(error);}catch(Exceptionex){log.error(创建失败,ex);TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();// 手动回滚}}受检异常不回滚TransactionalpublicvoidcreateUser(Stringname)throwsIOException{userRepository.save(newUserEntity(name));Files.readAllLines(Paths.get(not-exist));// IOException 是受检异常}原因Spring 默认只对 RuntimeException 和 Error 回滚。受检异常被认为是业务预期内的返回默认不回滚。修复声明要回滚的异常类型Transactional(rollbackForException.class)// 所有异常都回滚publicvoidcreateUser(Stringname)throwsIOException{userRepository.save(newUserEntity(name));Files.readAllLines(Paths.get(not-exist));}坑三子事务影响了主事务场景主用户要创建成功子用户失败无所谓ServicepublicclassSubUserService{TransactionalpublicvoidcreateSubUser(UserEntityentity){userRepository.save(entity);thrownewRuntimeException(子用户创建失败);}}ServicepublicclassUserService{AutowiredprivateSubUserServicesubUserService;TransactionalpublicvoidcreateUser(UserEntityentity){createMainUser(entity);// 插入主用户subUserService.createSubUser(entity);// 插入子用户会失败}privatevoidcreateMainUser(UserEntityentity){userRepository.save(entity);}}结果主用户也创建失败了。原因默认传播行为是 REQUIRED在一个事务里子方法失败会导致整个事务回滚。修复让子方法开启新事务ServicepublicclassSubUserService{Transactional(propagationPropagation.REQUIRES_NEW)// 开启新事务publicvoidcreateSubUser(UserEntityentity){userRepository.save(entity);thrownewRuntimeException(子用户创建失败);}}ServicepublicclassUserService{TransactionalpublicvoidcreateUser(UserEntityentity){createMainUser(entity);// 主用户 - 成功try{subUserService.createSubUser(entity);// 子用户 - 失败但独立事务}catch(Exceptionex){log.error(子用户失败了,ex);// 吞掉异常不影响主事务}}}原理REQUIRES_NEW 会挂起当前事务开启新事务。子事务失败只回滚自己主事务不受影响。一图总结坑一事务不生效 ├── private 方法 → 改成 public └── this 自调用 → 从外部调用或注入自己 坑二不回滚 ├── 吞了异常 → 手动 setRollbackOnly 或重新抛出 └── 受检异常 → rollbackFor Exception.class 坑三子事务影响主事务 └── 用 REQUIRES_NEW 开启独立事务调试小技巧开启事务 Debug 日志看事务到底有没有生效logging:level:org.springframework.orm.jpa:DEBUG看日志里的事务名是不是你方法的名字如果不是说明没走到你的方法事务# 生效事务名是你的方法Creating new transaction with name[com.xxx.UserService.createUser]# 不生效事务名是 JPA 的 save 方法Creating new transaction with name[SimpleJpaRepository.save]总结坑原因修复private 方法代理不到改成 publicthis 调用绕过代理从外部调用吞了异常异常没传播setRollbackOnly受检异常默认不回滚rollbackFor Exception.class子事务影响主事务同一个事务REQUIRES_NEW事务没处理好的 bug平时不容易发现一到高并发就数据乱了。所以写完代码多想一想事务到底有没有生效。

更多文章