Hibernate攔截器與事件監聽
攔截器(Interceptor) org.hibernate.Interceptor介面定義了Hibernate中通用攔截機制 建立Session物件的時候,所有的Session物件或者這個Session物件的所有持久化操作的動作都會被指定的攔截器進行攔截.
Interceptor介面的方法
- afterTransactionBegin() 當一個事務時候啟動時,會立刻呼叫這個方法,這個方法可以改變這個事務的狀態,例如:回滾事務
- instantiate() 建立物件,如果返回null,則Hibernate將呼叫實體類的預設構造方法建立持久化物件
- getEntity() 當一個持久化物件,通過標示符屬性在Session物件的快取中進行查詢,並且沒有找到時,會呼叫該方法
- getEntityName() 當session物件獲取持久化物件的名字時,會呼叫這個方法
- onLoad() 該方法在持久化物件初始化之前載入,這個的持久化物件處於剛被建立的狀態(物件的屬性值都未賦值)
- findDirty() 當呼叫Session物件的flush()方法時,講呼叫該方法判斷物件是否為髒資料,這是髒資料檢查的另外攔截的實現方式
- isTransient() 當呼叫Session物件的saveOrUpdate方法時,會呼叫該方法判斷物件是否尚未儲存
- onSave() 在物件被儲存之前呼叫,通過這個方法可以對要保持的物件的屬性進行修改
- onDelete() 該方法在持久化物件被刪除之前呼叫
- preFlush() 該方法當呼叫Session物件的flush()方法之前被呼叫
- onFlushDirty() 當呼叫Session物件flush()方法進行髒資料檢查時,如果發現持久化物件的狀態發生了改變,會呼叫該方法
- postFlush() 該方法呼叫Session物件的flush()方法之後被呼叫
- beforeTransactionCompletion() 在完成一個事務之前,呼叫此方法,這個方法可以改變事務的狀態,例如回滾事務
- afterTransactionCompletion() 當完成一個事務之後,立刻呼叫此方法
1、使用攔截器實現操作日誌 在應用系統中,對所有的資料庫的操作都做記錄,記錄所操作內容,操作的使用者和操作的時間
import org.hibernate.CallbackException;
import org.hibernate.EmptyInterceptor;
import org.hibernate.type.Type;
import java.io.Serializable;
import java.util.Date;
public class DaoLogInterceptor extends EmptyInterceptor {
private static final long serialVersionUID = 1L;
@Override
public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
super.onDelete(entity, id, state, propertyNames, types);
}
@Override
public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
return super.onSave(entity, id, state, propertyNames, types);
}
@Override
public void onCollectionUpdate(Object collection, Serializable key) throws CallbackException {
super.onCollectionUpdate(collection, key);
}
@Override
public void onCollectionRemove(Object collection, Serializable key) throws CallbackException {
super.onCollectionRemove(collection, key);
}
@Override
public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
return super.onLoad(entity, id, state, propertyNames, types);
}
@Override
public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
System.out.println("operateTm:"+entity.getClass().getName()+"--OperateTm:"+new Date()+"--操作使用者:當前登入使用者");
getOperateDetails(previousState, currentState, propertyNames);
return super.onFlushDirty(entity, id, currentState, previousState, propertyNames, types);
}
/**
* 操作前後的資料差異
*
* @param oldState
* @param state
* @param operateLog
* @return
*/
private void getOperateDetails(Object[] oldState, Object[] state, String[] propertyNames) {
for (int i = 0; i < propertyNames.length; i++) {
if (!equalsObject(oldState[i], state[i])) {
System.out.println("Field:"+propertyNames[i]+"NewValue:"+state[i] +"OldValue:"+oldState[i]);
}
}
}
private boolean equalsObject(Object o1, Object o2) {
if (o1 == null && o2 != null) {
return false;
}
if (o2 == null && o1 != null) {
return false;
}
if (o1 == null && o2 == null) {
return true;
}
return o1.equals(o2);
}
}
<!--hibernate配置-->
<bean id="myInterceptor" class="com.sf.sfbuy2.context.filter.DaoLogInterceptor"/>
<bean id="postUpdateListener" class="com.sf.sfbuy2.context.filter.PostUpdateListener"></bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<property name="entityInterceptor">
<ref bean="myInterceptor" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="use_sql_comments">true</prop>
<prop key="javax.persistence.validation.mode">none</prop>
<prop key="hibernate.validator.apply_to_ddl">false</prop>
<prop key="hibernate.validator.autoregister_listeners">false</prop>
</props>
</property>
<property name="packagesToScan">
<list>
<value>com.sf.sfbuy2.db.entity</value>
</list>
</property>
</bean>
2、Hibernate的事件監聽機制
Hibernate中的事件監聽機制可以對Session物件的動作進行監聽,一旦發生了特殊的事件,Hibernate就會執行監聽器中的事件處理方法
在某些功能的設計中,我們即可以使用Hibernate的攔截器實現,也可以使用Hibernate的事件監聽來實現
Hibernate中事件與對應的監聽器介面
事件型別 監聽器介面
auto-flush AutoFlushEventListener merge MergeEventListener delete DeleteEventListener persist PersistEventListener dirty-check DirtyCheckEventListener evice EvictEventListener flush FlushEventListener flush-entity FlushEntityEventListener load LoadEventListener load-collection InitializeCollectEventListener lock LockEventListener refresh RefreshEventListener replicate ReplicateEventListener save-update SaveOrUpdateEventListener pre-load PreLoadEventListener pre-update PreUpdateEventListener pre-delete PreDeleteEventListener pre-insert PreInsertEventListener post-load PostLoadEventListener post-update PostUpdateEventListener post-delete PostDeleteEventListener post-insert PostInsertEventListener
應用Hibernate事件監聽器
使用者制定的事件監聽器首先需要實現與所需要處理的事件對應的介面,或者繼承實現這個介面的類
通過使用Hibernate的配置檔案(hibernate.cfg.xml)配置事件監聽物件,或者使用Configuration物件註冊這個定製的事件監聽器物件
LogPostLoadEventListener
import org.hibernate.event.PostLoadEvent;
import org.hibernate.event.PostLoadEventListener;
public class LogPostLoadEventListener implements PostLoadEventListener {
private static final long serialVersionUID = 404241098418965422L;
public void onPostLoad(PostLoadEvent event) {
System.out.println("Class:" + event.getEntity().getClass().getName() + ",id:"
+ event.getId());
}
}
修改Hibernate.cfg.xml檔案
<mapping resource="com/rbh/examples/Guestbook.hbm.xml" />
<listener type="post-load" class="com.rbh.examples.LogPostLoadEventListener" />
</session-factory>
</hibernate-configuration>
或者通過Configuration 物件註冊這個監聽器物件
Configuration config = new Configuration();
config.setListener("post-load", new LogPostLoadEventListener());
config.configure();
Session session = config.buildSessionFactory().getCurrentSession();
編寫、配置好監聽器以後,當通過Session物件的load()、get()方法或者Query物件的list方法載入持久化物件之後,LogPostEventListener物件中的onPostLoad()方法就會被執行.
使用監聽器實現記錄操作日誌
利用Hibernate的事件機制,不僅能夠精確追蹤到持久化物件的欄位的修改,持久化物件關聯關係的變更,還能記錄更新前的數值和更新後的數值
監聽器與攔截器的比較
監聽器可以實現更細化粒度的攔截 通過監聽器獲取所攔截的持久化物件的修改後喝修改前的狀態值 能直接通過Event物件獲取Session物件