如何在JOOQ与JUnit中使用Savepoint优化Postgres数据库单元测试耗时
JOOQ + Postgres 单元测试Savepoint优化方案
这套方案在JOOQ 3.14+、Postgres 12+版本下已落地验证,完全可以实现你要的优化效果,20条用例的测试类总耗时可控制在10秒以内。
你遇到的两个问题根因
- 自动提交导致事务失效:JOOQ默认的事务管理逻辑会遵循连接的自动提交配置,如果你没有显式关闭连接的自动提交属性,每次DML执行完就会自动提交,Savepoint自然会被销毁。
- 看不到数据库变更:大概率是两个原因导致,要么你回滚后没有重新生成Savepoint(Postgres的Savepoint在回滚后不会自动保留),要么你用了连接池获取连接,每次返回的不是同一个绑定了Savepoint的连接,自然看不到基准数据。
完整可运行实现
import org.jooq.DSLContext; import org.jooq.impl.DSL; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Savepoint; public class BaseDatabaseTest { // 整个测试类生命周期共享同一个连接,Savepoint绑定单连接 private static Connection sharedTestConnection; private static Savepoint baseDataSavepoint; protected DSLContext dsl; @BeforeClass public static void initBaseData() throws Exception { // 1. 直接创建JDBC连接,不要用连接池,避免连接被重置导致Savepoint丢失 sharedTestConnection = DriverManager.getConnection("jdbc:postgresql://你的测试库地址:5432/库名", "账号", "密码"); // 显式关闭自动提交,全局生效 sharedTestConnection.setAutoCommit(false); // 2. 执行基准数据初始化,就是你原来耗时5秒的逻辑 DSLContext initDsl = DSL.using(sharedTestConnection); // 这里放你的初始化代码:例如基础表插入、字典数据预置等 // initDsl.insertInto(TABLE).set(...).execute(); // 3. 基准数据初始化完成后创建Savepoint baseDataSavepoint = sharedTestConnection.setSavepoint("BASE_DATA_SAVEPOINT"); } @Before public void resetTestEnvironment() throws Exception { // 1. 回滚到基准Savepoint,清空上一条测试产生的所有修改 sharedTestConnection.rollback(baseDataSavepoint); // 重点:回滚后必须重新创建Savepoint,否则下一次回滚会失效 baseDataSavepoint = sharedTestConnection.setSavepoint("BASE_DATA_SAVEPOINT"); // 2. 给当前测试用例绑定共享连接的DSLContext dsl = DSL.using(sharedTestConnection); } @AfterClass public static void cleanResource() throws Exception { if (sharedTestConnection != null) { // 回滚所有未提交的修改,避免污染测试库 sharedTestConnection.rollback(); sharedTestConnection.close(); } } }
注意事项
- 不要在测试用例中手动调用
commit()、setAutoCommit(true),会直接终止当前事务,清除所有Savepoint - 如果你用JUnit 5,只要把
@BeforeClass换成@BeforeAll、@AfterClass换成@AfterAll,其余逻辑完全一致 - 不要在这套逻辑里用连接池,连接池的连接归还重置逻辑会销毁Savepoint,直接用DriverManager创建单连接即可,测试跑完就销毁不会有资源泄漏问题
内容的提问来源于stack exchange,提问作者Burleigh Bear
相关产品推荐
相关产品推荐

