由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級別的。
<propertyname="hibernate.current_session_context_class">thread</property>
2.呼叫sessionFactory的getCurrentSession方法:
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:
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的實現,而不用擔心某個地方的事務是否關閉。
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) {
this.getHibernateTemplate().save(user);
//若干操作……
throw new RuntimeException();
}
Spring與Hibernate的整合,使用HibernateDaoSupport的配置:
在ssh框架應用中,Spring與Hibernate的事務整合基本上是比較固定的,我們把事務的整合單獨配置到applicationContext-common.xml中:
<?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.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 -->
<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物件建立。
<propertyname="hibernate.hbm2ddl.auto">update</property>
applicationContext-beans.xml:
通常將業務邏輯對實現類的引用單獨的xml檔案中,同時,在實現類中不能忽略sessionFactory工廠的注入。
<?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.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">
<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 這是花費最高代價但是最可靠的事務隔離級別。事務被處理為順序執行。
除了防止髒讀,不可重複讀外,還避免了幻像讀。
事務隔離級別主要應用在對大資料的處理方面,與鎖的機制是密不可分的,這裡不贅述。