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

