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

单元测试中Jooq查询无法读取Hibernate写入的数据问题

问题分析与解决方案

你的核心问题是Hibernate写入的数据在同一测试事务内无法被Jooq读取,根源在于Hibernate的缓存机制和Spring测试事务的上下文绑定逻辑:

  1. Hibernate的save方法默认仅将实体放入一级缓存,不会立即刷写到数据库连接,除非触发事务提交或手动调用flush;
  2. 你给@BeforeEach添加@Commit会提前提交事务,打破了Spring测试类@Transactional的全局事务上下文,反而导致测试方法在新事务中运行,若Jooq未正确绑定事务连接,就无法读到提交的数据;
  3. 保留测试后自动回滚的核心是维持单一测试事务,让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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 12:15:39