1. 程式人生 > >Hibernate攔截器與事件監聽

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物件