对 Java 9 的休眠支持

Posted

技术标签:

【中文标题】对 Java 9 的休眠支持【英文标题】:Hibernate support for Java 9 【发布时间】:2017-09-01 16:44:27 【问题描述】:

Hibernate 准备好使用 Java 9 的可用版本了吗?

我记得我已经尝试过了,但失败了。不幸的是,我不记得具体原因了。

顺便说一句,Hibernate Validator 5.2.3 已经可以与 Java 9 一起使用。

【问题讨论】:

也许你因为“不幸的是我不记得具体原因”而被否决了(你应该研究那个原因),但我认为它不起作用,官方回答什么时候可以期待兼容性会很好,所以我+1。 (AFAIK 这与 ASM 不理解 Java 9 字节码有关。显然,ByyteBuddy 确实并且准备在 Hibernate 中取代 ASM。但是什么时候?) @Nicolai 非常感谢您的回答、耐心和尊重。我很感激。也许我们现在唯一剩下的就是坐下来等待兼容性。 Hibernate ORM 已经支持 ByteBuddy,它仍然默认为 Javassist,因为我们对更改默认行为持保守态度。顺便说一句,最新的 Javassist 现在在 Java 9 上也能正常工作。 @Sanne 您能否在没有任何编译器/JVM 参数或其他解决方法的情况下从 Hibernate 上的哪个版本在 Java 9 中正常工作来创建答案? 【参考方案1】:

我也想知道同样的事情,并尝试在 Java 9 的早期访问版本下运行我的 Hibernate 应用程序。这是我学到的。

我遇到的第一个问题是 javax.xml.bind.JAXBException 的 ClassNotFoundException。 JAXB 自 Java 6 以来一直在运行时类路径中,但在 Java 9 中,它不再默认发布。至少有两种方法可以解决此问题:

    在 Java 9 JVM 中运行程序时,请包含命令行参数“--add-modules java.se.ee”。这指示 Java 9 再次在类路径中包含 JAXB(和其他库)。但请记住,这个论点将被 Java 8 JVM 拒绝;因此,如果您允许用户在多个 Java 版本下运行,则必须动态计算命令行,或要求用户手动编辑命令行。

    在您的应用程序中包含 JAXB 库。如果您的 Hibernate 应用程序是注解驱动的,它实际上可能不需要 JAXB 的任何实现,在这种情况下,您只需包含 JAXB API 即可。这是我添加到我的 pom 中的依赖项:

    <!-- JAXB API -->
    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.2.11</version>
    </dependency>
    

解决了 JAXB 问题后,我运行了应用程序并收到了数千行堆栈跟踪,如下所示:

java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @49f97198
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:337)
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:281)
    at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:197)
    at java.base/java.lang.reflect.Method.setAccessible(Method.java:191)
    at javassist.util.proxy.SecurityActions.setAccessible(SecurityActions.java:103)
    at javassist.util.proxy.FactoryHelper.toClass2(FactoryHelper.java:181)
    at javassist.util.proxy.FactoryHelper.toClass(FactoryHelper.java:164)
    at javassist.util.proxy.ProxyFactory.createClass3(ProxyFactory.java:507)
    at javassist.util.proxy.ProxyFactory.createClass2(ProxyFactory.java:492)
    at javassist.util.proxy.ProxyFactory.createClass1(ProxyFactory.java:428)
    at javassist.util.proxy.ProxyFactory.createClass(ProxyFactory.java:400)
    at org.hibernate.proxy.pojo.javassist.JavassistProxyFactory.postInstantiate(JavassistProxyFactory.java:72)
    at org.hibernate.tuple.entity.PojoEntityTuplizer.buildProxyFactory(PojoEntityTuplizer.java:162)
    at org.hibernate.tuple.entity.AbstractEntityTuplizer.<init>(AbstractEntityTuplizer.java:163)
    at org.hibernate.tuple.entity.PojoEntityTuplizer.<init>(PojoEntityTuplizer.java:58)

出现这些异常后,您的应用可能看似正常运行。但不要上当:对象的延迟初始化已被禁用。因此,您可能会遇到严重的性能问题。

出现这些错误是因为新模块系统中的强封装规则阻止了 Hibernate 的运行时字节码增强功能。有关此问题的详细说明,请参阅 this *** post。

如那篇文章所述,您可以通过在启动 JVM 时添加另一个命令行参数来消除这些错误。但这种方法只是一种变通方法,并不是一个好的长期解决方案。

经过反复试验,我发现了一个更好的解决方案:

    使用 Hibernate 5.0.0 或更高版本(早期版本不起作用),并且 请求 build-time bytecode enhancement(使用 Gradle、Maven 或 Ant 插件)。

这避免了 Hibernate 在运行时执行基于 Javassist 的类修改的需要,消除了上面显示的堆栈跟踪。我用 Hibernate 5.0.12.FINAL、5.1.5.Final 和 5.2.9.Final 对此进行了测试。

但是,您应该在之后彻底测试您的应用程序。 Hibernate 在构建时应用的字节码更改似乎与在运行时应用的不同,导致应用程序行为略有不同。当我启用构建时字节码增强时,我的应用程序中成功多年的单元测试突然失败了。 (我不得不追查新的 LazyInitializationException 错误和其他问题。)而且行为似乎从一个版本的 Hibernate 到另一个版本有所不同;我可以修复我的单元测试以在 5.0.12 中工作,只是看到它们在 5.1.5 中再次失败。 谨慎行事。

【讨论】:

非常感谢您分享您的发现。我稍后会试一试。 嗨,Hibernate 团队在这里。当然旧版本不会工作,尤其是因为我们甚至无法测试它们:我们需要的旧版本/测试工具都不能在不进行更改的情况下正常运行。所有最新版本都可以正常工作,包括 Javassist & co。

以上是关于对 Java 9 的休眠支持的主要内容,如果未能解决你的问题,请参考以下文章

具有休眠依赖项的 Jar 文件无法使用 jdk 9+(openjdk 12)执行

centos7.9会自动休眠吗

vmware黑群晖硬盘休眠

休眠:多对一关系失败

从Java线程到kotlin协程之线程休眠 (sleep)

org.hibernate.HibernateException:没有在休眠升级时指定 TransactionManagerLookup