休眠对 Java 9 的支持

2022-09-04 01:17:27

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

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

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


答案 1

我想知道同样的事情,并尝试在Java 9的抢先体验版本下运行我的Hibernate应用程序。这是我学到的。

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

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

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

发生这些错误的原因是,休眠的运行时字节码增强功能被新模块系统中的强封装规则所阻止。请参阅此堆栈溢出帖子,以获取有关此问题的精彩描述。

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

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

  1. 使用 Hibernate 5.0.0 或更高版本(早期版本不起作用),以及
  2. 请求构建时字节码增强功能(使用 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中看到它们再次失败。请谨慎操作。


答案 2

推荐