加速休眠初始化

Posted

技术标签:

【中文标题】加速休眠初始化【英文标题】:Speed Up Hibernate Initialization 【发布时间】:2014-09-22 18:56:15 【问题描述】:

我有一个在 JBoss 7 上运行的 Java EE 应用程序。为了进行 TDD,我正在设置使用 Arquillian 与嵌入式 Weld 和 H2 嵌入式数据库的嵌入式测试。

这很好用,但是 Hibernate 的初始启动需要相当长的时间(5-10 秒),而且我还没有包含所有 JPA 实体。我尝试使用持久化的 Oracle DB 来避免创建表,但这并没有太大的区别。

主要问题似乎是 Hibernate 遍历所有实体并验证和准备所有 CRUD 方法、命名查询等。

有没有办法告诉 Hibernate 在需要时(或根本不这样做)懒惰地执行此操作?大多数情况下,测试用例中只涉及实体和查询的子集,所以我很乐意在实施时用执行时间换取启动时间。

有什么想法吗?

我知道我可以只使用实体的一个子集,但有时会很困难,因为它们通常与测试上下文中不需要的其他实体有关系。或者是否有一种简单的方法可以“停用”此类关系以生成数据库的子集?

澄清

似乎不清楚我的问题是什么,所以我会尝试澄清:

我已经使用 Arquillian(嵌入式 Weld)设置了一个测试环境,该环境设置了一个嵌入式数据库 (H2) 来执行启用 JPA 的测试 我想使用这种方法进行测试驱动开发 (TDD),这意味着我将在我的本地开发机器上拥有以下工作流程:
    创建测试用例 运行测试用例 如果测试用例失败,实施必要的更改并返回 2。
通常,在完成一项功能之前,我会执行几次步骤 2 和 3,这意味着我通常会从我的 IDE 运行单个测试,该测试必须使用 Arquillian、Weld、嵌入式 DB 和只需运行一次测试即可。

场景就这么多了。现在我注意到运行该单个测试大约需要 10 秒,这不是世界末日,而是做 TDD 的时间。当我进一步调查时,我注意到大部分时间都用于 Hibernate 设置其内部结构(不是 Weld、Arquillian、Schema 创建或其他什么,而是 Hibernate 准备提供 EntityManager)。

所以我的问题是:有没有办法加快休眠初始化,以便我可以将这 10 秒缩短到 1-2 秒?我不在乎这是否是一种 hack(比如在多次手动测试运行期间保持测试 JVM 与休眠状态或停用休眠的某些验证或优化)。我唯一的问题是单个测试的启动时间。连续的测试运行良好且快速,所以我对完整的回归测试或在我的构建服务器上进行测试没有问题。

希望这能让我的情况更清楚...

【问题讨论】:

【参考方案1】:

让我猜猜,你在使用junit 库吗?

数据库架构创建通常不是最耗时的操作(尽管它取决于实体的数量)。

就个人而言,如果我是你,我会用 TestNG 运行你所有的 JUnit 测试(是的,TestNG 可以开箱即用地运行所有 JUnit 测试)并会看看 <your-module>/test-output/index.html(尤其是它的部分 Chronological viewTimes)。然后你就会知道哪些操作是最耗时的。有了这些信息,你可以走得更远。

还有几点建议:

    使用远程(或托管)容器的 Arquillian 测试通常更快,因为您只需启动一次服务器。 H2 嵌入式数据库确实是一个不错的选择。通常,您不必放弃它(通常,因为有时您的应用程序可能会使用 H2 中不存在的一些目标数据库功能)。 Arquillian Suite Extension 允许您只进行一次部署并在测试类中重复使用它。在许多测试的情况下,此扩展可以显着加快测试执行速度。 您可以在任何容器外测试您的实体(所谓的standalone JPAtransaction-type="RESOURCE_LOCAL")。这是最快的方法,但仅适用于测试实体注释、关系、查询等。

编辑

有很多方法可以更好地完成任务。只是几点:

    您不必每次都重新创建数据库。即使是嵌入式数据库(H2、HSQLDB、Derby)也有server mode,它们的使用寿命比 JVM 更长。 如果 Hibernate 初始化困扰您,请在所有测试中执行一次(我的意思是在单元测试之间保留 EntityManagerFactory)。 如果您想避免创建数据库,只需在服务器模式下使用 H2 并将休眠设置为不进行任何更改 (<property name="hibernate.hbm2ddl.auto" value="none" />)

【讨论】:

感谢您的反馈!是的,我正在使用 JUnit。不,模式创建并不是最糟糕的事情——它主要是 Hibernate 验证、设置它的内部结构或它在实际能够为持久性单元创建 EntityManager 之前所做的任何事情。嗯,不知道 TestNG - 但我不认为它会给我像分析信息这样的东西,对吗?或者你所说的“操作”是什么意思? 关于远程容器:我也有远程容器测试,但使用自定义框架而不是 Arquillian 来执行测试。我们有一个遗留(EJB 2 和 EJB 3 / JPA)应用程序,使用微部署确实是 PITA 恕我直言。使用嵌入式焊接,我也进行部署,但仅提供启用 CDI 的类。我仍然可以使用我的测试类路径。我真的看不到远程服务器上的微部署适用于具有大量依赖项的现实应用程序...... 理想情况下,我可以以某种方式保持嵌入式 Weld 容器运行以多次执行测试(即执行真正的 TDD),但我无法完成。尝试使用查找、断点等进行一些调整,并尝试使用 Eclipse 进行热代码替换,但它并没有真正奏效:-( @mmey 是的,TestNG 不会给你分析,但会给你一个很好的总结测试执行期间最耗时的方法。您不必切换到 TestNG,只需使用一次即可获取该信息。顺便说一句:要保持嵌入式 Weld 容器运行,请查看 Arquillian Suite Extension 好的,谢谢,如果我有时间我可能会检查一下 TestNG。我不认为 Suite Extension 对我有用。问题是初始数据库设置(例如,使用单一测试方法进行 TDD 时),之后我重用嵌入式数据库并且一切运行顺利......我需要以某种方式保留 JVM(使用嵌入式数据库)在我的 IDE 中更改内容时运行,然后重新执行测试。

以上是关于加速休眠初始化的主要内容,如果未能解决你的问题,请参考以下文章

休眠:org.hibernate.LazyInitializationException:无法初始化代理 - 没有会话 [重复]

休眠:延迟初始化与损坏的哈希码/等于难题

休眠延迟加载不适用于 Spring Boot => 无法延迟初始化角色集合无法初始化代理 - 无会话

休眠的动态类

休眠搜索,现有数据不可搜索

休眠拦截器 - 加载事件之后