单元测试中Jooq查询无法读取Hibernate写入的数据问题
问题分析与解决方案
你的核心问题是Hibernate写入的数据在同一测试事务内无法被Jooq读取,根源在于Hibernate的缓存机制和Spring测试事务的上下文绑定逻辑:
- Hibernate的
save方法默认仅将实体放入一级缓存,不会立即刷写到数据库连接,除非触发事务提交或手动调用flush; - 你给
@BeforeEach添加@Commit会提前提交事务,打破了Spring测试类@Transactional的全局事务上下文,反而导致测试方法在新事务中运行,若Jooq未正确绑定事务连接,就无法读到提交的数据; - 保留测试后自动回滚的核心是维持单一测试事务,让Hibernate和Jooq共享同一个数据库连接。
具体解决步骤
1. 调整测试事务配置
移除@BeforeEach上的@Commit注解,保留测试类的@Transactional,确保整个测试流程(@BeforeEach+测试方法)处于同一个事务中,测试结束后自动回滚。
2. 强制Hibernate刷写缓存
在@BeforeEach的保存操作后,手动调用flush,将缓存中的数据同步到当前事务的数据库连接中,让同一事务内的Jooq可以读取到数据。
3. 确保Jooq绑定Spring事务
保证Jooq的DSLContext由Spring注入,使用Spring管理的数据源,自动参与当前线程的事务上下文。
修改后的测试代码示例
@ExtendWith(SpringExtension.class) @SpringBootTest @Transactional public class DeliverySummaryQueryBuilderTest { @Autowired private EntityManager entityManager; @BeforeEach public void before() throws SQLException { Cluster cluster = new Cluster(); cluster.setId("cluster-1"); cluster = clusterRepository.save(cluster); Employee employee = new Employee(); employee.setId(MANAGER_ID); employee.setRole(EmployeeRole.MANAGER); employee = employeeRepository.save(employee); // 强制刷写Hibernate缓存到当前事务的数据库连接 entityManager.flush(); // 也可以直接调用对应Repository的flush方法: // clusterRepository.flush(); // employeeRepository.flush(); // 其他测试数据保存逻辑... } @Test void test() throws SQLException { final List<DeliveryStatusSummaryDTO> summary = deliveryStatusSummaryV2Repository.getDeliveryStatusSummary( LocalDate.parse("2022-08-01"), LocalDate.parse("2022-08-31"), Set.of(MANAGER_ID), Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), List.of(MANAGER, DAY, LOB, CATEGORY) ); // 这里添加断言验证结果,例如: // assertThat(summary).isNotEmpty(); } }
Jooq Repository的正确配置示例
确保Repository使用Spring注入的DSLContext:
@Repository public class DeliveryStatusSummaryV2Repository { private final DSLContext dsl; @Autowired public DeliveryStatusSummaryV2Repository(DSLContext dsl) { this.dsl = dsl; } public List<DeliveryStatusSummaryDTO> getDeliveryStatusSummary(...) { return dsl.select(...) .from(...) .where(...) .fetchInto(DeliveryStatusSummaryDTO.class); } }
原理说明
- 整个测试流程在单一Spring事务中运行,测试结束后自动回滚,无需手动清理数据;
entityManager.flush()强制Hibernate将缓存中的持久化操作同步到数据库连接,同一事务内的Jooq查询可以直接读取这些未提交的数据(事务内读写可见,不受数据库隔离级别影响);- Spring管理的
DSLContext会自动绑定当前线程的事务连接,确保Jooq和Hibernate使用同一个数据库连接,数据互通。
内容的提问来源于stack exchange,提问作者Lesha Pipiev
相关产品推荐
相关产品推荐

