詳解Hibernate攔截器與事件監(jiān)聽器
攔截器(Intercept):與Struts2的攔截器機制基本一樣,都是一個操作穿過一層層攔截器,每穿過一個攔截器就會觸發(fā)相應(yīng)攔截器的事件做預(yù)處理或善后處理。
監(jiān)聽器(Listener):其實功能與攔截器是相似的,但它實現(xiàn)原理不同,它是為每一個事件注冊一個或多個監(jiān)聽器,一旦事件發(fā)生,則事件源通知所有監(jiān)聽該事件的監(jiān)聽器,然后監(jiān)聽器處理通知(觀察者模式)。
攔截器
Hibernate 為我們提供了實現(xiàn)攔截器的接口org.hibernate.Interceptor,它里面提供了許多攔截事件。通常不需要實現(xiàn)這個接口,因為我們實現(xiàn)自己的攔截器不可能每一個事件都是必須的。所以Hibernate為我們提供了org.hibernate.Interceptor接口的一個空實現(xiàn)類 org.hibernate.EmptyIntercept,通常情況下我們只需繼承這個空實現(xiàn)類,Override需要的事件方法即可。
攔截器的工作原理簡易示意圖:
設(shè)置攔截器后,相應(yīng)的操作都會先穿過一層層相應(yīng)的攔截器,讓攔截器執(zhí)行預(yù)處理或善后處理。
攔截器使用實例:
創(chuàng)建攔截器:
- public class AutoUpdateTimeInterceptor extends EmptyInterceptor
- {
- private static final long serialVersionUID = -8597658125309889388L;
- /*
- * entity - POJO對象
- * id - POJO對象的主鍵
- * state - POJO對象的每一個屬性所組成的集合(除了ID)
- * propertyNames - POJO對象的每一個屬性名字組成的集合(除了ID)
- * types - POJO對象的每一個屬性類型所對應(yīng)的Hibernate類型組成的集合(除了ID)
- */
- @Override
- public boolean onSave(Object entity, Serializable id, Object[] state,
- String[] propertyNames, Type[] types)
- {
- if (entity instanceof People)
- {
- for (int index=0;index<propertyNames.length;index++)
- {
- /*找到名為"checkinTime"的屬性*/
- if ("checkinTime".equals(propertyNames[index]))
- {
- /*使用攔截器將People對象的"checkinTime"屬性賦上值*/
- state[index] = new Timestamp(new Date().getTime());
- return true;
- }
- }
- }
- return false;
- }
- }
場景類
- public class Client
- {
- public static void main(String[] args)
- {
- /*為Session添加攔截器*/
- Session session = HibernateUtil.getSessionFactory().openSession(new AutoUpdateTimeInterceptor());
- Transaction tx = null;
- try
- {
- tx = session.beginTransaction();
- People people = new People();
- people.setName("zhangsan");
- session.save(people);
- tx.commit();
- }
- catch (Exception e)
- {
- if(tx!=null)
- {
- tx.rollback();
- }
- e.printStackTrace();
- }
- finally
- {
- session.close();
- }
- }
- }
場景類中并沒有顯示的設(shè)置了people對象的"checkinTime"的屬性值,啟動該場景類代碼,現(xiàn)在來查看數(shù)據(jù)庫信息:
可以看到checkin_time這列屬性依然被賦值了,說明該賦值操作是攔截器幫助我們完成的。使用攔截器的時候需要注意攔截器的返回值,我以前一直以為攔截器的返回值會控制一個操作是否可以繼續(xù),通過實驗發(fā)現(xiàn),即使返回false操作也會繼續(xù)執(zhí)行的,只是返回false的話,攔截器的所有設(shè)置都是無效的,不會反應(yīng)到數(shù)據(jù)庫中。
返回false:
- public class AutoUpdateTimeInterceptor extends EmptyInterceptor
- {
- private static final long serialVersionUID = -8597658125309889388L;
- /*
- * entity - POJO對象
- * id - POJO對象的主鍵
- * state - POJO對象的每一個屬性所組成的集合(除了ID)
- * propertyNames - POJO對象的每一個屬性名字組成的集合(除了ID)
- * types - POJO對象的每一個屬性類型所對應(yīng)的Hibernate類型組成的集合(除了ID)
- */
- @Override
- public boolean onSave(Object entity, Serializable id, Object[] state,
- String[] propertyNames, Type[] types)
- {
- if (entity instanceof People)
- {
- for (int index=0;index<propertyNames.length;index++)
- {
- /*找到名為"checkinTime"的屬性*/
- if ("checkinTime".equals(propertyNames[index]))
- {
- /*使用攔截器將People對象的"checkinTime"屬性賦上值*/
- state[index] = new Timestamp(new Date().getTime());
- // return true;
- }
- }
- }
- return false;
- }
- }
查看插入的數(shù)據(jù):
可以看到數(shù)據(jù)依然保存到數(shù)據(jù)庫中了,但攔截器的操作并沒有反映到數(shù)據(jù)庫中,攔截器的操作是無效的。
但是比較奇怪的是Console輸出的SQL語句:
- Hibernate:
- insert
- into
- people
- (name, checkin_time, id)
- values
- (?, ?, ?)
- Hibernate:
- update
- people
- set
- name=?,
- checkin_time=?
- where
- id=?
居然多了一條Update語句,我使用了p6spy顯示了這兩條SQL語句的綁定值:
- 1327304507491|0|0|statement|insert into people (name, checkin_time, id) values (?, ?, ?)|insert into people (name, checkin_time, id) values ('zhangsan', '2012-01-23 15:41:47.45', '402881e53509837f0135098380370001')
- 1327304507491|0|0|statement|update people set name=?, checkin_time=? where id=?|update people set name='zhangsan', checkin_time='' where id='402881e53509837f0135098380370001'
可以看到,攔截器的操作會直接反映到數(shù)據(jù)庫中的,但是如果返回值是false的話,Hibernate會產(chǎn)生一條Update SQL語句將攔截器的操作結(jié)果取消了。
最后,Hibernate的攔截器有兩種設(shè)置方式,一種是使用sessionFactory.openSession(Interceptor interceptor),這樣的攔截器只會針對該session有效,又叫做局部攔截器。另一種是使用Configuration的setInterceptor(Interceptor interceptor)方法設(shè)置,這樣的攔截器對每一個session都有效,又稱之為全局攔截器。
事件監(jiān)聽器
基本上,Session接口的每個方法都有相對應(yīng)的事件。比如 LoadEvent,F(xiàn)lushEvent,等等(查閱XML配置文件的DTD,以及org.hibernate.event包來獲得所有已定義的事件的列表)。當某個方法被調(diào)用時,Hibernate Session會生成一個相對應(yīng)的事件并激活所有配置好的事件監(jiān)聽器。系統(tǒng)預(yù)設(shè)的監(jiān)聽器實現(xiàn)的處理過程就是被監(jiān)聽的方法要做的(被監(jiān)聽的方法所做的其實僅僅是激活監(jiān)聽器, “實際”的工作是由監(jiān)聽器完成的)。不過,你可以自由地選擇實現(xiàn)一個自己定制的監(jiān)聽器(比如,實現(xiàn)并注冊用來處理處理LoadEvent的 LoadEventListener接口), 來負責(zé)處理所有的調(diào)用Session的load()方法的請求。
在定義自己的事件監(jiān)聽器時,其實不需要實現(xiàn)XXXListener接口,Hibernate為了方便我們定義事件監(jiān)聽器,已經(jīng)為每個事件監(jiān)聽器接口實提供了一個默認的實現(xiàn)。在org.hibernate.event.def包下面可以找到Hibernate為我們提供的默認實現(xiàn),我們只需要繼承這些默認實現(xiàn),在其基礎(chǔ)上添加我們自定義的功能即可。
事件監(jiān)聽器的簡單示意圖:
當某個方法被調(diào)用時,Hibernate Session會生成一個相對應(yīng)的事件并激活所有配置好的事件監(jiān)聽器。
事件監(jiān)聽器使用:
創(chuàng)建事件監(jiān)聽器:
- public class SaveOrUpdateListener extends DefaultSaveOrUpdateEventListener
- {
- private static final long serialVersionUID = 7496518453770213930L;
- /*監(jiān)聽保存或更新事件*/
- @Override
- public void onSaveOrUpdate(SaveOrUpdateEvent event)
- {
- People people = null;
- if(event.getObject() instanceof People)
- {
- people = (People)event.getObject();
- }
- people.setCheckinTime(new Timestamp(new Date().getTime()));
- System.out.println("invoke!");
- /*一定要調(diào)用父類提供的功能,不然就和繼承接口一樣了*/
- super.onSaveOrUpdate(event);
- }
- }
通過hibernate.cfg.xml配置文件將事件監(jiān)聽器配置到Hibernate中:
第一種配置方式:
- <event type="save-update">
- <listener class="com.suxiaolei.hibernate.listener.SaveOrUpdateListener"/>
- </event>
第二種配置方式:
- <listener class="com.suxiaolei.hibernate.listener.SaveOrUpdateListener" type="save-update"/>
兩種配置方式產(chǎn)生的效果都是一樣的。只是一個以"事件"為主,一個以"監(jiān)聽器"為主。type是指定監(jiān)聽事件的類型,class指定監(jiān)聽器的實現(xiàn)類,一個事件可以有多個監(jiān)聽器。type有許多取值,下表列出了所有type的值:
上面列表每一個選項對應(yīng)著一個特定的事件。
場景類:
- public class Client
- {
- public static void main(String[] args)
- {
- Session session = HibernateUtil.getSessionFactory().openSession();
- Transaction tx = null;
- try
- {
- tx = session.beginTransaction();
- People people = new People();
- people.setName("lisi");
- session.saveOrUpdate(people);
- tx.commit();
- }
- catch (Exception e)
- {
- if(tx!=null)
- {
- tx.rollback();
- }
- e.printStackTrace();
- }
- finally
- {
- session.close();
- }
- }
- }
people對象依然沒有設(shè)置checkinTime屬性,運行程序后,查看數(shù)據(jù)庫:
可以看到,checkin_time字段的依然賦值了,說明我們配置的事件監(jiān)聽器生效了。
使用事件監(jiān)聽器我發(fā)現(xiàn)需要注意父類行為的順序,例如:
- public void onSaveOrUpdate(SaveOrUpdateEvent event)
- {
- People people = null;
- if(event.getObject() instanceof People)
- {
- people = (People)event.getObject();
- }
- people.setCheckinTime(new Timestamp(new Date().getTime()));
- System.out.println("invoke!");
- /*一定要調(diào)用父類提供的功能,不然就和繼承接口一樣了*/
- super.onSaveOrUpdate(event);
- }
- public void onSaveOrUpdate(SaveOrUpdateEvent event)
- {
- /*一定要調(diào)用父類提供的功能,不然就和繼承接口一樣了*/
- super.onSaveOrUpdate(event);
- People people = null;
- if(event.getObject() instanceof People)
- {
- people = (People)event.getObject();
- }
- people.setCheckinTime(new Timestamp(new Date().getTime()));
- System.out.println("invoke!");
- }
例如上面的順序,雖然最后產(chǎn)生的效果一致,但是第二種順序會多產(chǎn)生一條update語句,有可能會產(chǎn)生性能問題,所以在使用事件監(jiān)聽器的時候需要多加注意。
原文鏈接:http://www.cnblogs.com/otomedaybreak/archive/2012/01/23/2328980.html
【編輯推薦】