posts - 495,  comments - 11,  trackbacks - 0

          4.8 事 件 機(jī) 制

          通常,Hibernate執(zhí)行持久化過(guò)程中,應(yīng)用程序無(wú)法參與其中。所有的數(shù)據(jù)持久化操作,對(duì)用戶都是透明的,用戶無(wú)法插入自己的動(dòng)作。

          通過(guò)事件框架,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),重寫(xiě)Hibernate的事件監(jiān)聽(tīng)器。

          4.8.1 攔截器

          通過(guò)Interceptor接口,可以從Session中回調(diào)應(yīng)用程序的特定方法,這種回調(diào)機(jī)制可讓?xiě)?yīng)用程序在持久化對(duì)象被保存、更新、刪除或加載之前,檢查并修改其屬性。

          通過(guò)Interceptor接口,可以在數(shù)據(jù)進(jìn)入數(shù)據(jù)庫(kù)之間,對(duì)數(shù)據(jù)進(jìn)行最后的檢查,如果數(shù)據(jù)不符合要求,可以修改數(shù)據(jù),從而避免非法數(shù)據(jù)進(jìn)入數(shù)據(jù)庫(kù)。當(dāng)然,通常無(wú)須這樣做,只是在某些特殊的場(chǎng)合下,才考慮使用攔截器完成檢查功能。

          使用攔截器可按如下步驟進(jìn)行:

          (1)定義實(shí)現(xiàn)Interceptor接口的攔截器類(lèi);

          (2)通過(guò)Session啟用攔截器,或者通過(guò)Configuration啟用全局?jǐn)r截器。

          下面是一個(gè)攔截器的示例代碼,該攔截器沒(méi)有進(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ù)庫(kù)中的數(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)類(lèi)中,實(shí)現(xiàn)了很多方法,這些方法都是在Hibernate執(zhí)行特定動(dòng)作時(shí)自動(dòng)調(diào)用。

          完成了攔截器的定義,下面是關(guān)于攔截器的使用。攔截器的使用有兩種方法:

          ?? ● 通過(guò)SessionFactory的openSession(Interceptor in)方法打開(kāi)一個(gè)帶局部攔截器的Session。

          ?? ● 通過(guò)Configuration的setInterceptor(Interceptor in)方法設(shè)置全局?jǐn)r截器。

          下面是使用局部攔截器的示例代碼:

          public class HibernateUtil

          {

          ??? //靜態(tài)類(lèi)屬性 SessionFactory

          ??? public static final SessionFactory sessionFactory;

          ??? //靜態(tài)初始化塊,完成靜態(tài)屬性的初始化

          ??? static

          ??? {

          ??????? try

          ??????? {

          ??????????? //采用默認(rèn)的hibernate.cfg.xml來(lái)啟動(dòng)一個(gè)Configuration的實(shí)例

          ??????????? Configuration configuration=new Configuration().configure();

          ??????????? //由Configuration的實(shí)例來(lái)創(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();

          ??? //不加攔截器的打開(kāi)Session方法

          ??? public static Session currentSession() throws HibernateException

          ??? {

          ??????? Session s = (Session) session.get();

          ??????? //如果該線程還沒(méi)有Session,則創(chuàng)建一個(gè)新的Session

          ??????? if (s == null)

          ??????? {

          ??????????? s = sessionFactory.openSession();

          ??????????? //將獲得的Session變量存儲(chǔ)在ThreadLocal變量的Session里

          ??????????? session.set(s);

          ??????? }

          ??????? return s;

          ??? }

          ??? //加攔截器的打開(kāi)Session方法

          ??? public static Session currentSession(Interceptor it) throws

          ??? HibernateException

          ??? {

          ??????? Session s = (Session) session.get();

          ??????? //如果該線程還沒(méi)有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工具類(lèi)提供了兩個(gè)currentSession方法,分別用于不使用攔截器獲取Session對(duì)象和使用攔截器獲取Session對(duì)象。

          下面是主程序使用攔截器的代碼片段:

          private void testUser()

          {

          ??? //以攔截器開(kāi)始Session

          ??? Session session = HibernateUtil.currentSession(new MyInterceptor());

          ??? //開(kāi)始事務(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("中國(guó)");

          ??? 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ǔ)充來(lái)使用。

          基本上,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)器。

          系統(tǒng)默認(rèn)監(jiān)聽(tīng)器實(shí)現(xiàn)的處理過(guò)程,完成了所有的數(shù)據(jù)持久化操作,包括插入和修改等操作。如果用戶定義了自己的監(jiān)聽(tīng)器,則意味著用戶必須完成對(duì)象的持久化操作。

          例如,可以在系統(tǒng)中實(shí)現(xiàn)并注冊(cè)LoadEventListener監(jiān)聽(tīng)器,該監(jiān)聽(tīng)器負(fù)責(zé)處理所有調(diào)用Session的load()方法的請(qǐng)求。

          監(jiān)聽(tīng)器是單態(tài)模式對(duì)象,即所有同類(lèi)型的事件處理共享同一個(gè)監(jiān)聽(tīng)器實(shí)例,因此監(jiān)聽(tīng)器不應(yīng)該保存任何狀態(tài),即不應(yīng)該使用成員變量。

          使用事件系統(tǒng)可按如下步驟進(jìn)行:

          (1)實(shí)現(xiàn)自己的事件監(jiān)聽(tīng)器類(lèi);

          (2)注冊(cè)自定義事件監(jiān)聽(tīng)器,代替系統(tǒng)默認(rèn)的事件監(jiān)聽(tīng)器。

          實(shí)現(xiàn)用戶的自定義監(jiān)聽(tīng)器有如下3個(gè)方法:

          ?? ● 實(shí)現(xiàn)對(duì)應(yīng)的監(jiān)聽(tīng)器接口,這是不可思議的,實(shí)現(xiàn)接口必須實(shí)現(xiàn)接口內(nèi)的所有方法,關(guān)鍵是必須實(shí)現(xiàn)Hibernate對(duì)應(yīng)的持久化操作,即數(shù)據(jù)庫(kù)訪問(wèn),這意味著程序員完全取代了Hibernate的底層操作。

          ?? ● 繼承事件適配器,可以選擇性地實(shí)現(xiàn)需要關(guān)注的方法,但依然試圖取代Hibernate完成數(shù)據(jù)庫(kù)的訪問(wèn),這也不太現(xiàn)實(shí)。

          ?? ● 繼承系統(tǒng)默認(rèn)的事件監(jiān)聽(tīng)器,擴(kuò)展特定方法。

          實(shí)際上,前兩種方法很少使用。因?yàn)镠ibernate的持久化操作也是通過(guò)這些監(jiān)聽(tīng)器實(shí)現(xiàn)的,如果用戶取代了這些監(jiān)聽(tīng)器,則應(yīng)該自己實(shí)現(xiàn)所有的持久化操作,這意味著用戶放棄了Hibernate的持久化操作,而改為自己完成Hibernate的核心操作。

          通常推薦采用第三種方法實(shí)現(xiàn)自己的事件監(jiān)聽(tīng)器。Hibernate默認(rèn)的事件監(jiān)聽(tīng)器都被聲明成non-final,從而方便用戶繼承。

          下面是用戶自定義監(jiān)聽(tīng)器的示例:

          //自定義LoadListener,繼承默認(rèn)的DefaultLoadEventListener實(shí)現(xiàn)類(lèi)

          public class MyLoadListener extends DefaultLoadEventListener

          {

          ??? //在LoadEventListener接口僅僅定義了這個(gè)方法

          ??? public Object onLoad(LoadEvent event, LoadEventListener.LoadType

          ??? loadType)throws HibernateException

          ??? {

          ??????? //先調(diào)用父類(lèi)的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)聽(tīng)SaveEvent事件:

          //自定義SavaListener,繼承默認(rèn)的DefaultSaveEventListener實(shí)現(xiàn)類(lèi)

          public class MySaveListener extends DefaultSaveEventListener

          {

          ??? //該方法完成實(shí)際的數(shù)據(jù)插入動(dòng)作

          ??? protected Serializable performSaveOrUpdate(SaveOrUpdateEvent event)

          ??? {

          ??????? //先執(zhí)行用戶自定義的操作

          ??????? System.out.println(event.getObject());

          ??????? //調(diào)用父類(lèi)的默認(rèn)持久化操作

          ??????? return super.performSaveOrUpdate(event);

          ??? }

          }

          注意:擴(kuò)展用戶自定義監(jiān)聽(tīng)器時(shí),別忘了在方法中調(diào)用父類(lèi)的對(duì)應(yīng)方法。

          注冊(cè)用戶自定義監(jiān)聽(tīng)器也有兩種方法:

          ?? ● 編程式,通過(guò)使用Configuration對(duì)象編程注冊(cè)。

          ?? ● 聲明式,在Hibernate的XML格式配置文件中進(jìn)行聲明,使用Properties格式的配置文件將無(wú)法配置自定義監(jiān)聽(tīng)器。

          下面的示例代碼,通過(guò)編程方式使用自定義監(jiān)聽(tīng)器:

          public class HibernateUtil2

          {

          ??? //靜態(tài)類(lèi)屬性 SessionFactory

          ??? public static final SessionFactory sessionFactory;

          ??? //靜態(tài)初始化塊,完成靜態(tài)屬性的初始化

          ??? static

          ??? {

          ??????? try

          ??????? {

          ??????????? Configuration cfg = new Configuration();

          ??????????? //注冊(cè)loadEventListener監(jiān)聽(tīng)器

          ??????????? cfg.getSessionEventListenerConfig().setLoadEventListener

          ??????????? ( new MyLoadListener() );

          ??????????? //注冊(cè)saveListener監(jiān)聽(tīng)器

          ??????????? cfg.getSessionEventListenerConfig().setSaveEventListener

          ??????????? (new MySaveListener() );

          ??????????? //由Configuration實(shí)例來(lái)創(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();

          ??? //不加攔截器的打開(kāi)Session方法

          ??? public static Session currentSession() throws HibernateException

          ??? {

          ??????? Session s = (Session) session.get();

          ??????? //如果該線程還沒(méi)有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)聽(tīng)器,注冊(cè)事件監(jiān)聽(tīng)器的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ù)庫(kù)驅(qū)動(dòng) -->

          ??????? <property name="connection.driver_class">com.mysql.jdbc.Driver

          ??????? </property>

          ??????? <!-- 數(shù)據(jù)庫(kù)服務(wù)的url -->

          ??? ??? <property name="connection.url">jdbc:mysql://localhost/hibernate

          ??????? </property>

          ??????? <!-- 數(shù)據(jù)庫(kù)服務(wù)的用戶名 -->

          ??????? <property name="connection.username">root</property>

          ??????? <!-- 數(shù)據(jù)庫(kù)服務(wù)的密碼 -->

          ??????? <property name="connection.password">32147</property>

          ??????? <!-- JDBC connection pool (use the built-in) -->

          ??????? <property name="connection.pool_size">5</property>

          ??????? <!-- 設(shè)置數(shù)據(jù)庫(kù)方言 -->

          ??????? <property name="dialect">org.hibernate.dialect.MySQLDialect

          ??????? </property>

          ??????? <!-- 顯示Hibernate生成的SQL語(yǔ)句 -->

          ??????? <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)聽(tīng)器 -->

          ??? ??? <listener type="load" class="lee.MyLoadListener"/>

          ??? ??? <listener type="save" class="lee.MySaveListener"/>

          ??? </session-factory>

          </hibernate-configuration>

          使用配置文件注冊(cè)事件監(jiān)聽(tīng)器雖然方便,但也有不利之處,通過(guò)配置文件注冊(cè)的監(jiān)聽(tīng)器不能共享實(shí)例。如果多個(gè)<listener/>元素中使用了相同的類(lèi),則每一個(gè)引用都將產(chǎn)生一個(gè)新的攔截器實(shí)例。如果需要在多個(gè)事件之間共享監(jiān)聽(tīng)器的實(shí)例,則必須使用編程方式注冊(cè)事件監(jiān)聽(tīng)器。

          注意:雖然監(jiān)聽(tīng)器類(lèi)實(shí)現(xiàn)了特定監(jiān)聽(tīng)器的接口,在注冊(cè)的時(shí)候還要明確指出注冊(cè)的事件。這是因?yàn)橐粋€(gè)類(lèi)可能實(shí)現(xiàn)多個(gè)監(jiān)聽(tīng)器的接口,注冊(cè)時(shí)明確指定要監(jiān)聽(tīng)的事件,可以使得啟用或者禁用某個(gè)事件監(jiān)聽(tīng)的配置工作更簡(jiǎn)單。

          posted on 2009-07-19 09:42 jadmin 閱讀(88) 評(píng)論(0)  編輯  收藏

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 宜川县| 山西省| 昌平区| 惠州市| 江都市| 平罗县| 林州市| 海口市| 措勤县| 康保县| 土默特右旗| 互助| 定远县| 漠河县| 区。| 巴塘县| 华坪县| 渑池县| 台前县| 宁河县| 府谷县| 安福县| 体育| 鲁甸县| 饶河县| 巴南区| 集贤县| 深水埗区| 安义县| 海淀区| 崇明县| 蒙阴县| 揭东县| 加查县| 阿拉尔市| 新乐市| 辽宁省| 崇明县| 北宁市| 枞阳县| 江阴市|