休止状態のインターセプター
1. 概要
このディスカッションでは、Hibernate’sの抽象化されたリレーショナルマッピング実装内で操作をインターセプトするさまざまな方法を見ていきます。
2. Hibernateインターセプターの定義
Hibernate Interceptorは、Hibernate内の特定のイベントに反応できるようにするインターフェースです。
これらのインターセプターはコールバックとして登録され、Hibernateのセッションとアプリケーション間の通信リンクを提供します。 このようなコールバックを使用すると、アプリケーションはsave, update, delete, etc.などのコアHibernateの操作をインターセプトできます。
インターセプターを定義するには2つの方法があります。
-
implementing the org.hibernate.Interceptorインターフェース
-
extending the org.hibernate.EmptyInterceptorクラス
2.1. Interceptorインターフェースの実装
Implementing org.hibernate.Interceptor requires implementing about 14 accompanying methods。 これらのメソッドには、onLoad, onSave, onDelete, findDirty,などが含まれます。
Interceptorインターフェースを実装するクラスがシリアル化可能(implements 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;
}
}
特別な要件がない場合は、extending the EmptyInterceptorクラスを使用し、必要なメソッドのみをオーバーライドすることを強くお勧めします。
2.2. EmptyInterceptorの拡張
org.hibernate.EmptyInterceptorクラスを拡張すると、インターセプターを定義する簡単な方法が提供されます。 We now only need to override the methods that relate to the operation we want to intercept.
たとえば、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イベントの伝播を許可することをお勧めします。
Another use-case would be providing an audit trail for database interactions.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);
}
deleteやload(オブジェクトの初期化)などの他のイベントは、対応するonDeleteメソッドとonLoadメソッドをそれぞれ実装することでインターセプトできます。
3. Interceptorsの登録
Hibernateインターセプターは、SessionスコープまたはSessionFactory-scopedとして登録できます。
3.1. Session-scoped Interceptor
Sessionスコープのインターセプターが特定のセッションにリンクされています。 セッションが次のように定義または開かれているときに作成されます。
public static Session getSessionWithInterceptor(Interceptor interceptor)
throws IOException {
return getSessionFactory().withOptions()
.interceptor(interceptor).openSession();
}
上記では、インターセプターを特定の休止状態セッションに明示的に登録しました。
3.2. SessionFactory-スコープInterceptor
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構成ファイルに追加します。
org.hibernate.context.internal.ThreadLocalSessionContext
また、直列化可能性を確保するために、SessionFactoryスコープのインターセプターはSerializableインターフェースのreadResolveメソッドを実装する必要があります。
4. 結論
HibernateインターセプターをSession-scopedまたはSessionFactory-scopedとして定義および登録する方法を見てきました。 どちらの場合でも、特にシリアライズ可能なセッションが必要な場合は、インターセプターがシリアライズ可能であることを確認する必要があります。
インターセプターの他の代替手段には、HibernateイベントとJPAコールバックが含まれます。
そして、いつものように、完全なsource code over on Githubをチェックアウトできます。