Spring Boot中@Transactional执行save后抛异常未回滚问题
Spring Boot事务未回滚问题排查与解决
问题概述
在PaiementServiceImpl的store方法中,调用paiement_repo.save保存对象后抛出ResponseStatusException,但事务未回滚,数据仍被持久化到数据库。以下是具体排查步骤和解决方案:
1. 移除控制器层的事务注解
事务管理应仅在服务层实现,控制器层添加@Transactional会导致事务边界混乱,干扰服务层的事务逻辑。
修改后的控制器代码:
@RestController @RequestMapping("/paiements") @CrossOrigin("*") @Slf4j public class PaiementController { @Autowired public PaiementRepository paiement_repo; @Autowired public PaiementServiceImpl paiementService; @PostMapping public ResponseEntity<Paiement> store(@RequestBody Paiement paiement ) throws ResponseStatusException { return paiementService.store(paiement); } }
服务层简化事务配置(类级别统一配置,方法无需重复):
@Service @Transactional(rollbackFor = { ResponseStatusException.class }) public class PaiementServiceImpl { @Autowired PaiementRepository paiement_repo; public ResponseEntity<Paiement> store(Paiement paiement ) throws ResponseStatusException { Paiement newp = paiement_repo.save(paiement); throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "XXXXXXXXXXXXXXXXXXX"); } }
2. 检查MySQL表存储引擎
MySQL的MyISAM引擎不支持事务,必须使用InnoDB。执行以下SQL检查表引擎:
SHOW TABLE STATUS LIKE 'paiement';
若引擎为MyISAM,执行修改命令:
ALTER TABLE paiement ENGINE=InnoDB;
同时修改application.properties,确保Hibernate创建表时默认使用InnoDB:
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
3. 验证事务代理是否生效
Spring事务基于AOP代理实现,启动日志中若出现类似以下内容,说明代理正常生成:
o.s.aop.framework.CglibAopProxy : Creating CGLIB proxy for [com.example.PaiementServiceImpl]
若未出现,检查启动类是否添加@SpringBootApplication注解(包含组件扫描逻辑)。
4. 排查异常是否被捕获
检查全局异常处理器(@RestControllerAdvice)是否捕获了ResponseStatusException且未重新抛出。若异常被拦截未传播,事务不会触发回滚。
5. 修正配置文件错误
application.properties中存在拼写错误,修正后避免潜在问题:
# 原错误配置 # spring.jpa.hibernate.dll-auto = update # 修正后 spring.jpa.hibernate.ddl-auto=update
6. 最简场景测试
编写测试用例验证服务层事务逻辑:
@SpringBootTest public class PaiementServiceTest { @Autowired private PaiementServiceImpl paiementService; @Autowired private PaiementRepository paiementRepository; @Test void testStoreRollback() { Paiement paiement = new Paiement(); paiement.setReference("TEST_TRANSACTION"); paiement.setMontant(100.0); // 确认异常抛出 Assertions.assertThrows(ResponseStatusException.class, () -> { paiementService.store(paiement); }); // 验证数据未被持久化 List<Paiement> paiements = paiementRepository.findAll(); Assertions.assertTrue(paiements.stream().noneMatch(p -> p.getReference().equals("TEST_TRANSACTION"))); } }
内容的提问来源于stack exchange,提问作者hyug4 IG
相关产品推荐
相关产品推荐

