在 OpenLiberty 上使用 Hibernate Envers 设置自定义修订信息

Setting custom revision information with Hibernate Envers on OpenLiberty

我们正在将应用程序从 JEE7 迁移到 JEE8。 此应用程序依赖于 Hibernate-ORM 和 Hibernate-Envers。在 JEE7 上,它是 5.2.17 版,在 JEE8 上,两个库现在都是 5.4.3 版。 我们有一个自定义修订实体,它通过 RevisionListener 的实现添加用户信息来扩展 DefaultRevisionEntity。 在 JEE7 上,它在 OpenLiberty 19.0.0.5 上运行良好,对于 JEE8 和 Hibernate 5.4.3,我们遇到了错误。在 JEE8 和 Hibernate 5.4.3 上,现在可以在 revisionlisteners 上使用 CDI 功能。 ()。 在 OpenLiberty 上,revisionlistener 初始化以某种方式完成,当没有 bean 管理器准备好使用时,就像这个堆栈跟踪显示的那样:

Caused by: java.lang.IllegalStateException: org.hibernate.resource.beans.container.internal.NotYetReadyException: CDI BeanManager not (yet) ready to use
[INFO]  at org.hibernate.resource.beans.container.internal.JpaCompliantLifecycleStrategy$BeanImpl.initialize(JpaCompliantLifecycleStrategy.java:112)
[INFO]  at org.hibernate.resource.beans.container.internal.CdiBeanContainerExtendedAccessImpl$BeanImpl.initialize(CdiBeanContainerExtendedAccessImpl.java:113)
[INFO]  at org.hibernate.resource.beans.container.internal.CdiBeanContainerExtendedAccessImpl$BeanImpl.getBeanInstance(CdiBeanContainerExtendedAccessImpl.java:119)
[INFO]  at org.hibernate.resource.beans.internal.ManagedBeanRegistryImpl$ContainedBeanManagedBeanAdapter.getBeanInstance(ManagedBeanRegistryImpl.java:139)
[INFO]  at org.hibernate.envers.internal.revisioninfo.DefaultRevisionInfoGenerator.generate(DefaultRevisionInfoGenerator.java:77)
[INFO]  at org.hibernate.envers.internal.synchronization.AuditProcess.getCurrentRevisionData(AuditProcess.java:133)
[INFO]  at org.hibernate.envers.internal.synchronization.AuditProcess.executeInSession(AuditProcess.java:115)
[INFO]  at org.hibernate.envers.internal.synchronization.AuditProcess.doBeforeTransactionCompletion(AuditProcess.java:174)
[INFO]  at org.hibernate.envers.internal.synchronization.AuditProcessManager.doBeforeTransactionCompletion(AuditProcessManager.java:47)
[INFO]  at org.hibernate.engine.spi.ActionQueue$BeforeTransactionCompletionProcessQueue.beforeTransactionCompletion(ActionQueue.java:954)
[INFO]  ... 20 more
[INFO] Caused by: org.hibernate.resource.beans.container.internal.NotYetReadyException: CDI BeanManager not (yet) ready to use
[INFO]  ... 31 more
[INFO] Caused by: java.lang.NullPointerException
[INFO]  at org.hibernate.resource.beans.container.internal.JpaCompliantLifecycleStrategy$BeanImpl.initialize(JpaCompliantLifecycleStrategy.java:109)
[INFO]  ... 29 more

我发现这个 https://discourse.hibernate.org/t/beanmanager-createinstance-being-called-before-afterbeandiscovery-event-fired/2239 对话可能指向相同的方向。

实际上这是我们在 server.xml 中设置的功能。

<featureManager>
    <feature>jaxrs-2.1</feature> 
    <feature>jsonp-1.1</feature>
    <feature>cdi-2.0</feature>
    <feature>jpaContainer-2.2</feature>
    <feature>ejbLite-3.2</feature>
    <feature>mpMetrics-1.1</feature>
    <feature>mpHealth-1.0</feature>
    <feature>mpConfig-1.3</feature>
    <feature>servlet-4.0</feature>
</featureManager>

如果您需要更多信息,我会提供。 这是一个已知问题吗?如果您能为这个问题提供提示或解决方案,我将不胜感激。

非常感谢。

是的,从我从 Hibernate 和 IBM 收到的回复来看,这似乎是一个已知问题,而且正如我在论坛中提到的那样,我能够在非常有帮助的指导下开发解决该问题的方法休眠团队。

解决方法是实施 org.hibernate.search.hcore.spi.EnvironmentSynchronizer 以控制 JPA 初始化何时可以安全地继续,并实施 javax.enterprise.inject.spi.Extension 以检测 CDI 何时准备好 BeanManager

我使用 org.hibernate.service.spi.ServiceContributor 界面注册了我的 EnvironmentSynchronizer 实现。在 EnvironmentSynchronizer 内部,whenEnvironmentReady 事件方法传入一个 Runnable,它表示负责 JPA 初始化的任务。这需要推迟到 CDI 完成 bean 发现,并且 BeanManager 准备就绪。

我的 CDI 扩展实现等待 AfterBeanDiscovery 事件方法,这是允许先前推迟的 JPA 初始化任务成功继续的正确时间。

在我找到解决方法后,我开始与 IBM 支持人员沟通以提高对兼容性问题的认识,并查看是否可以在未来的版本中将这种对 Hibernate 初始化的细粒度控制内置到 WebSphere Liberty 中。 IBM 表示他们希望 Hibernate 与 WebSphere 开箱即用,这太棒了,所以我希望在不久的将来我们能在 WebSphere Liberty 中看到对最新版本 Hibernate 的全面支持。

更新: 我刚刚确认,不幸的是,我们的 EnvironmentSynchronizer 实现不会延迟 Envers 初始化。 Envers 初始化仍然在 WebSphere 中启动时崩溃,我们的解决方法就位。

我将更新我们与 IBM 的案例以包括 Envers,并在 Hibernate 论坛中的线程中添加注释以查看是否有可用的即时选项。

更新: 这是我用 Hibernate Envers 和自定义 RevisionListener 测试过的解决方案。将此添加到 persistence.xml 将允许 WebSphere 成功启动:

<property name="hibernate.delay_cdi_access" value="true"/>

Steve Ebersole 的评论:

This option (2) essentially tells Hibernate to delay accessing the BeanManager until it first needs to which is during runtime. In other words, the first time you perform an operation that needs a particular CDI bean Hibernate will ask the BeanManager for it. This has a serious downside in that if the bean does not exist you will not know about that until runtime, conceivably months after a deployment.

我们可能会在短期内使用此选项,打算在 IBM WebSphere 完全支持开箱即用的最新版本的 Hibernate 后将其删除。