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
相关产品推荐
相关产品推荐

