如何在@SpringBootTest中禁用Hibernate的Schema创建/删除操作?
问题重现
使用@SpringBootTest编写单元测试时,测试可正常通过,但日志中出现Hibernate执行DROP TABLE语句的错误。已尝试在application-test.yml中配置禁用Schema操作:
spring: jpa: generate-ddl: false properties: hibernate: ddl-auto: none repositories: enabled: false
同时在测试类上添加了相关注解:
@SpringBootTest(properties = {"spring.config.location=classpath:application-test.yml", "spring.profiles.active=test"}, webEnvironment = SpringBootTest.WebEnvironment.MOCK) @AutoConfigureMockMvc @ActiveProfiles("test") @TestPropertySource(locations = "classpath:application-test.yml") @AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.H2, replace = AutoConfigureTestDatabase.Replace.NONE) @ExtendWith(SpringExtension.class) public class MyTestClassTest {}
但测试仍会触发DROP表操作,报错信息如下:
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "drop table if exists customers.customer_alias CASCADE " via JDBC Statement
at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:67)
at org.hibernate.tool.schema.internal.SchemaDropperImpl.applySqlString(SchemaDropperImpl.java:387)
at org.hibernate.tool.schema.internal.SchemaDropperImpl.applySqlStrings(SchemaDropperImpl.java:371)
at org.hibernate.tool.schema.internal.SchemaDropperImpl.dropFromMetadata(SchemaDropperImpl.java:246)
at org.hibernate.tool.schema.internal.SchemaDropperImpl.performDrop(SchemaDropperImpl.java:156)
at org.hibernate.tool.schema.internal.SchemaDropperImpl.doDrop(SchemaDropperImpl.java:128)
at org.hibernate.tool.schema.internal.SchemaDropperImpl.doDrop(SchemaDropperImpl.java:114)
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:157)
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:85)
at org.hibernate.internal.SessionFactoryImpl.(SessionFactoryImpl.java:335)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:471)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1498)
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1168)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:919)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:591)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:409)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
解决方案
1. 简化测试类注解,避免配置冲突
测试类中存在重复的配置注解,会导致配置优先级混乱,简化后保留核心注解即可:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) @AutoConfigureMockMvc @ActiveProfiles("test") @AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.H2, replace = AutoConfigureTestDatabase.Replace.NONE) public class MyTestClassTest {}
2. 完善测试环境的JPA配置
修改src/test/resources/application-test.yml,补充Hibernate原生配置,彻底禁用所有Schema操作:
spring: jpa: generate-ddl: false # Spring Boot简化配置,直接设置ddl-auto为none hibernate: ddl-auto: none properties: hibernate: # 禁用Schema更新 schema_update: enabled: false # 禁用HBM2DDL工具的自动操作 tool: hbm2ddl: auto: none # Hibernate 5.x/6.x通用的Schema管理开关 schema-management: auto: none
注意:spring.jpa.hibernate.ddl-auto的优先级高于properties.hibernate.ddl-auto,直接配置前者更可靠。
3. Hibernate 6.x专属配置调整
如果使用Hibernate 6.x,部分配置项名称有变化,需调整为:
spring: jpa: properties: hibernate: schema-management: auto: none ddl: auto: none
4. 排查自定义Bean或第三方依赖
若上述配置仍不生效,检查项目中是否存在自定义的SchemaExport初始化Bean,或第三方依赖中包含触发Schema操作的逻辑,手动禁用对应Bean即可。
验证
启动测试后,查看日志确认drop table相关SQL语句不再出现,即为配置生效。
内容的提问来源于stack exchange,提问作者meda

