集成测试中RestController早于Hibernate建表的解决方法
集成测试中H2数据库表未创建导致RestController调用失败的解决方法
在集成测试场景下使用H2数据库时,RestController会在Hibernate完成数据库Schema创建前被调用,导致测试因表不存在失败(当前ddl-auto配置为update)。需要解决两个问题:
- 如何确保控制器在数据库完全创建完成后再被使用?
- Hibernate是否提供可供控制器依赖的Bean?
报错信息
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "_USER" not found (this database is empty); SQL statement: select u1_0.id,u1_0.company_id,u1_0.country_id,u1_0.created_at,u1_0.created_by,u1_0.disabled,u1_0.email,u1_0.email_confirmed,u1_0.full_access,u1_0.full_name,u1_0.gender,u1_0.mfa,u1_0.mfa_secret,u1_0.modified_at,u1_0.modified_by,u1_0.notification_email,u1_0.notification_slack_webhook,u1_0.notification_slack_webhook_enabled,u1_0.password,u1_0.phone,u1_0.role,u1_0.temp_mfa_secret from _user u1_0 [42104-220] at org.h2.message.DbException.getJdbcSQLException(DbException.java:514) ~[h2-2.2.220.jar:2.2.220] at org.h2.message.DbException.getJdbcSQLException(DbException.java:489) ~[h2-2.2.220.jar:2.2.220] at org.h2.message.DbException.get(DbException.java:223) ~[h2-2.2.220.jar:2.2.220] at org.h2.message.DbException.get(DbException.java:199) ~[h2-2.2.220.jar:2.2.220] at org.h2.command.Parser.getTableOrViewNotFoundDbException(Parser.java:8548) ~[h2-2.2.220.jar:2.2.220] at org.h2.command.Parser.getTableOrViewNotFoundDbException(Parser.java:8532) ~[h2-2.2.220.jar:2.2.220] at org.h2.command.Parser.readTableOrView(Parser.java:8521) ~[h2-2.2.220.jar:2.2.220] at org.h2.command.Parser.readTablePrimary(Parser.java:1911) ~[h2-2.2.220.jar:2.2.220] at org.h2.command.Parser.readTableReference(Parser.java:2391) ~[h2-2.2.220.jar:2.2.220] at org.h2.command.Parser.parseSelectFromPart(Parser.java:2844) ~[h2-2.2.220.jar:2.2.220] at org.h2.command.Parser.parseSelect(Parser.java:2950) ~[h2-2.2.220.jar:2.2.220] at org.h2.command.Parser.parseQueryPrimary(Parser.java:2834) ~[h2-2.2.220.jar:2.2.220] at org.h2.command.Parser.parseQueryTerm(Parser.java:2690) ~[h2-2.2.220.jar:2.2.220] at org.h2.command.Parser.parseQueryExpressionBody(Parser.java:2669) ~[h2-2.2.220.jar:2.2.220] at org.h2.command.Parser.parseQueryExpressionBodyAndEndOfQuery(Parser.java:2662) ~[h2-2.2.220.jar:2.2.220] at org.h2.command.Parser.parseQueryExpression(Parser.java:2655) ~[h2-2.2.220.jar:2.2.220] at org.h2.command.Parser.parseQuery(Parser.java:2624) ~[h2-2.2.220.jar:2.2.220] at org.h2.command.Parser.parsePrepared(Parser.java:732) ~[h2-2.2.220.jar:2.2.220] at org.h2.command.Parser.parse(Parser.java:697) ~[h2-2.2.220.jar:2.2.220] at org.h2.command.Parser.parse(Parser.java:669) ~[h2-2.2.220.jar:2.2.220] at org.h2.command.Parser.prepareCommand(Parser.java:577) ~[h2-2.2.220.jar:2.2.220] at org.h2.engine.SessionLocal.prepareLocal(SessionLocal.java:634) ~[h2-2.2.220.jar:2.2.220] at org.h2.engine.SessionLocal.prepareCommand(SessionLocal.java:557) ~[h2-2.2.220.jar:2.2.220] at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1116) ~[h2-2.2.220.jar:2.2.220] at org.h2.jdbc.JdbcPreparedStatement.<init>(JdbcPreparedStatement.java:92) ~[h2-2.2.220.jar:2.2.220] at org.h2.jdbc.JdbcConnection.prepareStatement(JdbcConnection.java:288) ~[h2-2.2.220.jar:2.2.220] at com.zaxxer.hikari.pool.ProxyConnection.prepareStatement(ProxyConnection.java:327) ~[HikariCP-5.0.1.jar:na] at com.zaxxer.hikari.pool.HikariProxyConnection.prepareStatement(HikariProxyConnection.java) ~[HikariCP-5.0.1.jar:na] at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$1.doPrepare(StatementPreparerImpl.java:91) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final] at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:177) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final] ... 211 common frames omitted
解决方案
1. 监听Hibernate初始化事件
通过@EventListener监听Hibernate的SessionFactoryInitializedEvent事件,该事件在SessionFactory初始化完成(包含Schema创建)后触发。用CountDownLatch阻塞控制器逻辑,直到事件触发:
import org.hibernate.event.spi.SessionFactoryInitializedEvent; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; import java.util.concurrent.CountDownLatch; @Component public class HibernateStartupListener { private final CountDownLatch latch = new CountDownLatch(1); @EventListener public void onSessionFactoryInitialized(SessionFactoryInitializedEvent event) { latch.countDown(); } public void waitForInitialization() throws InterruptedException { latch.await(); } }
在测试类或控制器初始化时注入该Listener,调用waitForInitialization()确保Schema创建完成后再执行后续逻辑。
2. 使用@DependsOn强制依赖顺序
让RestController依赖Hibernate的核心Bean(如entityManagerFactory或sessionFactory),确保控制器在这些Bean初始化完成后再创建:
@RestController @DependsOn("entityManagerFactory") public class UserController { // 业务逻辑 }
3. 调整DDL配置(测试环境专用)
将测试环境的spring.jpa.hibernate.ddl-auto设置为create或create-drop,相比update,create会直接创建Schema而非检查更新,能更快完成表创建,避免时序问题:
spring.jpa.hibernate.ddl-auto=create
4. 提前执行数据库初始化脚本
使用Spring的@Sql注解在测试前执行Schema脚本,确保表提前存在:
@SpringBootTest @Sql(scripts = "/schema.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) public class UserControllerIntegrationTest { // 测试用例 }
其中/schema.sql存放创建表的SQL语句,测试方法执行前会自动运行该脚本。
内容的提问来源于Stack Exchange,提问作者Ahmed Ashour
相关产品推荐
相关产品推荐

