You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 13:54:22