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

Spring单元测试用嵌入式H2+Liquibase:测试时表无数据

Spring单元测试中Liquibase初始化H2数据库后表无数据排查

环境版本

  • Spring 4.2.1
  • Liquibase 4.7.1
  • H2 2.1.210

配置实现

数据源与Liquibase XML配置

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="org.h2.Driver" />
    <property name="url" value="jdbc:h2:mem:test;MODE=Oracle;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE" />
    <property name="username" value="test" />
    <property name="password" value="test" />
</bean>

<bean id="liquibase" class="liquibase.integration.spring.SpringLiquibase">
    <property name="dataSource"  ref="dataSource"  />
    <property name="changeLog"  value="classpath:liquibase/changelog/sil_client_init.h2.sql"  />
    <property name="contexts"  value="test"  />
</bean>

<!-- Session factory -->
<bean id="sessionFactory"
      class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation" value="classpath:hibernate-config.xml" />
</bean>

<!-- Transaction manager -->
<bean id="txManager"
      class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

测试父类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:applicationContext.xml" })
@Ignore
public class SpringApplicationContextIT {

    @Before
    public void setUp() {
        BasicConfigurator.configure();
        FacesContextMocker.mockFacesContext();
        assertNotNull(applicationContext);
    }

    @Autowired
    private ApplicationContext applicationContext;

}

测试类

public class ClientDAOTest extends SpringApplicationContextIT {

    @Autowired
    private IClientDAO simpleClientDAO;

    @Test
    @Transactional
    public void getAllClients() throws ClientDAOException {
        simpleClientDAO.find();
    }
}

问题现象

运行getAllClients()测试方法时,Spring上下文已正常加载,日志显示Liquibase的changeset已执行,但调用simpleClientDAO.find()查询时,Client表为空。

排查思路与解决方法

1. 检查事务回滚与Hibernate缓存影响

测试方法添加了@Transactional注解,Spring默认会在测试结束后回滚事务,但Liquibase的初始化是在上下文加载阶段执行,属于独立事务。可能因为Hibernate会话缓存未刷新,导致看不到初始化数据:

  • 尝试在查询前手动刷新Hibernate会话:
    @Autowired
    private SessionFactory sessionFactory;
    
    @Test
    @Transactional
    public void getAllClients() throws ClientDAOException {
        sessionFactory.getCurrentSession().flush();
        sessionFactory.getCurrentSession().clear();
        List<Client> clients = simpleClientDAO.find();
        // 执行断言验证数据存在
    }
    
  • 或者添加@Rollback(false)注解,禁用测试事务的自动回滚(注意这会保留测试数据,可能影响其他测试)。

2. 验证Liquibase上下文匹配

Liquibase配置中设置了contexts="test",需确认sil_client_init.h2.sql中的changeset是否带有context="test"标签:

  • 如果changeset未指定上下文,Liquibase会跳过执行。需给目标changeset添加上下文:
    --liquibase formatted sql
    --changeset author:your-id context:test
    CREATE TABLE CLIENT (...);
    INSERT INTO CLIENT VALUES (...);
    
  • 或者移除Liquibase bean中的contexts配置,让所有changeset无条件执行。

3. 检查Hibernate的DDL自动生成策略

查看hibernate-config.xml中的hibernate.hbm2ddl.auto配置:

  • 如果设置为create或create-drop,Hibernate会在SessionFactory初始化时重新创建表,覆盖Liquibase插入的数据。
  • 建议将该属性设置为none或validate,避免与Liquibase的DDL操作冲突。

4. 确认数据源一致性与直接验证数据

排除Hibernate层面的问题,直接通过JdbcTemplate查询数据库:

@Autowired
private DataSource dataSource;

@Test
public void checkDBData() throws SQLException {
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    int count = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM CLIENT", Integer.class);
    System.out.println("实际Client表数据量:" + count);
}
  • 如果JdbcTemplate能查到数据,说明问题出在Hibernate会话或缓存;如果也查不到,需重新检查Liquibase的执行日志。

5. 查看Liquibase详细执行日志

调高Liquibase的日志级别到DEBUG,查看每个changeset的执行细节:

  • 在log4j配置中添加:
    log4j.logger.liquibase=DEBUG
    
  • 检查是否有changeset执行失败、SQL语法错误(注意H2的Oracle兼容模式下的语法要求)或被跳过的情况。

6. 调整Liquibase与SessionFactory的初始化顺序

确保Liquibase先于SessionFactory完成初始化,避免Hibernate的hbm2ddl操作覆盖Liquibase创建的表:

  • 在SessionFactory bean上添加depends-on="liquibase"属性:
    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" depends-on="liquibase">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="classpath:hibernate-config.xml" />
    </bean>
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 21:25:22