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

SpringBoot集成测试MyBatis场景下如何实现单事务全链路操作并测试后回滚

问题核心原因

你遇到的问题本质是两个底层机制导致的:

  • 手动创建的SqlSession和Spring托管的MyBatis Mapper、业务服务使用的连接不属于同一个事务上下文,未提交的插入数据对其他连接不可见,直接导致第一个断言失败
  • 事务是和线程绑定的,calculateService内部新开的线程无法继承主线程的事务,所有子线程的数据库操作都会开启独立事务,测试主线程的回滚操作完全覆盖不到这部分数据变更
解决方案

方案1:适配单线程执行,用Spring事务自动回滚(成本最低)

这个方案适合可以对业务代码做少量测试适配的场景,所有操作都在测试主线程的同一个事务中执行,测试结束自动回滚:

  • 删掉自定义创建SqlSession、Spy注入、手动管理连接的逻辑,这部分逻辑会破坏Spring的事务托管机制
  • 测试类添加@Transactional和@Rollback(true)注解,Spring测试框架会自动为测试方法绑定全局事务,默认测试结束就会回滚所有操作
  • 不要自己手动拿Connection执行SQL,注入Spring提供的JdbcTemplate执行插入操作,JdbcTemplate会自动复用当前事务绑定的连接,插入的数据对同一个事务内的MyBatis Mapper查询可见
  • 给calculateService的方法添加测试环境开关,测试场景下不开启新线程,串行执行所有计算、存储逻辑,确保所有数据库操作都在测试主线程的事务内

修改后的代码示例:

@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
@Transactional
@Rollback(true)
public class MyTestClass {

    @Autowired
    private DataRepository dataRepository;
    @Autowired
    private CalculateService calculateService;
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    public void myTest(){
        // 用JdbcTemplate执行插入,复用当前事务的连接
        String createdProductId = jdbcTemplate.queryForObject("Insert into ... RETURNING id", String.class);

        Product createdProduct = dataRepository.getProduct(createdProductId);
        assertNotNull(createdProduct); // 此处断言可以正常通过

        // 测试场景下calculate方法不开新线程,所有操作都在当前事务
        calculateService.calculate(createdProduct);

        createdProduct = dataRepository.getProduct(createdProductId);
        assertSomeField(createdProduct.getSomeField);
    }
}

方案2:必须保留多线程逻辑的场景

如果不能修改业务代码的多线程逻辑,事务机制无法跨线程实现回滚,推荐以下两种方案:

子方案2.1 使用H2内存数据库

这是集成测试的主流方案,每次测试启动独立的内存数据库实例,测试结束后实例自动销毁,不需要手动处理数据回滚:

  1. 引入H2依赖,在测试配置文件application-test.yml中配置内存数据源
spring:
  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
    username: sa
    password:
  sql:
    init:
      schema-locations: classpath:你的建表脚本路径.sql
  1. 测试类添加@ActiveProfiles("test")启用测试配置即可。

子方案2.2 手动数据清理

如果必须使用测试环境的物理库,可以在测试前后手动清理关联表数据:

  • 用@Before方法在测试执行前清空本次测试涉及的所有业务表
  • 用@After方法在测试执行后再次清空关联表,避免测试数据残留

内容的提问来源于stack exchange,提问作者Fyodor Iz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 18:15:03