14

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.

Marcos
  • 1,237
  • 1
  • 15
  • 31
  • 3
    Maybe you got downvoted for "unfortunately I don't remember the specific cause" (you should research that cause) but I second that it doesn't work and an official answer for when we can expect compatibility would be nice, so +1 from me. (AFAIK it has to do with ASM not understanding Java 9 bytecode. Apparently ByyteBuddy does and is poised to replace ASM within Hibernate. But when?) – Nicolai Parlog Apr 06 '17 at 20:52
  • 1
    @Nicolai Thank you very much for your answer, patience and respect. I appreciated. Maybe the only thing that is left to us right now is to sit down and wait for the compatibility. – Marcos Apr 06 '17 at 23:06
  • Hibernate ORM already has support for ByteBuddy, it just defaults to Javassist still as we're conservative with changing default behaviours. Incidentally, latest Javassist works fine on Java 9 as well now. – Sanne Sep 28 '17 at 22:12
  • 1
    @Sanne Could you create an answer from what version on Hibernate works fine in Java 9 without any compiler / JVM arguments or other workarounds? – Terran Oct 10 '17 at 20:37

1 Answers1

23

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.

Community
  • 1
  • 1
David T
  • 415
  • 1
  • 4
  • 5
  • Thank you very much for sharing your findings. I'll give it a try later. – Marcos Apr 07 '17 at 16:34
  • Hi, Hibernate team here. Sure older versions aren't going to work, not least as we can't even test them: none of the older build/testing tools we need run fine without making changes. All up to date versions will work fine though, including Javassist & co. – Sanne Sep 28 '17 at 22:10