posts - 495,  comments - 11,  trackbacks - 0

          4.8 事 件 機 制

          通常,Hibernate執行持久化過程中,應用程序無法參與其中。所有的數據持久化操作,對用戶都是透明的,用戶無法插入自己的動作。

          通過事件框架,Hibernate允許應用程序能響應特定的內部事件,從而允許實現某些通用的功能,或對Hibernate功能進行擴展。

          Hibernate的事件框架由兩個部分組成:

          ?? ● 攔截器機制,對于特定動作攔截,回調應用中的特定動作。

          ?? ● 事件系統,重寫Hibernate的事件監聽器。

          4.8.1 攔截器

          通過Interceptor接口,可以從Session中回調應用程序的特定方法,這種回調機制可讓應用程序在持久化對象被保存、更新、刪除或加載之前,檢查并修改其屬性。

          通過Interceptor接口,可以在數據進入數據庫之間,對數據進行最后的檢查,如果數據不符合要求,可以修改數據,從而避免非法數據進入數據庫。當然,通常無須這樣做,只是在某些特殊的場合下,才考慮使用攔截器完成檢查功能。

          使用攔截器可按如下步驟進行:

          (1)定義實現Interceptor接口的攔截器類;

          (2)通過Session啟用攔截器,或者通過Configuration啟用全局攔截器。

          下面是一個攔截器的示例代碼,該攔截器沒有進行任何實際的操作,僅僅打印出標志代碼:

          public class MyInterceptor extends EmptyInterceptor

          {

          ??? //更新的次數

          ??? private int updates;

          ??? //插入的次數

          ??? private int creates;

          ??? //刪除數據時,將調用onDelete方法

          ??? public void onDelete(Object entity,Serializable id,Object[]

          ??? state,String[] propertyNames, Type[] types)

          ??? {

          ??????? //do nothing

          ??? }

          ??? //同步Session和數據庫中的數據

          ??? 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;

          ??????? }

          ??? //加載持久化實例時,調用該方法

          ??? 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;

          ??? }

          ??? //保存持久化實例時,調用該方法

          ??? 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("創建的次數: " + creates + ", 更新的次數: " +

          ??? updates);

          ??? }

          ??? public void preFlush(Iterator entities)

          ??? {

          ??????? updates=0;

          ??????? creates=0;

          ??? }

          ??? //事務提交前,觸發該方法

          ??? public void beforeTransactionCompletion(Transaction tx)

          ??? {

          ??????? System.out.println("事務即將結束");

          ??? }

          ??? //事務提交后,觸發該方法

          ??? public void afterTransactionCompletion(Transaction tx)

          ??? {

          ??????? System.out.println("事務已經結束");

          ??? }

          }

          在上面的攔截器實現類中,實現了很多方法,這些方法都是在Hibernate執行特定動作時自動調用。

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

          ?? ● 通過SessionFactory的openSession(Interceptor in)方法打開一個帶局部攔截器的Session。

          ?? ● 通過Configuration的setInterceptor(Interceptor in)方法設置全局攔截器。

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

          public class HibernateUtil

          {

          ??? //靜態類屬性 SessionFactory

          ??? public static final SessionFactory sessionFactory;

          ??? //靜態初始化塊,完成靜態屬性的初始化

          ??? static

          ??? {

          ??????? try

          ??????? {

          ??????????? //采用默認的hibernate.cfg.xml來啟動一個Configuration的實例

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

          ??????????? //由Configuration的實例來創建一個SessionFactory實例

          ??????????? sessionFactory = configuration.buildSessionFactory();

          ??????? }

          ??????? catch (Throwable ex)

          ??????? {

          ??????????? System.err.println("初始化sessionFactory失敗." + ex);

          ??????????? throw new ExceptionInInitializerError(ex);

          ??????? }

          ??? }

          ??? //ThreadLocal是隔離多個線程的數據共享,不存在多個線程之間共享資源,因此不再需要

          ??? 對線程同步???

          ??? public static final ThreadLocal session = new ThreadLocal();

          ??? //不加攔截器的打開Session方法

          ??? public static Session currentSession() throws HibernateException

          ??? {

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

          ??????? //如果該線程還沒有Session,則創建一個新的Session

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

          ??????? {

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

          ??????????? //將獲得的Session變量存儲在ThreadLocal變量的Session里

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

          ??????? }

          ??????? return s;

          ??? }

          ??? //加攔截器的打開Session方法

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

          ??? HibernateException

          ??? {

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

          ??????? //如果該線程還沒有Session,則創建一個新的Session

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

          ??????? {

          ??????????? //以攔截器創建Session對象

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

          ??????????? //將獲得的Session變量存儲在ThreadLocal變量的Session里

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

          ??????????? }

          ??????? return s;

          ??? }

          ??? //關閉Session對象

          ??? public static void closeSession() throws HibernateException

          ??? {

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

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

          ??? ??????? s.close();

          ??????? session.set(null);

          ??? }

          }

          上面的Hibernate工具類提供了兩個currentSession方法,分別用于不使用攔截器獲取Session對象和使用攔截器獲取Session對象。

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

          private void testUser()

          {

          ??? //以攔截器開始Session

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

          ??? //開始事務

          ??? Transaction tx = session.beginTransaction();

          ??? //執行下面的代碼時,可以看到系統回調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);

          ??? */

          ??? //執行下面的代碼時,可以看到系統回調onLoad等方法

          ??? Object o = session.load(User.class , new Integer(1));

          ??? System.out.println(o);

          ??? User u = (User)o;

          ??? System.out.println(u.getName());

          ??? //提交事務時,可以看到系統回調事務相關方法

          ??? tx.commit();

          ??? HibernateUtil.closeSession();

          }

          4.8.2 事件系統

          Hibernate 3的事件系統是功能更強大的事件框架,事件系統可以替代攔截器,也可以作為攔截器的補充來使用。

          基本上,Session接口的每個方法都有對應的事件。如LoadEvent和FlushEvent等。當Session調用某個方法時,Hibernate Session會生成對應的事件,并激活對應的事件監聽器。

          系統默認監聽器實現的處理過程,完成了所有的數據持久化操作,包括插入和修改等操作。如果用戶定義了自己的監聽器,則意味著用戶必須完成對象的持久化操作。

          例如,可以在系統中實現并注冊LoadEventListener監聽器,該監聽器負責處理所有調用Session的load()方法的請求。

          監聽器是單態模式對象,即所有同類型的事件處理共享同一個監聽器實例,因此監聽器不應該保存任何狀態,即不應該使用成員變量。

          使用事件系統可按如下步驟進行:

          (1)實現自己的事件監聽器類;

          (2)注冊自定義事件監聽器,代替系統默認的事件監聽器。

          實現用戶的自定義監聽器有如下3個方法:

          ?? ● 實現對應的監聽器接口,這是不可思議的,實現接口必須實現接口內的所有方法,關鍵是必須實現Hibernate對應的持久化操作,即數據庫訪問,這意味著程序員完全取代了Hibernate的底層操作。

          ?? ● 繼承事件適配器,可以選擇性地實現需要關注的方法,但依然試圖取代Hibernate完成數據庫的訪問,這也不太現實。

          ?? ● 繼承系統默認的事件監聽器,擴展特定方法。

          實際上,前兩種方法很少使用。因為Hibernate的持久化操作也是通過這些監聽器實現的,如果用戶取代了這些監聽器,則應該自己實現所有的持久化操作,這意味著用戶放棄了Hibernate的持久化操作,而改為自己完成Hibernate的核心操作。

          通常推薦采用第三種方法實現自己的事件監聽器。Hibernate默認的事件監聽器都被聲明成non-final,從而方便用戶繼承。

          下面是用戶自定義監聽器的示例:

          //自定義LoadListener,繼承默認的DefaultLoadEventListener實現類

          public class MyLoadListener extends DefaultLoadEventListener

          {

          ??? //在LoadEventListener接口僅僅定義了這個方法

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

          ??? loadType)throws HibernateException

          ??? {

          ??????? //先調用父類的onLoad方法,從而完成默認的持久化操作

          ??????? Object o = super.onLoad(event, loadType);

          ??????? //加入用戶的自定義處理

          ??????? System.out.println("自定義的load事件");

          ??????? System.out.println(event.getEntityClassName() + "==========" +

          ??????? event.getEntityId());

          ??????? return o;

          ??? }

          }

          下面還有一個MySaveListener,用于監聽SaveEvent事件:

          //自定義SavaListener,繼承默認的DefaultSaveEventListener實現類

          public class MySaveListener extends DefaultSaveEventListener

          {

          ??? //該方法完成實際的數據插入動作

          ??? protected Serializable performSaveOrUpdate(SaveOrUpdateEvent event)

          ??? {

          ??????? //先執行用戶自定義的操作

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

          ??????? //調用父類的默認持久化操作

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

          ??? }

          }

          注意:擴展用戶自定義監聽器時,別忘了在方法中調用父類的對應方法。

          注冊用戶自定義監聽器也有兩種方法:

          ?? ● 編程式,通過使用Configuration對象編程注冊。

          ?? ● 聲明式,在Hibernate的XML格式配置文件中進行聲明,使用Properties格式的配置文件將無法配置自定義監聽器。

          下面的示例代碼,通過編程方式使用自定義監聽器:

          public class HibernateUtil2

          {

          ??? //靜態類屬性 SessionFactory

          ??? public static final SessionFactory sessionFactory;

          ??? //靜態初始化塊,完成靜態屬性的初始化

          ??? static

          ??? {

          ??????? try

          ??????? {

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

          ??????????? //注冊loadEventListener監聽器

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

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

          ??????????? //注冊saveListener監聽器

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

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

          ??????????? //由Configuration實例來創建一個SessionFactory實例

          ??????????? sessionFactory = cfg.configure().buildSessionFactory();

          ??????? }

          ??????? catch (Throwable ex)

          ??????? {

          ??????????? System.err.println("初始化sessionFactory失敗." + ex);

          ??????????? throw new ExceptionInInitializerError(ex);

          ??????? }

          ??? }

          ??? //ThreadLocal是隔離多個線程的數據共享,不存在多個線程之間共享資源,因此不再需要

          ??? 對線程同步

          ??? public static final ThreadLocal session = new ThreadLocal();

          ??? //不加攔截器的打開Session方法

          ??? public static Session currentSession() throws HibernateException

          ??? {

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

          ??????? //如果該線程還沒有Session,則創建一個新的Session

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

          ??????? {

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

          ??? ??????? //將獲得的Session變量存儲在ThreadLocal變量的Session里

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

          ??????? }

          ??????? return s;

          ??? }

          ??? //關閉Session對象

          ??? public static void closeSession() throws HibernateException

          ??? {

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

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

          ??????????? s.close();

          ??????? session.set(null);

          ??? }

          }

          如果不想修改代碼,也可以在配置文件中使用事件監聽器,注冊事件監聽器的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>

          ??????? <!—設置數據庫驅動 -->

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

          ??????? </property>

          ??????? <!-- 數據庫服務的url -->

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

          ??????? </property>

          ??????? <!-- 數據庫服務的用戶名 -->

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

          ??????? <!-- 數據庫服務的密碼 -->

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

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

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

          ??????? <!-- 設置數據庫方言 -->

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

          ??????? </property>

          ??????? <!-- 顯示Hibernate生成的SQL語句 -->

          ??????? <property name="show_sql">true</property>

          ??????? <!-- 配置應用啟動時,是否啟動自動建表 -->

          ??????? <property name="hbm2ddl.auto">update</property>

          ??????? <!-- 列出所有的持久化映射文件 -->

          ??????? <mapping resource="User.hbm.xml"/>

          ??????? <!-- 注冊事件監聽器 -->

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

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

          ??? </session-factory>

          </hibernate-configuration>

          使用配置文件注冊事件監聽器雖然方便,但也有不利之處,通過配置文件注冊的監聽器不能共享實例。如果多個<listener/>元素中使用了相同的類,則每一個引用都將產生一個新的攔截器實例。如果需要在多個事件之間共享監聽器的實例,則必須使用編程方式注冊事件監聽器。

          注意:雖然監聽器類實現了特定監聽器的接口,在注冊的時候還要明確指出注冊的事件。這是因為一個類可能實現多個監聽器的接口,注冊時明確指定要監聽的事件,可以使得啟用或者禁用某個事件監聽的配置工作更簡單。

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

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 梧州市| 九台市| 成武县| 岳池县| 雷山县| 咸宁市| 左云县| 金堂县| 木兰县| 平顶山市| 镇安县| 三门县| 涡阳县| 上思县| 仁怀市| 乐至县| 闻喜县| 勃利县| 马鞍山市| 万荣县| 玉门市| 华宁县| 永昌县| 开阳县| 大冶市| 莱西市| 乌拉特中旗| 嘉峪关市| 定陶县| 洪洞县| 清水县| 洛阳市| 白山市| 固原市| 宜丰县| 临夏市| 浑源县| 磐安县| 达州市| 红原县| 乃东县|