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

Spring Boot测试用DBUnit及数据库回滚遇问题:无法获取初始数据且不回滚

问题分析与解决方案

核心问题1:事务回滚失效 + 无法获取初始数据

你的代码里存在注解拼写错误:@Transcational 应该是 @Transactional。Spring无法识别错误的注解,导致测试方法未被纳入事务上下文,所以:

  • @Sql 执行的初始数据不在事务内,测试后无法回滚
  • MockMvc和DBUnit的操作不在同一个事务中,DBUnit查询看不到MockMvc修改的数据,也看不到初始数据

核心问题2:初始数据加载方式不匹配

你步骤里说明用initial-data.xml定义初始数据,但代码里用@Sql加载的是.sql脚本——@Sql仅支持SQL文件,无法解析DBUnit的XML数据集,需要用DBUnit API加载XML初始数据。

核心问题3:ResultSet转IDataset的代码错误

IDataSet是接口,不能直接new IDataSet(resultSet),需要使用DBUnit的具体实现类,比如ResultSetTableDataSet。


修正后的完整代码

import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ResultSetTableDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
import org.dbunit.operation.DatabaseOperation;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.transaction.annotation.Transactional;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class ApiTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private DataSource dataSource;

    @Test
    @Transactional // 修正拼写错误
    @Rollback
    public void testApi() throws Exception {
        // 1. 使用DBUnit加载XML初始数据到当前事务的数据库连接
        try (Connection conn = DataSourceUtils.getConnection(dataSource)) {
            IDatabaseConnection dbUnitConn = new DatabaseConnection(conn);
            InputStream initialDataStream = getClass().getResourceAsStream("/initial-data.xml");
            IDataSet initialDataSet = new FlatXmlDataSetBuilder().build(initialDataStream);
            // 插入初始数据(使用INSERT操作,避免清空表)
            DatabaseOperation.INSERT.execute(dbUnitConn, initialDataSet);
        }

        // 2. 模拟API调用
        mockMvc.perform(post("/your-api-endpoint")
                        .param("yourParam", "value"))
                .andExpect(status().isOk());

        // 3. 从当前事务连接查询实际数据,转换为DBUnit的IDataset
        try (Connection conn = DataSourceUtils.getConnection(dataSource);
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery("SELECT * FROM your_table")) {

            IDataSet actualDataSet = new ResultSetTableDataSet(rs);

            // 4. 加载预期数据
            InputStream expectedDataStream = getClass().getResourceAsStream("/expected-data.xml");
            IDataSet expectedDataSet = new FlatXmlDataSetBuilder().build(expectedDataStream);

            // 5. 对比数据
            org.dbunit.Assertion.assertEquals(expectedDataSet, actualDataSet);
        }
        // 事务会自动回滚,无需手动释放连接(Spring会处理)
    }
}

关键修正点说明

  1. 修复事务注解拼写:把@Transcational改为@Transactional,让Spring为测试方法创建事务上下文,确保所有操作(初始数据插入、API调用、查询)都在同一个事务内,测试结束后自动回滚。
  2. 用DBUnit加载XML初始数据:通过DatabaseOperation.INSERT把XML数据集插入到Spring事务管理的数据库连接中,保证初始数据在当前事务内可见。
  3. 统一数据库连接:所有DBUnit操作都使用DataSourceUtils.getConnection(dataSource)获取Spring事务绑定的连接,确保和MockMvc、Spring数据操作共享同一个事务上下文,避免隔离级别导致的数据不可见问题。
  4. 修正ResultSet转IDataset的代码:使用ResultSetTableDataSet实现类来转换结果集。
  5. 使用classpath加载资源:用getClass().getResourceAsStream()代替硬写文件路径,适配不同环境的资源加载规则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 01:17:03