在 JUnit/Spring 测试期间触发 Hibernate 的 SchemaExport.execute (hbm2ddl.auto) 的原因是啥?
Posted
技术标签:
【中文标题】在 JUnit/Spring 测试期间触发 Hibernate 的 SchemaExport.execute (hbm2ddl.auto) 的原因是啥?【英文标题】:What is responsible for triggering a Hibernate's SchemaExport.execute (hbm2ddl.auto) during a JUnit/Spring test?在 JUnit/Spring 测试期间触发 Hibernate 的 SchemaExport.execute (hbm2ddl.auto) 的原因是什么? 【发布时间】:2014-07-01 20:29:27 【问题描述】:在使用SpringJUnit4ClassRunner
运行 JUnit/Spring 测试期间,负责触发 Hibernate 的 SchemaExport.execute
(考虑到 hbm2ddl.auto
已激活)的 Spring 事件是什么?
(考虑到所有这些,我可以拥有多个具有相同上下文配置的测试套件,并且可以缓存 spring 上下文...)
我问这个问题是因为我设置了一个 Spring 监听器(用作数据填充器),如下所示:
@Profile( Profiles.DEFAULT, Profiles.CLOUD, Profiles.TEST, Profiles.DEV )
@Component
public class BootstrapLoaderListener implements ApplicationListener<ContextRefreshedEvent>, ResourceLoaderAware, Ordered
private static final Logger log = Logger.getLogger(BootstrapLoaderListener.class);
@Override
public int getOrder()
return HIGHEST_PRECEDENCE;
@Override
@Transactional
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent)
initApplication();
...
我有两个集成测试套件 (SpringJUnit4ClassRunner
),它们共享相同的上下文配置,因此共享相同的缓存应用程序上下文。
但我注意到,当两个测试套件中的第二个运行在第一个之后,上面的应用程序侦听器没有收到通知。
总结一下:
第一个测试套件运行(从模式导出创建表和通知应用程序侦听器) 第二个测试套件运行(仅创建表但没有通知应用程序侦听器)【问题讨论】:
【参考方案1】:ContextRefreshedEvent
的 Javadoc 明确指出它是一个...
ApplicationContext
初始化或刷新时引发的事件。
因此,您的 BootstrapLoaderListener
只会在 ApplicationContext
启动(即,被初始化或刷新)时被调用,这只会发生一次:当 Spring TestContext 框架为您的测试加载上下文时。不过,如果您使用的是@DirtiesContext
,您的ApplicationContext
可能会被重新创建多次。
换句话说,您目睹的关于您的听众的行为是设计使然,并且是意料之中的。
至于为什么要第二次创建您的架构,这仍然是一个谜。通常这不应该发生。例如,如果您使用 Spring 的 LocalSessionFactoryBean
或 LocalSessionFactoryBuilder
,则这些工厂中的每一个都只会在 ApplicationContext
初始化或刷新时创建一次 Hibernate SessionFactory
。
也许您有一些非标准配置导致SessionFactory
被多次创建,但根据您提供的信息我无法判断。
但是,如果您希望在每次测试之前调用设置代码(因为它不会自动调用多次),则不建议使用ApplicationListener
设置测试数据库。如果您真的想在测试中使用ApplicationListener
,您可以在ApplicationContext
中为您的测试从@Before
或@BeforeTransaction
方法中触发您自己的ContextRefreshedEvent
。
作为替代方案(可能是更好的解决方案),您可以通过以下方式之一以编程方式在 @Before
或 @BeforeTransaction
方法中调用 SQL 脚本:
SimpleJdbcTestUtils.executeSqlScript(...)
方法之一(在 Spring 2.5 - 3.x 上)
JdbcTestUtils.executeSqlScript(...)
方法之一(在 Spring 4.x 及更高版本上)
ScriptUtils.executeSqlScript(...)
方法之一(在 Spring 4.0.3 及更高版本上)
AbstractTransactionalJUnit4SpringContextTests
或AbstractTransactionalTestNGSpringContextTests
中的executeSqlScript(...)
方法
ResourceDatabasePopulator
和 DatabasePopulatorUtils
从尚未发布的 Spring 4.1 开始,您可以通过 @Sql
注释以声明方式调用 SQL 脚本。
祝你好运!
Sam(Spring TestContext 框架的作者)
附言您还可以实现自定义 TestExecutionListener
以使用上述相同选项以编程方式调用 SQL 脚本。
【讨论】:
非常感谢 Sam 的详细回复。关于ContextRefreshedEvent
,其实和我想的一样。现在关于第二次重新创建模式,这对我来说仍然是一个谜!当我找到问题的原因时,我一定会在这里发表评论。以上是关于在 JUnit/Spring 测试期间触发 Hibernate 的 SchemaExport.execute (hbm2ddl.auto) 的原因是啥?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Junit5 / spring boot 中命令执行控制器测试类?
使用 JUnit5+Spring Security 测试 Spring Boot 控制器