Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate support for Java 9

Is Hibernate ready to work with the available builds of Java 9?

I remember that I already tried it and it failed. Unfortunately I don't remember the specific cause.

By the way, Hibernate Validator 5.2.3 already works with Java 9.

like image 351
Marcos Avatar asked Apr 06 '17 14:04

Marcos


People also ask

Which version of Hibernate is compatible with Java 11?

Hibernate 5.3 lists Java 11 as supported in the latest release 5.3. 22. Moreover, Hibernate 5.6 is also Java 11 compatible.

Which Hibernate version is compatible with Java 8?

Hibernate 5.3 This version supports Java 8 and the latest versions and JPA 2.1 and the latest versions.

How do I download Hibernate ORM?

Download the latest version of Hibernate from http://www.hibernate.org/downloads.


1 Answers

I wondered the same thing, and tried running my Hibernate app under an early access release of Java 9. Here's what I learned.

The first problem I encountered was a ClassNotFoundException for javax.xml.bind.JAXBException. JAXB has been in the runtime classpath since Java 6, but in Java 9 it is no longer published by default. There are at least two ways you can fix this:

  1. When you run your program in the Java 9 JVM, include the command line argument "--add-modules java.se.ee". This instructs Java 9 to include JAXB (and other libraries) in the classpath again. But keep in mind, this argument will be rejected by a Java 8 JVM; so if you are allowing your users to run under more than one version of Java, you'll have to compute your command line dynamically, or require your users to edit the command lines manually.
  2. Include the JAXB library in your application. If your Hibernate app is annotation-driven, it may not actually require any implementation of JAXB, in which case you can get away with only including the JAXB API. Here is the dependency I added to my pom:

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

With the JAXB problem resolved, I ran the app and received several thousand lines of stack traces that looked like this:

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)

After these exceptions, your app may appear to run normally. But don't be fooled: lazy initialization of objects has been disabled. You may experience significant performance problems as a result.

These errors are occurring because the Hibernate's runtime bytecode enhancement is being blocked by the strong encapsulation rules in the new module system. See this stackoverflow post for a great description of this problem.

As noted in that post, you can eliminate these errors by adding another command line argument when you launch the JVM. But that approach is only a workaround, and not a good long-term solution.

After much trial and error, I discovered a better solution:

  1. Use Hibernate 5.0.0 or higher (earlier versions won't work), and
  2. Request build-time bytecode enhancement (using the Gradle, Maven, or Ant plugins).

This avoids the need for Hibernate to perform Javassist-based class modifications at runtime, eliminating the stack trace shown above. I tested this with Hibernate 5.0.12.FINAL, 5.1.5.Final, and 5.2.9.Final.

HOWEVER, you should thoroughly test your application afterward. The bytecode changes applied by Hibernate at build-time appear to differ from the ones applied at runtime, causing slightly different application behavior. Unit tests in my app that have succeeded for years suddenly failed when I enabled build-time bytecode enhancement. (I had to chase down new LazyInitializationException errors and other problems.) And the behavior seems to vary from one version of Hibernate to another; I could fix my unit tests to work in 5.0.12, only to see them fail again in 5.1.5. Proceed with caution.

like image 155
David T Avatar answered Sep 21 '22 05:09

David T