Hibernateインターセプタの例 – 監査ログ
Hibernateには、データベースCRUD操作のような異なる種類のHibernateイベントをインターセプトしたりフックしたりするための強力な機能があります。
この記事では、Hibernateインターセプタを使用してアプリケーション監査ログ機能を実装する方法を示し、Hibernateの保存、更新、または削除のすべての操作を ‘
auditlog
‘という名前のデータベーステーブルに記録します。
Hibernateインターセプタの例 – 監査ログ
1.テーブルを作成する
「auditlog」という表を作成して、すべてのアプリケーション監査レコードを保管します。
DROP TABLE IF EXISTS `mkyong`.`auditlog`; CREATE TABLE `mkyong`.`auditlog` ( `AUDIT__LOG__ID` bigint(20) unsigned NOT NULL AUTO__INCREMENT, `ACTION` varchar(100) NOT NULL, `DETAIL` text NOT NULL, `CREATED__DATE` date NOT NULL, `ENTITY__ID` bigint(20) unsigned NOT NULL, `ENTITY__NAME` varchar(255) NOT NULL, PRIMARY KEY (`AUDIT__LOG__ID`) ) ENGINE=InnoDB AUTO__INCREMENT=9 DEFAULT CHARSET=utf8;
2.マーカーインターフェースを作成する
マーカーインターフェイスを作成します。このインターフェイスを実装したクラスはすべて監査されます。このインタフェースでは、実装されたクラスがidentifier –
getId()
および内容をログに記録する – ‘
getLogDeatil()
‘を公開する必要があります。すべての公開データはデータベースに格納されます。
package com.mkyong.interceptor;//market interface
public interface IAuditLog {
public Long getId();
public String getLogDeatil();
}
3. ‘auditlog’テーブルをマップする
テーブル ‘auditlog’でマップする通常の注釈モデルファイル。
@Entity
@Table(name = "auditlog", catalog = "mkyong")
public class AuditLog implements java.io.Serializable {
private Long auditLogId;
private String action;
private String detail;
private Date createdDate;
private long entityId;
private String entityName;
...
}
4. IAuditLogを実装したクラス
後でインターセプタのデモに使用するテーブル ‘stock’でマップする通常のアノテーションモデルファイル。
IAuditLog
マーカーインターフェースを実装し、
getId()
および
getLogDeatil()
メソッドを実装する必要があります。
...
@エンティティ
@Table(name = "stock"、catalog = "mkyong"
パブリッククラスStockはjava.io.Serializable、IAuditLog {
...
@Transient
@オーバーライド
public Long getId(){
これを返す.stockId.longValue();
}
@Transient
@オーバーライド
public String getLogDeatil(){
StringBuilder sb =新しいStringBuilder();
sb.append( "在庫ID:").append(在庫ID)
.append( "ストックコード:").append(stockCode)
.append( "ストック名:").append(stockName);
戻り値sb.toString();
}
...
5. Create a Helper class
A helper class to accept the data from interceptor and store it into
database.
...
public class AuditLogUtil{
public static void LogIt(String action,
IAuditLog entity, Connection conn ){
Session tempSession = HibernateUtil.getSessionFactory().openSession(conn);
try {
AuditLog auditRecord = new AuditLog(action,entity.getLogDeatil()
, new Date(),entity.getId(), entity.getClass().toString());
tempSession.save(auditRecord);
tempSession.flush();
} finally {
tempSession.close();
}
}
}
6. Hibernateインターセプタクラスを作成する
Hibernate
EmptyInterceptor
を拡張してインターセプタクラスを作成します。
ここで最も一般的なインターセプタ関数です。
データベースはまだありません。
-
onFlushDirty – オブジェクトを更新するときに呼び出され、オブジェクトは更新されません
まだデータベースに更新しないでください。
-
onDelete – オブジェクトを削除すると呼び出され、オブジェクトは削除されません
データベースにまだ入っていません。
-
preFlush – 保存、更新、または削除されたオブジェクトの前に呼び出されます。
データベースにコミットします(通常postFlushの前)。
-
postFlush – 保存、更新、または削除されたオブジェクトの後に呼び出されます。
データベースにコミットします。
コードは非常に冗長であり、自己探索的でなければなりません。
...
パブリッククラスAuditLogInterceptorはEmptyInterceptorを継承します。プライベートセットの挿入=新しいHashSet();プライベートセットの更新=新しいHashSet();プライベートセットの削除=新しいHashSet(); public void setSession(Session session){this.session = session; } public boolean onSave(Object entity、Serializable id、Object[]state、String[]propertyNames、Type[]types)throws CallbackException {System.out.println( "onSave"); if(エンティティインスタンスのIAuditLog){inserts.add(エンティティ); }偽を返します。 }}}}} public boolean onFlushDirty(Object entity、Serializable id、Object[]currentState、Object[]previousState、String[]プロパティ名、型[]型)は、CallbackExceptionをスローします(System.out.println( "onFlushDirty"); if(エンティティインスタンスIAuditLog){updates.add(エンティティ); }偽を返します。 } public void onDelete(Object entity、Serializable id、Object[]state、String[]プロパティ名、型[]型){System.out.println( "onDelete"); if(エンティティインスタンスIAuditLog){deletes.add(エンティティ); }}
//called before commit into database
public void preFlush(Iterator iterator) {
System.out.println("preFlush");
}
//called after committed into database
public void postFlush(Iterator iterator) {
System.out.println("postFlush");
try{
for (Iterator it = inserts.iterator(); it.hasNext();) {
IAuditLog entity = (IAuditLog) it.next();
System.out.println("postFlush - insert");
AuditLogUtil.LogIt("Saved",entity, session.connection());
}
for (Iterator it = updates.iterator(); it.hasNext();) {
IAuditLog entity = (IAuditLog) it.next();
System.out.println("postFlush - update");
AuditLogUtil.LogIt("Updated",entity, session.connection());
}
for (Iterator it = deletes.iterator(); it.hasNext();) {
IAuditLog entity = (IAuditLog) it.next();
System.out.println("postFlush - delete");
AuditLogUtil.LogIt("Deleted",entity, session.connection());
}
} finally {
inserts.clear();
updates.clear();
deletes.clear();
}
}
}
7.Enabling the interceptor
You can enable the interceptor by pass it as an argument to
openSession(interceptor);
.
...
Session session = null;
Transaction tx = null;
try {
AuditLogInterceptor interceptor = new AuditLogInterceptor();
session = HibernateUtil.getSessionFactory().openSession(interceptor);
interceptor.setSession(session);
//test insert
tx = session.beginTransaction();
Stock stockInsert = new Stock();
stockInsert.setStockCode("1111");
stockInsert.setStockName("mkyong");
session.saveOrUpdate(stockInsert);
tx.commit();
//test update
tx = session.beginTransaction();
Query query = session.createQuery("from Stock where stockCode = '1111'");
Stock stockUpdate = (Stock)query.list().get(0);
stockUpdate.setStockName("mkyong-update");
session.saveOrUpdate(stockUpdate);
tx.commit();
//test delete
tx = session.beginTransaction();
session.delete(stockUpdate);
tx.commit();
} catch (RuntimeException e) {
try {
tx.rollback();
} catch (RuntimeException rbe) {
//log.error("Couldn’t roll back transaction", rbe);
}
throw e;
} finally {
if (session != null) {
session.close();
}
}
...
In insert test
session.saveOrUpdate(stockInsert);//onSaveを呼び出します tx.commit();//preFlushを呼び出すと、postFlushが続きます。
更新テスト
session.saveOrUpdate(stockUpdate);//onFlushDirtyを呼び出します tx.commit();//preFlushを呼び出すと、postFlushが続きます。
削除テストで
session.delete(stockUpdate);//onDeleteを呼び出します tx.commit();//preFlushを呼び出すと、postFlushが続きます。
======出力
onSave Hibernate:mkyong.stock(STOCK__CODE、STOCK__NAME)の値(?、?)に挿入するpreFlush postFlush postFlush - hibernateを挿入する:mkyong.auditlog(ACTION、CREATED__DATE、DETAIL、ENTITY__ID、ENTITY__NAME)の値(?、?、?、 ?、?)preFlush Hibernate:select ... mkyong.stock stock0__よりStock0__.STOCK__CODE = '1111' preFlush onFlushDirty Hibernate:mkyong.stock setをSTOCK__CODE = ?, STOCK__NAME =に更新しますか?ここで、STOCK__ID =? postFlush postFlush - hibernateを更新します。mKeyong.auditlog(ACTION、CREATED__DATE、DETAIL、ENTITY__ID、ENTITY__NAME)の値(?、?、?、?、?)に挿入します。preFlush hibernate:mkyong.stockから削除します。 ポストフラッシュ postFlush - 削除 休止状態: mkyong.auditlogに挿入する (ACTION、CREATED__DATE、DETAIL、ENTITY__ID、ENTITY__NAME) 値(?、?、?、?、?)
======データベース内
SELECT ** FROM auditlog a;
すべての監査データがデータベースに挿入されます。

===結論
監査ログは、多くの場合データベースで処理される便利な機能です。
トリガを使用して、私はそれを実装するためにアプリケーションを使用することをお勧めします
移植性に関する懸念があります。
この例をダウンロードする –
リンク://wp-content/uploads/2010/02/HibernateInterceptotExample.zip[Hibernate
インターセプタexample.zip]
hibernate
interceptor