1. 概要

このディスカッションでは、Hibernateの抽象化されたリレーショナルマッピング実装内で操作をインターセプトするさまざまな方法を見ていきます。

2. Hibernateインターセプターの定義

Hibernate Interceptorは、Hibernate内の特定のイベントに反応できるようにするインターフェースです。

これらのインターセプターはコールバックとして登録され、Hibernateのセッションとアプリケーション間の通信リンクを提供します。 このようなコールバックを使用すると、アプリケーションは保存、更新、削除などのコアHibernateの操作をインターセプトできます。

インターセプターを定義する方法は2つあります。

  1. org.hibernate.Interceptorインターフェースの実装
  2. org.hibernate.EmptyInterceptorクラスの拡張

2.1. インターセプターインターフェースの実装

org.hibernate.Interceptorを実装するには、約14の付随するメソッドを実装する必要があります。 これらのメソッドには、 onLoad、onSave、onDelete、findDirty、などがあります。

Interceptorインターフェイスを実装するクラスがシリアル化可能であることを確認することも重要です(実装java .io.Serializable )。

典型的な例は次のようになります。

public class CustomInterceptorImpl implements Interceptor, Serializable {

    @Override
    public boolean onLoad(Object entity, Serializable id, 
      Object[] state, String[] propertyNames, Type[] types) 
      throws CallbackException {
        // ...
        return false;
    }

    // ...

    @Override
    public String onPrepareStatement(String sql) {
        // ...   
        return sql;
    }

}

特別な要件がない場合は、 EmptyInterceptor クラスを拡張し、必要なメソッドのみをオーバーライドすることを強くお勧めします。

2.2. EmptyInterceptorを拡張する

org.hibernate.EmptyInterceptor クラスを拡張すると、インターセプターを定義する簡単な方法が提供されます。 インターセプトする操作に関連するメソッドのみをオーバーライドする必要があります。

たとえば、CustomInterceptorを次のように定義できます。

public class CustomInterceptor extends EmptyInterceptor {
}

また、実行前にデータ保存操作をインターセプトする必要がある場合は、onSaveメソッドをオーバーライドする必要があります。

@Override
public boolean onSave(Object entity, Serializable id, 
  Object[] state, String[] propertyNames, Type[] types) {
    
    if (entity instanceof User) {
        logger.info(((User) entity).toString());
    }
    return super.onSave(entity, id, state, propertyNames, types);
}

User の場合、この実装がエンティティを単純に出力することに注意してください。

trueまたはfalseの値を返すことは可能ですが、 super.onSave()を呼び出してonSaveイベントの伝播を許可することをお勧めします。

別のユースケースは、データベースの相互作用の監査証跡を提供することです。 onFlushDirty()メソッドを使用して、エンティティがいつ変更されるかを知ることができます。

User オブジェクトの場合、タイプ User のエンティティに変更が発生するたびに、そのlastModified日付プロパティを更新することを決定できます。

これは、次の方法で実現できます。

@Override
public boolean onFlushDirty(Object entity, Serializable id, 
  Object[] currentState, Object [] previousState, 
  String[] propertyNames, Type[] types) {
    
    if (entity instanceof User) {
        ((User) entity).setLastModified(new Date());
        logger.info(((User) entity).toString());
    }
    return super.onFlushDirty(entity, id, currentState, 
      previousState, propertyNames, types);
}

deleteload(オブジェクトの初期化)などの他のイベントは、対応するonDeleteメソッドとonLoadメソッドをそれぞれ実装することでインターセプトできます。

3. インターセプターの登録

Hibernateインターセプターは、SessionスコープまたはSessionFactoryスコープとして登録できます。

3.1. セッションスコープのインターセプター

Sessionスコープのインターセプターが特定のセッションにリンクされています。 セッションが次のように定義または開かれているときに作成されます。

public static Session getSessionWithInterceptor(Interceptor interceptor) 
  throws IOException {
    return getSessionFactory().withOptions()
      .interceptor(interceptor).openSession();
}

上記では、特定の休止状態セッションにインターセプターを明示的に登録しました。

3.2. SessionFactory-スコープインターセプター

SessionFactory-スコープのインターセプターは、 SessionFactoryを構築する前に登録されます。これは通常、SessionFactoryBuilderインスタンスのapplyInterceptorメソッドを介して行われます。

ServiceRegistry serviceRegistry = configureServiceRegistry();
SessionFactory sessionFactory = getSessionFactoryBuilder(serviceRegistry)
  .applyInterceptor(new CustomInterceptor())
  .build();

SessionFactory-スコープのインターセプターがすべてのセッションに適用されることに注意することが重要です。 したがって、セッション固有の状態を保存しないように注意する必要があります。このインターセプターは、異なるセッションで同時に使用されるためです。

セッション固有の動作については、前に示したように、別のインターセプターでセッションを明示的に開くことをお勧めします。

SessionFactory スコープのインターセプターの場合、当然、スレッドセーフであることを確認する必要があります。 これは、プロパティファイルでセッションコンテキストを指定することで実現できます。

hibernate.current_session_context_class=org.hibernate.context.internal.ThreadLocalSessionContext

または、これをXML構成ファイルに追加します。

<property name="hibernate.current_session_context_class">
    org.hibernate.context.internal.ThreadLocalSessionContext
</property>

また、直列化可能性を確保するために、 SessionFactory スコープのインターセプターは、SerializableインターフェースのreadResolveメソッドを実装する必要があります。

4. 結論

HibernateインターセプターをSessionスコープまたはSessionFactoryスコープとして定義および登録する方法を見てきました。 いずれの場合も、特にシリアライズ可能なセッションが必要な場合は、インターセプターがシリアライズ可能であることを確認する必要があります。

インターセプターのその他の代替手段には、HibernateEventsとJPACallbacksがあります。

そして、いつものように、Githubで完全なソースコードをチェックアウトできます。