web-dev-qa-db-ja.com

Java 9のHibernateサポート

HibernateはJava 9の利用可能なビルドで動作する準備ができていますか?

私はすでにそれを試したが失敗したことを覚えています。残念ながら、具体的な原因は覚えていません。

ちなみに、Hibernate Validator5.2.3はすでにJava 9で動作します。

12
Marcos

同じことを考えて、HibernateアプリをJava 9の早期アクセスリリースで実行してみました。これが私が学んだことです。

私が最初に遭遇した問題は、javax.xml.bind.JAXBExceptionのClassNotFoundExceptionでした。 JAXBはJava 6以降、ランタイムクラスパスに含まれていますが、Java 9では、デフォルトで公開されなくなりました。修正できる方法は少なくとも2つあります。この:

  1. Java 9 JVMでプログラムを実行するときは、コマンドライン引数 "--add-modules Java.se.ee"を含めます。これにより、Java 9 JAXB(および他のライブラリ)をクラスパスに再び含めるため。ただし、この引数はJava 8 JVM;であるため、ユーザーがより多くの下で実行できるようにする場合は、拒否されることに注意してください。 Javaの1つのバージョンよりも、コマンドラインを動的に計算するか、ユーザーにコマンドラインを手動で編集するように要求する必要があります。
  2. アプリケーションにJAXBライブラリを含めます。 Hibernateアプリがアノテーション駆動型である場合、実際にはJAXBの実装を必要としない場合があります。その場合、JAXBAPIを含めるだけで済みます。これが私の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のランタイムバイトコード拡張が新しいモジュールシステムの強力なカプセル化ルールによってブロックされているために発生しています。この問題の詳細については、 このstackoverflow post を参照してください。

その投稿に記載されているように、JVMの起動時に別のコマンドライン引数を追加することで、これらのエラーを排除できます。ただし、そのアプローチは回避策にすぎず、長期的な解決策としては適切ではありません。

多くの試行錯誤の末、私はより良い解決策を発見しました。

  1. Hibernate 5.0.0以降を使用し(以前のバージョンは機能しません)、
  2. リクエスト ビルド時のバイトコード拡張 (Gradle、Maven、またはAntプラグインを使用)。

これにより、Hibernateが実行時にJavassistベースのクラス変更を実行する必要がなくなり、上記のスタックトレースが排除されます。これをHibernate5.0.12.FINAL、5.1.5.Final、および5.2.9.Finalでテストしました。

[〜#〜]ただし[〜#〜]、後でアプリケーションを徹底的にテストする必要があります。ビルド時にHibernateによって適用されるバイトコードの変更は、実行時に適用されるものとは異なるように見え、アプリケーションの動作がわずかに異なります。ビルド時のバイトコード拡張を有効にすると、何年も成功していたアプリの単体テストが突然失敗しました。 (新しいLazyInitializationExceptionエラーやその他の問題を追跡する必要がありました。)そして、動作はHibernateのバージョンごとに異なるようです。ユニットテストを5.0.12で機能するように修正できましたが、5.1.5で再び失敗することがわかりました。 注意して続行してください。

21
David T