4.8 事 件 機(jī) 制
通常,Hibernate執(zhí)行持久化過程中,應(yīng)用程序無法參與其中。所有的數(shù)據(jù)持久化操作,對(duì)用戶都是透明的,用戶無法插入自己的動(dòng)作。
通過事件框架,Hibernate允許應(yīng)用程序能響應(yīng)特定的內(nèi)部事件,從而允許實(shí)現(xiàn)某些通用的功能,或?qū)ibernate功能進(jìn)行擴(kuò)展。
Hibernate的事件框架由兩個(gè)部分組成:
?? ● 攔截器機(jī)制,對(duì)于特定動(dòng)作攔截,回調(diào)應(yīng)用中的特定動(dòng)作。
?? ● 事件系統(tǒng),重寫Hibernate的事件監(jiān)聽器。
4.8.1 攔截器
通過Interceptor接口,可以從Session中回調(diào)應(yīng)用程序的特定方法,這種回調(diào)機(jī)制可讓應(yīng)用程序在持久化對(duì)象被保存、更新、刪除或加載之前,檢查并修改其屬性。
通過Interceptor接口,可以在數(shù)據(jù)進(jìn)入數(shù)據(jù)庫之間,對(duì)數(shù)據(jù)進(jìn)行最后的檢查,如果數(shù)據(jù)不符合要求,可以修改數(shù)據(jù),從而避免非法數(shù)據(jù)進(jìn)入數(shù)據(jù)庫。當(dāng)然,通常無須這樣做,只是在某些特殊的場(chǎng)合下,才考慮使用攔截器完成檢查功能。
使用攔截器可按如下步驟進(jìn)行:
(1)定義實(shí)現(xiàn)Interceptor接口的攔截器類;
(2)通過Session啟用攔截器,或者通過Configuration啟用全局?jǐn)r截器。
下面是一個(gè)攔截器的示例代碼,該攔截器沒有進(jìn)行任何實(shí)際的操作,僅僅打印出標(biāo)志代碼:
public class MyInterceptor extends EmptyInterceptor
{
??? //更新的次數(shù)
??? private int updates;
??? //插入的次數(shù)
??? private int creates;
??? //刪除數(shù)據(jù)時(shí),將調(diào)用onDelete方法
??? public void onDelete(Object entity,Serializable id,Object[]
??? state,String[] propertyNames, Type[] types)
??? {
??????? //do nothing
??? }
??? //同步Session和數(shù)據(jù)庫中的數(shù)據(jù)
??? public boolean onFlushDirty(Object entity, Serializable id, Object[]
??? currentState, Object[] previousState, String[] propertyNames, Type[]
??????????????????????? ??? types)
??? {
??????? //每同步一次,修改的累加器加1
??????? updates++;
??????? for ( int i=0; i < propertyNames.length; i++ )
??????? {
??????????? if ( "lastUpdateTimestamp".equals( propertyNames[i] ) )
??????????? {
??????????????? currentState[i] = new Date();
??????????????? return true;
??????????? }
??????? }
??????? return false;
??????? }
??? //加載持久化實(shí)例時(shí),調(diào)用該方法
??? public boolean onLoad(Object entity,Serializable id,Object[]
??? state,String[] propertyNames,Type[] types)
??? {
??????? System.out.println("========================");
??????? for ( int i=0; i < propertyNames.length; i++ )
??????? {
??????????? if ( "name".equals( propertyNames[i] ) )
??????????? {
??????????????? System.out.println(state[i]);
??????????????? state[i] = "aaa";
??????????????? return true;
??????????? }
??????? }
??????? return false;
??? }
??? //保存持久化實(shí)例時(shí),調(diào)用該方法
??? public boolean onSave(Object entity,Serializable id,Object[]
??? state,String[] propertyNames,Type[] types)
??? {
??????? creates++;
??????? for ( int i=0; i<propertyNames.length; i++ )
??????? {
??????????? if ( "createTimestamp".equals( propertyNames[i] ) )
??????????? {
??????????????? state[i] = new Date();
??? ??????????? return true;
??????????? }
??????? }
??????? return false;
??? }
??? //提交刷新
??? public void postFlush(Iterator entities)
??? {
??????? System.out.println("創(chuàng)建的次數(shù): " + creates + ", 更新的次數(shù): " +
??? updates);
??? }
??? public void preFlush(Iterator entities)
??? {
??????? updates=0;
??????? creates=0;
??? }
??? //事務(wù)提交前,觸發(fā)該方法
??? public void beforeTransactionCompletion(Transaction tx)
??? {
??????? System.out.println("事務(wù)即將結(jié)束");
??? }
??? //事務(wù)提交后,觸發(fā)該方法
??? public void afterTransactionCompletion(Transaction tx)
??? {
??????? System.out.println("事務(wù)已經(jīng)結(jié)束");
??? }
}
在上面的攔截器實(shí)現(xiàn)類中,實(shí)現(xiàn)了很多方法,這些方法都是在Hibernate執(zhí)行特定動(dòng)作時(shí)自動(dòng)調(diào)用。
完成了攔截器的定義,下面是關(guān)于攔截器的使用。攔截器的使用有兩種方法:
?? ● 通過SessionFactory的openSession(Interceptor in)方法打開一個(gè)帶局部攔截器的Session。
?? ● 通過Configuration的setInterceptor(Interceptor in)方法設(shè)置全局?jǐn)r截器。
下面是使用局部攔截器的示例代碼:
public class HibernateUtil
{
??? //靜態(tài)類屬性 SessionFactory
??? public static final SessionFactory sessionFactory;
??? //靜態(tài)初始化塊,完成靜態(tài)屬性的初始化
??? static
??? {
??????? try
??????? {
??????????? //采用默認(rèn)的hibernate.cfg.xml來啟動(dòng)一個(gè)Configuration的實(shí)例
??????????? Configuration configuration=new Configuration().configure();
??????????? //由Configuration的實(shí)例來創(chuàng)建一個(gè)SessionFactory實(shí)例
??????????? sessionFactory = configuration.buildSessionFactory();
??????? }
??????? catch (Throwable ex)
??????? {
??????????? System.err.println("初始化sessionFactory失敗." + ex);
??????????? throw new ExceptionInInitializerError(ex);
??????? }
??? }
??? //ThreadLocal是隔離多個(gè)線程的數(shù)據(jù)共享,不存在多個(gè)線程之間共享資源,因此不再需要
??? 對(duì)線程同步???
??? public static final ThreadLocal session = new ThreadLocal();
??? //不加攔截器的打開Session方法
??? public static Session currentSession() throws HibernateException
??? {
??????? Session s = (Session) session.get();
??????? //如果該線程還沒有Session,則創(chuàng)建一個(gè)新的Session
??????? if (s == null)
??????? {
??????????? s = sessionFactory.openSession();
??????????? //將獲得的Session變量存儲(chǔ)在ThreadLocal變量的Session里
??????????? session.set(s);
??????? }
??????? return s;
??? }
??? //加攔截器的打開Session方法
??? public static Session currentSession(Interceptor it) throws
??? HibernateException
??? {
??????? Session s = (Session) session.get();
??????? //如果該線程還沒有Session,則創(chuàng)建一個(gè)新的Session
??????? if (s == null)
??????? {
??????????? //以攔截器創(chuàng)建Session對(duì)象
??????? ??? s = sessionFactory.openSession(it);
??????????? //將獲得的Session變量存儲(chǔ)在ThreadLocal變量的Session里
??????????? session.set(s);
??????????? }
??????? return s;
??? }
??? //關(guān)閉Session對(duì)象
??? public static void closeSession() throws HibernateException
??? {
??????? Session s = (Session) session.get();
??????? if (s != null)
??? ??????? s.close();
??????? session.set(null);
??? }
}
上面的Hibernate工具類提供了兩個(gè)currentSession方法,分別用于不使用攔截器獲取Session對(duì)象和使用攔截器獲取Session對(duì)象。
下面是主程序使用攔截器的代碼片段:
private void testUser()
{
??? //以攔截器開始Session
??? Session session = HibernateUtil.currentSession(new MyInterceptor());
??? //開始事務(wù)
??? Transaction tx = session.beginTransaction();
??? //執(zhí)行下面的代碼時(shí),可以看到系統(tǒng)回調(diào)onSave等方法
??? /*
??? User u = new User();
??? u.setName("Yeeku Lee");
??? u.setAge(28);
??? u.setNationality("中國");
??? session.persist(u);
??? u.setAge(29);
??? u.setAge(30);
??? session.persist(u);
??? */
??? //執(zhí)行下面的代碼時(shí),可以看到系統(tǒng)回調(diào)onLoad等方法
??? Object o = session.load(User.class , new Integer(1));
??? System.out.println(o);
??? User u = (User)o;
??? System.out.println(u.getName());
??? //提交事務(wù)時(shí),可以看到系統(tǒng)回調(diào)事務(wù)相關(guān)方法
??? tx.commit();
??? HibernateUtil.closeSession();
}
4.8.2 事件系統(tǒng)
Hibernate 3的事件系統(tǒng)是功能更強(qiáng)大的事件框架,事件系統(tǒng)可以替代攔截器,也可以作為攔截器的補(bǔ)充來使用。
基本上,Session接口的每個(gè)方法都有對(duì)應(yīng)的事件。如LoadEvent和FlushEvent等。當(dāng)Session調(diào)用某個(gè)方法時(shí),Hibernate Session會(huì)生成對(duì)應(yīng)的事件,并激活對(duì)應(yīng)的事件監(jiān)聽器。
系統(tǒng)默認(rèn)監(jiān)聽器實(shí)現(xiàn)的處理過程,完成了所有的數(shù)據(jù)持久化操作,包括插入和修改等操作。如果用戶定義了自己的監(jiān)聽器,則意味著用戶必須完成對(duì)象的持久化操作。
例如,可以在系統(tǒng)中實(shí)現(xiàn)并注冊(cè)LoadEventListener監(jiān)聽器,該監(jiān)聽器負(fù)責(zé)處理所有調(diào)用Session的load()方法的請(qǐng)求。
監(jiān)聽器是單態(tài)模式對(duì)象,即所有同類型的事件處理共享同一個(gè)監(jiān)聽器實(shí)例,因此監(jiān)聽器不應(yīng)該保存任何狀態(tài),即不應(yīng)該使用成員變量。
使用事件系統(tǒng)可按如下步驟進(jìn)行:
(1)實(shí)現(xiàn)自己的事件監(jiān)聽器類;
(2)注冊(cè)自定義事件監(jiān)聽器,代替系統(tǒng)默認(rèn)的事件監(jiān)聽器。
實(shí)現(xiàn)用戶的自定義監(jiān)聽器有如下3個(gè)方法:
?? ● 實(shí)現(xiàn)對(duì)應(yīng)的監(jiān)聽器接口,這是不可思議的,實(shí)現(xiàn)接口必須實(shí)現(xiàn)接口內(nèi)的所有方法,關(guān)鍵是必須實(shí)現(xiàn)Hibernate對(duì)應(yīng)的持久化操作,即數(shù)據(jù)庫訪問,這意味著程序員完全取代了Hibernate的底層操作。
?? ● 繼承事件適配器,可以選擇性地實(shí)現(xiàn)需要關(guān)注的方法,但依然試圖取代Hibernate完成數(shù)據(jù)庫的訪問,這也不太現(xiàn)實(shí)。
?? ● 繼承系統(tǒng)默認(rèn)的事件監(jiān)聽器,擴(kuò)展特定方法。
實(shí)際上,前兩種方法很少使用。因?yàn)镠ibernate的持久化操作也是通過這些監(jiān)聽器實(shí)現(xiàn)的,如果用戶取代了這些監(jiān)聽器,則應(yīng)該自己實(shí)現(xiàn)所有的持久化操作,這意味著用戶放棄了Hibernate的持久化操作,而改為自己完成Hibernate的核心操作。
通常推薦采用第三種方法實(shí)現(xiàn)自己的事件監(jiān)聽器。Hibernate默認(rèn)的事件監(jiān)聽器都被聲明成non-final,從而方便用戶繼承。
下面是用戶自定義監(jiān)聽器的示例:
//自定義LoadListener,繼承默認(rèn)的DefaultLoadEventListener實(shí)現(xiàn)類
public class MyLoadListener extends DefaultLoadEventListener
{
??? //在LoadEventListener接口僅僅定義了這個(gè)方法
??? public Object onLoad(LoadEvent event, LoadEventListener.LoadType
??? loadType)throws HibernateException
??? {
??????? //先調(diào)用父類的onLoad方法,從而完成默認(rèn)的持久化操作
??????? Object o = super.onLoad(event, loadType);
??????? //加入用戶的自定義處理
??????? System.out.println("自定義的load事件");
??????? System.out.println(event.getEntityClassName() + "==========" +
??????? event.getEntityId());
??????? return o;
??? }
}
下面還有一個(gè)MySaveListener,用于監(jiān)聽SaveEvent事件:
//自定義SavaListener,繼承默認(rèn)的DefaultSaveEventListener實(shí)現(xiàn)類
public class MySaveListener extends DefaultSaveEventListener
{
??? //該方法完成實(shí)際的數(shù)據(jù)插入動(dòng)作
??? protected Serializable performSaveOrUpdate(SaveOrUpdateEvent event)
??? {
??????? //先執(zhí)行用戶自定義的操作
??????? System.out.println(event.getObject());
??????? //調(diào)用父類的默認(rèn)持久化操作
??????? return super.performSaveOrUpdate(event);
??? }
}
注意:擴(kuò)展用戶自定義監(jiān)聽器時(shí),別忘了在方法中調(diào)用父類的對(duì)應(yīng)方法。
注冊(cè)用戶自定義監(jiān)聽器也有兩種方法:
?? ● 編程式,通過使用Configuration對(duì)象編程注冊(cè)。
?? ● 聲明式,在Hibernate的XML格式配置文件中進(jìn)行聲明,使用Properties格式的配置文件將無法配置自定義監(jiān)聽器。
下面的示例代碼,通過編程方式使用自定義監(jiān)聽器:
public class HibernateUtil2
{
??? //靜態(tài)類屬性 SessionFactory
??? public static final SessionFactory sessionFactory;
??? //靜態(tài)初始化塊,完成靜態(tài)屬性的初始化
??? static
??? {
??????? try
??????? {
??????????? Configuration cfg = new Configuration();
??????????? //注冊(cè)loadEventListener監(jiān)聽器
??????????? cfg.getSessionEventListenerConfig().setLoadEventListener
??????????? ( new MyLoadListener() );
??????????? //注冊(cè)saveListener監(jiān)聽器
??????????? cfg.getSessionEventListenerConfig().setSaveEventListener
??????????? (new MySaveListener() );
??????????? //由Configuration實(shí)例來創(chuàng)建一個(gè)SessionFactory實(shí)例
??????????? sessionFactory = cfg.configure().buildSessionFactory();
??????? }
??????? catch (Throwable ex)
??????? {
??????????? System.err.println("初始化sessionFactory失敗." + ex);
??????????? throw new ExceptionInInitializerError(ex);
??????? }
??? }
??? //ThreadLocal是隔離多個(gè)線程的數(shù)據(jù)共享,不存在多個(gè)線程之間共享資源,因此不再需要
??? 對(duì)線程同步
??? public static final ThreadLocal session = new ThreadLocal();
??? //不加攔截器的打開Session方法
??? public static Session currentSession() throws HibernateException
??? {
??????? Session s = (Session) session.get();
??????? //如果該線程還沒有Session,則創(chuàng)建一個(gè)新的Session
??????? if (s == null)
??????? {
??????????? s = sessionFactory.openSession();
??? ??????? //將獲得的Session變量存儲(chǔ)在ThreadLocal變量的Session里
??????????? session.set(s);
??????? }
??????? return s;
??? }
??? //關(guān)閉Session對(duì)象
??? public static void closeSession() throws HibernateException
??? {
??????? Session s = (Session) session.get();
??????? if (s != null)
??????????? s.close();
??????? session.set(null);
??? }
}
如果不想修改代碼,也可以在配置文件中使用事件監(jiān)聽器,注冊(cè)事件監(jiān)聽器的Hibernate配置文件代碼如下:
<?xml version='1.0' encoding='GBK'?>
<!-- Hibernate配置文件的文件頭,包含DTD等信息 -->
<!DOCTYPE hibernate-configuration PUBLIC
??????? "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
??????? "http://hibernate.sourceforge.net/hibernate-configuration-3.0.
??????? dtd">
<!-- Hibernate配置文件的根元素 -->
<hibernate-configuration>
??? <session-factory>
??????? <!—設(shè)置數(shù)據(jù)庫驅(qū)動(dòng) -->
??????? <property name="connection.driver_class">com.mysql.jdbc.Driver
??????? </property>
??????? <!-- 數(shù)據(jù)庫服務(wù)的url -->
??? ??? <property name="connection.url">jdbc:mysql://localhost/hibernate
??????? </property>
??????? <!-- 數(shù)據(jù)庫服務(wù)的用戶名 -->
??????? <property name="connection.username">root</property>
??????? <!-- 數(shù)據(jù)庫服務(wù)的密碼 -->
??????? <property name="connection.password">32147</property>
??????? <!-- JDBC connection pool (use the built-in) -->
??????? <property name="connection.pool_size">5</property>
??????? <!-- 設(shè)置數(shù)據(jù)庫方言 -->
??????? <property name="dialect">org.hibernate.dialect.MySQLDialect
??????? </property>
??????? <!-- 顯示Hibernate生成的SQL語句 -->
??????? <property name="show_sql">true</property>
??????? <!-- 配置應(yīng)用啟動(dòng)時(shí),是否啟動(dòng)自動(dòng)建表 -->
??????? <property name="hbm2ddl.auto">update</property>
??????? <!-- 列出所有的持久化映射文件 -->
??????? <mapping resource="User.hbm.xml"/>
??????? <!-- 注冊(cè)事件監(jiān)聽器 -->
??? ??? <listener type="load" class="lee.MyLoadListener"/>
??? ??? <listener type="save" class="lee.MySaveListener"/>
??? </session-factory>
</hibernate-configuration>
使用配置文件注冊(cè)事件監(jiān)聽器雖然方便,但也有不利之處,通過配置文件注冊(cè)的監(jiān)聽器不能共享實(shí)例。如果多個(gè)<listener/>元素中使用了相同的類,則每一個(gè)引用都將產(chǎn)生一個(gè)新的攔截器實(shí)例。如果需要在多個(gè)事件之間共享監(jiān)聽器的實(shí)例,則必須使用編程方式注冊(cè)事件監(jiān)聽器。
注意:雖然監(jiān)聽器類實(shí)現(xiàn)了特定監(jiān)聽器的接口,在注冊(cè)的時(shí)候還要明確指出注冊(cè)的事件。這是因?yàn)橐粋€(gè)類可能實(shí)現(xiàn)多個(gè)監(jiān)聽器的接口,注冊(cè)時(shí)明確指定要監(jiān)聽的事件,可以使得啟用或者禁用某個(gè)事件監(jiān)聽的配置工作更簡單。
posted on 2009-07-19 09:42
jadmin 閱讀(86)
評(píng)論(0) 編輯 收藏