由openSession、getCurrentSession和HibernateDaoSupport淺談Spring對事物的支持
??? Spring和Hibernate的集成的一個要點就是對事務的支持,openSession、getCurrentSession都是編程式事務(手動設置事務的提交、回滾)中重要的對象,HibernateDaoSupport則提供了更方便的聲明式事務支持。
??? Hibernate中最重要的就是Session對象的引入,它是對jdbc的深度封裝,包括對事務的處理,Session對象通過SessionFactory來管理,openSession和getCurrentSession是管理session的重要的方法。
??? openSession和getCurrentSession的根本區別在于有沒有綁定當前線程,所以,使用方法有差異:
* openSession沒有綁定當前線程,所以,使用完后必須關閉,
* currentSession和當前線程綁定,在事務結束后會自動關閉。
關于事務的邊界和傳播:
???? 通常情況下事務的邊界需要設置在業務邏輯處理層中,但是,如果在一個業務中涉及到多個業務邏輯層之間的方法,且需要在同一個事務中運行,那么,這就涉及到了事務的傳播性。
如果使用openSession,就要在dao層的方法中傳遞session,而這種做法是很糟糕的,首先增加了參數的個數,另外,方法是否需要事務,完全是可以當做一種獨立的服務抽離出的。
因為currentSession是線程級別的,所以,只要業務邏輯方法在同一個線程中,就不會擔心上面的問題。這也是currentSession的一個優越處之一。
使用currentSession:
1.在配置文件中將線程配置成Thread級別的。
?
- <SPAN?style="FONT-SIZE:?18px"><propertynamepropertyname="hibernate.current_session_context_class">thread</property></SPAN>??
<propertyname="hibernate.current_session_context_class">thread</property>
2.調用sessionFactory的getCurrentSession方法:
?
- <SPAN?style="FONT-SIZE:?18px">publicvoid?addUser(User?user)?{??
- ??
- ????Session?session?=?null;??
- ??
- ????try?{??
- ??
- ???????session?=HibernateUtils.getSessionFactory().getCurrentSession();??
- ??
- ???????session.beginTransaction();????????
- ??
- ???????session.save(user);??????????
- ??
- ???????Loglog?=?new?Log();??
- ??
- ???????log.setType("操作日志");??
- ??
- ???????log.setTime(new?Date());??
- ??
- ???????log.setDetail("XXX");????????
- ??
- ???????LogManager?logManager?=?newLogManagerImpl();??
- ??
- ???????logManager.addLog(log);?????????
- ??
- ???????session.getTransaction().commit();??
- ??
- ????}catch(Exception?e)?{??
- ??
- ???????e.printStackTrace();??
- ??
- ???????session.getTransaction().rollback();?????
- ??
- ????}??
- ??
- }</SPAN>??
publicvoid addUser(User user) {Session session = null;try {session =HibernateUtils.getSessionFactory().getCurrentSession();session.beginTransaction(); session.save(user); Loglog = new Log();log.setType("操作日志");log.setTime(new Date());log.setDetail("XXX"); LogManager logManager = newLogManagerImpl();logManager.addLog(log); session.getTransaction().commit();}catch(Exception e) {e.printStackTrace();session.getTransaction().rollback(); }}
?
使用openSession:
?
- <SPAN?style="FONT-SIZE:?18px">public?void?addUser(User?user)?{??
- ??
- ??????Sessionsession?=?null;??
- ??
- ??????try{??
- ??
- ?????????session=?HibernateUtils.getSession();??
- ??
- ?????????session.beginTransaction();??
- ??
- //?若干操作…………???????? ??
- ??
- ?????????session.getTransaction().commit();??
- ??
- ??????}catch(Exceptione)?{??
- ??
- ?????????e.printStackTrace();??
- ??
- ?????????session.getTransaction().rollback();??
- ??
- ??????}finally{??
- ??
- ?????????HibernateUtils.closeSession(session);??
- ??
- ??????}??
- ??
- ???}??
- ??
- ?</SPAN>??
public void addUser(User user) {Sessionsession = null;try{session= HibernateUtils.getSession();session.beginTransaction();// 若干操作………… session.getTransaction().commit();}catch(Exceptione) {e.printStackTrace();session.getTransaction().rollback();}finally{HibernateUtils.closeSession(session);}}
使用HibernateDaoSupport聲明式事務:
??? Spring與Hibernate的集成使用最多的是HibernateDaoSupport,它對session的獲取以及事務做了進一步的封裝,只需要關注dao的實現,而不用擔心某個地方的事務是否關閉。
?
- <SPAN?style="FONT-SIZE:?18px">this.getHibernateTemplate().save(user);</SPAN>??
this.getHibernateTemplate().save(user);
?
關于異常與事務回滾:???
??? Spring在遇到運行期異常(繼承了RuntimeException)的時候才會回滾,如果是Exception(如用戶輸入密碼錯誤)拋出就好,事務會繼續往下進行。
??? Spring對異常的處理的靈活性還是比較高的,可以配置遇到某個Exception進行回滾,某個RuntimeException不回滾,但是對于EJB就沒有這么靈活了,EJB相當于是固定的套餐。
不會回滾:??
?
- public?void?addUser(User?user)??
- ??
- ???throws?Exception?{??
- ??
- ??????this.getHibernateTemplate().save(user);??
- ??
- ?????????//若干操作……?????????? ??
- ??
- ??????throw?new?Exception();??
- ??
- ???}??
public void addUser(User user)throws Exception {this.getHibernateTemplate().save(user);//若干操作…… throw new Exception();}
? 回滾:
?
- public?void?addUser(User?user)?{??
- ??
- ???????this.getHibernateTemplate().save(user);??????
- ??
- ???????//若干操作……??????? ??
- ??
- ???????throw?new?RuntimeException();??
- ??
- ?}??
public void addUser(User user) {this.getHibernateTemplate().save(user); //若干操作…… throw new RuntimeException();}
?
?
Spring與Hibernate的集成,使用HibernateDaoSupport的配置:
?? 在ssh框架應用中,Spring與Hibernate的事務集成基本上是比較固定的,我們把事務的集成單獨配置到applicationContext-common.xml中:
?
- <SPAN?style="FONT-SIZE:?18px"><?xml?version="1.0"encoding="UTF-8"?>???
- ??
- <beansxmlnsbeansxmlns="http://www.springframework.org/schema/beans"??
- ??
- ???????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"??
- ??
- ???????xmlns:aop="http://www.springframework.org/schema/aop"??
- ??
- ???????xmlns:tx="http://www.springframework.org/schema/tx"??
- ??
- ????????xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.0.xsd??
- ??
- ??????????http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.0.xsd??
- ??
- ???????????http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.0.xsd">??
- ??
- ???<!--配置SessionFactory?-->??
- ??
- ???<beanidbeanid="sessionFactory"class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">??
- ??
- ??????<propertynamepropertyname="configLocation">??
- ??
- ?????????<value>classpath:hibernate.cfg.xml</value>??
- ??
- ??????</property>??
- ??
- ???</bean>??
- ????
- ??
- ???<!--配置事務管理器?-->??
- ??
- ???<beanidbeanid="transactionManager"class="org.springframework.orm.hibernate3.HibernateTransactionManager">??
- ??
- ??????<propertynamepropertyname="sessionFactory">??
- ??
- ?????????<refbeanrefbean="sessionFactory"/>??????????
- ??
- ??????</property>??
- ??
- ???</bean>??
- ????
- ??
- ???<!--那些類那些方法使用事務?-->??
- ??
- ???<aop:config>??
- ??
- ??????<aop:pointcutidaop:pointcutid="allManagerMethod"?expression="execution(*com.bjpowernode.usermgr.manager.*.*(..))"/>??
- ??
- ??????<aop:advisorpointcut-refaop:advisorpointcut-ref="allManagerMethod"?advice-ref="txAdvice"/>??
- ??
- ???</aop:config>??
- ????
- ??
- ???<!--事務的傳播特性?-->???
- ??
- ???<tx:adviceidtx:adviceid="txAdvice"?transaction-manager="transactionManager">??
- ??
- ??????<tx:attributes>??
- ??
- ?????????<tx:methodnametx:methodname="add*"?propagation="REQUIRED"/>??
- ??
- ?????????<tx:methodnametx:methodname="del*"?propagation="REQUIRED"/>??
- ??
- ?????????<tx:methodnametx:methodname="modify*"?propagation="REQUIRED"/>??
- ??
- ?????????<tx:methodnametx:methodname="*"?propagation="REQUIRED"read-only="true"/>??
- ??
- ??????</tx:attributes>??
- ??
- ???</tx:advice>??
- ??
- </beans></SPAN>??
<?xml version="1.0"encoding="UTF-8"?> <beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.0.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.0.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.0.xsd"><!--配置SessionFactory --><beanid="sessionFactory"class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"><propertyname="configLocation"><value>classpath:hibernate.cfg.xml</value></property></bean><!--配置事務管理器 --><beanid="transactionManager"class="org.springframework.orm.hibernate3.HibernateTransactionManager"><propertyname="sessionFactory"><refbean="sessionFactory"/> </property></bean><!--那些類那些方法使用事務 --><aop:config><aop:pointcutid="allManagerMethod" expression="execution(*com.bjpowernode.usermgr.manager.*.*(..))"/><aop:advisorpointcut-ref="allManagerMethod" advice-ref="txAdvice"/></aop:config><!--事務的傳播特性 --> <tx:adviceid="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:methodname="add*" propagation="REQUIRED"/><tx:methodname="del*" propagation="REQUIRED"/><tx:methodname="modify*" propagation="REQUIRED"/><tx:methodname="*" propagation="REQUIRED"read-only="true"/></tx:attributes></tx:advice></beans>
? ?
因為在hibernate.cfg.xml中添加了如下配置,所以,在tomcat等容器啟動的時候,會自動將相應的bean對象創建。
?
- <SPAN?style="FONT-SIZE:?18px">?<propertynamepropertyname="hibernate.hbm2ddl.auto">update</property></SPAN>??
<propertyname="hibernate.hbm2ddl.auto">update</property>
?
applicationContext-beans.xml:
??? 通常將業務邏輯對實現類的引用單獨的xml文件中,同時,在實現類中不能忽略sessionFactory工廠的注入。
?
- <SPAN?style="FONT-SIZE:?18px"><?xml?version="1.0"encoding="UTF-8"?>???
- ??
- <beansxmlnsbeansxmlns="http://www.springframework.org/schema/beans"??
- ??
- ???????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"??
- ??
- ????????xmlns:aop="http://www.springframework.org/schema/aop"??
- ??
- ???????xmlns:tx="http://www.springframework.org/schema/tx"??
- ??
- ???????xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.0.xsd??
- ??
- ???????????http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.0.xsd??
- ??
- ??????????http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.0.xsd">??
- ??
- ????????????
- ??
- ???<beanidbeanid="userManager"?class="com.bjpowernode.usermgr.manager.UserManagerImpl">??
- ??
- ??????<propertynamepropertyname="sessionFactory"?ref="sessionFactory"/>??
- ??
- ??????<propertynamepropertyname="logManager"?ref="logManager"/>??
- ??
- ???</bean>??
- ????
- ??
- ???<beanidbeanid="logManager"class="com.bjpowernode.usermgr.manager.LogManagerImpl">??
- ??
- ??????<propertynamepropertyname="sessionFactory"?ref="sessionFactory"/>??
- ??
- ???</bean>??
- ??
- </beans></SPAN>??
<?xml version="1.0"encoding="UTF-8"?> <beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.0.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.0.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.0.xsd"><beanid="userManager" class="com.bjpowernode.usermgr.manager.UserManagerImpl"><propertyname="sessionFactory" ref="sessionFactory"/><propertyname="logManager" ref="logManager"/></bean><beanid="logManager"class="com.bjpowernode.usermgr.manager.LogManagerImpl"><propertyname="sessionFactory" ref="sessionFactory"/></bean></beans>
事務傳播特性:
?? 為了保證調用的業務邏輯方法都使用同一個事務,通常都使用REQUIRED這個級別,它表示:如果上一個方法中有事務,就直接使用,如果沒有,就創建一個事務,這樣,一旦事務創建了后,后續調用的方法就不會再創建。
?? 其他的事務傳播特性見下表:
?
Spring事務的隔離級別:
?? 1. ISOLATION_DEFAULT: 這是一個PlatfromTransactionManager默認的隔離級別,使用數據庫默認的事務隔離級別。
?? ???? 另外四個與JDBC的隔離級別相對應。
?? 2. ISOLATION_READ_UNCOMMITTED: 這是事務最低的隔離級別,它充許令外一個事務可以看到這個事務未提交的數據。
?? ???? 這種隔離級別會產生臟讀,不可重復讀和幻像讀。
?? 3. ISOLATION_READ_COMMITTED: 保證一個事務修改的數據提交后才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的數據
?? 4. ISOLATION_REPEATABLE_READ: 這種事務隔離級別可以防止臟讀,不可重復讀。但是可能出現幻像讀。
?? ???? 它除了保證一個事務不能讀取另一個事務未提交的數據外,還保證了避免下面的情況產生(不可重復讀)。
?? 5. ISOLATION_SERIALIZABLE 這是花費最高代價但是最可靠的事務隔離級別。事務被處理為順序執行。
???? 除了防止臟讀,不可重復讀外,還避免了幻像讀。
??? 事務隔離級別主要應用在對大數據的處理方面,與鎖的機制是密不可分的,這里不贅述。