事務(Transaction)是並發控制的單位,是用戶定義的一個操作序列。這些操作要么都做,要么都不做,是一個不可分割的工作單位。
數據庫向用戶提供保存當前程序狀態的方法,叫事務提交(commit);當事務執行過程中,使數據庫忽略當前的狀態並回到前面保存的狀態的方法叫事務回滾(rollback)
事務特性(ACID)
原子性(atomicity):將事務中所做的操作捆綁成一個原子單元,即對於事務所進行的數據修改等操作,要么全部執行,要么全部不執行。
一致性(Consistency):事務在完成時,必須使所有的數據都保持一致狀態,而且在相關數據中,所有規則都必須應用於事務的修改,以保持所有數據的完整性。事務結束時,所有的內部數據結構都應該是正確的。
隔離性(Isolation):由並發事務所做的修改必須與任何其他事務所做的修改相隔離。事務查看數據時數據所處的狀態,要么是被另一並發事務修改之前的狀態,要么是被另一並發事務修改之后的狀態,即事務不會查看由另一個並發事務正在修改的數據。這種隔離方式也叫可串行性。
持久性(Durability):事務完成之后,它對系統的影響是永久的,即使出現系統故障也是如此。
事務隔離(Isolation Level)
事務隔離意味着對於某一個正在運行的事務來說,好像系統中只有這一個事務,其他並發的事務都不存在一樣。
大部分情況下,很少使用完全隔離的事務。但不完全隔離的事務會帶來如下一些問題。
更新丟失(Lost Update):兩個事務都企圖去更新一行數據,導致事務拋出異常退出,兩個事務的更新都白費了。
臟數據(Dirty Read):如果第二個應用程序使用了第一個應用程序修改過的數據,而這個數據處於未提交狀態,這時就會發生臟讀。第一個應用程序隨后可能會請求回滾被修改的數據,從而導致第二個事務使用的數據被損壞,即所謂的“變臟”。
不可重讀(Unrepeatable Read):一個事務兩次讀同一行數據,可是這兩次讀到的數據不一樣,就叫不可重讀。如果一個事務在提交數據之前,另一個事務可以修改和刪除這些數據,就會發生不可重讀。
幻讀(Phantom Read):一個事務執行了兩次查詢,發現第二次查詢結果比第一次查詢多出了一行,這可能是因為另一個事務在這兩次查詢之間插入了新行。針對由事務的不完全隔離所引起的上述問題,提出了一些隔離級別,用來防范這些問題。
讀操作未提交(Read Uncommitted):讀取未提交的數據是允許的。說明一個事務在提交前,其變化對於其他事務來說是可見的。這樣臟讀、不可重讀和幻讀都是允許的。當一個事務已經寫入一行數據但未提交,其他事務都不能再寫入此行數據;但是,任何事務都可以讀任何數據。這個隔離級別使用排寫鎖實現。
讀操作已提交(Read Committed):讀取未提交的數據是不允許的,它使用臨時的共讀鎖和排寫鎖實現。這種隔離級別不允許臟讀,但不可重讀和幻讀是允許的。
可重讀(Repeatable Read):說明事務保證能夠再次讀取相同的數據而不會失敗。此隔離級別不允許臟讀和不可重讀,但幻讀會出現。
可串行化(Serializable):提供最嚴格的事務隔離。這個隔離級別不允許事務並行執行,只允許串行執行。這樣,臟讀、不可重讀或幻讀都可發生。
1. 1事務隔離與隔離級別的關系
| 隔離級別 |
臟讀(Dirty Read) |
不可重讀(Unrepeatable read) |
幻讀(Phantom Read) |
| 讀操作未提交(Read Uncommitted) |
可能 |
可能 |
可能 |
| 讀操作已提交(Read Committed) |
不可能 |
可能 |
可能 |
| 可重讀(Repeatable Read) |
不可能 |
不可能 |
可能 |
| 可串行化(Serializable) |
不可能 |
不可能 |
不可能 |
事務的傳播(Propagation)
| 事務傳播行為類型 |
說明 |
| PROPAGATION_REQUIRED |
如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。這是 最常見的選擇。 |
| PROPAGATION_SUPPORTS |
支持當前事務,如果當前沒有事務,就以非事務方式執行。 |
| PROPAGATION_MANDATORY |
使用當前的事務,如果當前沒有事務,就拋出異常。 |
| PROPAGATION_REQUIRES_NEW |
新建事務,如果當前存在事務,把當前事務掛起。 |
| PROPAGATION_NOT_SUPPORTED |
以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。 |
| PROPAGATION_NEVER |
以非事務方式執行,如果當前存在事務,則拋出異常。 |
| PROPAGATION_NESTED |
如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則執行與 PROPAGATION_REQUIRED 類似的操作。 |
當使用 PROPAGATION_NESTED 時, 底層的數據源必須基於 JDBC 3.0 ,並且實現者需要支持保存點事務機制。
readOnly
事務屬性中的readOnly標志表示對應的事務應該被最優化為只讀事務。這是一個最優化提示 。在一些情況下,一些事務策略能夠起到顯著的最優化效果,例如在使用Object/Relational映射工具 (如:Hibernate或TopLink)時避免dirty checking(試圖“刷新”)。
Timeout
在事務屬性中還有定義“timeout”值的選項,指定事務超時為幾秒。在JTA中,這將被簡單地傳遞到J2EE服務器的事務協調程序,並據此得到相應的解釋。
例子:
1 ServiceA { 2 3 void methodA() { 4 try { 5 //savepoint 6 ServiceB.methodB(); 7 } 8 catch (SomeException) { 9 // 執行其他業務, 如 ServiceC.methodC(); 10 } 11 } 12 13 }
1: PROPAGATION_REQUIRED
加入當前正要執行的事務不在另外一個事務里,那么就起一個新的事務
例如:
ServiceB.methodB的事務級別定義為PROPAGATION_REQUIRED
ServiceA.methodA已經起了事務,這時調用ServiceB.methodB,ServiceB.methodB就加入ServiceA.methodA的事務內部,就不再起新的事務。ServiceA.methodA沒有在事務中,這時調用ServiceB.methodB,
ServiceB.methodB就會為自己分配一個事務。
在ServiceA.methodA或者在ServiceB.methodB內的任何地方出現異常,事務都會被回滾。即使ServiceB.methodB的事務已經被提交,但是ServiceA.methodA在接下來fail要回滾,ServiceB.methodB也要回滾
2: PROPAGATION_SUPPORTS
如果當前在事務中,即以事務的形式運行,如果當前不再一個事務中,那么就以非事務的形式運行
3: PROPAGATION_MANDATORY
必須在一個事務中運行。也就是說,他只能被一個父事務調用。否則,他就要拋出異常
4: PROPAGATION_REQUIRES_NEW
例如:
ServiceA.methodA的事務級別為PROPAGATION_REQUIRED,ServiceB.methodB的事務級別為PROPAGATION_REQUIRES_NEW,
當調用ServiceB.methodB的時候,ServiceA.methodA所在的事務就會掛起,ServiceB.methodB會起一個新的事務,等待ServiceB.methodB的事務完成以后,他才繼續執行。
PROPAGATION_REQUIRES_NEW與PROPAGATION_REQUIRED 的事務區別在於事務的回滾程度:
因為ServiceB.methodB和ServiceA.methodA兩個不同的事務。如果ServiceB.methodB已經提交,那么ServiceA.methodA失敗回滾,ServiceB.methodB是不會回滾的。如果ServiceB.methodB失敗回滾,
如果他拋出的異常被ServiceA.methodA捕獲,ServiceA.methodA事務仍然可能提交。
5: PROPAGATION_NOT_SUPPORTED
當前不支持事務。
例如:
ServiceA.methodA的事務級別是PROPAGATION_REQUIRED ,而ServiceB.methodB的事務級別是PROPAGATION_NOT_SUPPORTED ,
調用ServiceB.methodB時,ServiceA.methodA的事務掛起,而以非事務的狀態運行完,再繼續ServiceA.methodA的事務。
6: PROPAGATION_NEVER
不能在事務中運行。
假設ServiceA.methodA的事務級別是PROPAGATION_REQUIRED, 而ServiceB.methodB的事務級別是PROPAGATION_NEVER ,
那么ServiceB.methodB就要拋出異常了。
7: PROPAGATION_NESTED
理解Nested的關鍵是savepoint。他與PROPAGATION_REQUIRES_NEW的區別是,PROPAGATION_REQUIRES_NEW另起一個事務,將會與他的父事務相互獨立,
而Nested的事務和他的父事務是相依的,他的提交是要等和他的父事務一塊提交的。也就是說,如果父事務最后回滾,他也要回滾的。
而Nested事務的好處是他有一個savepoint。
Spring事務處理
Spring配置文件中關於事務配置總是由三個組成部分,分別是DataSource、TransactionManager和代理機制這三部分,無論哪種配置方式,一般變化的只是代理機制這部分。
DataSource、TransactionManager這兩部分只是會根據數據訪問方式有所變化,比如使用Hibernate進行數據訪問 時,DataSource實際為SessionFactory,TransactionManager的實現為 HibernateTransactionManager。
具體如下圖:
第一種方式:每個Bean都有一個代理
1. <?xml version="1.0" encoding="UTF-8"?> 2. <beans xmlns="http://www.springframework.org/schema/beans" 3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4. xmlns:context="http://www.springframework.org/schema/context" 5. xmlns:aop="http://www.springframework.org/schema/aop" 6. xsi:schemaLocation="http://www.springframework.org/schema/beans 7. http://www.springframework.org/schema/beans/spring-beans-2.5.xsd 8. http://www.springframework.org/schema/context 9. http://www.springframework.org/schema/context/spring-context-2.5.xsd 10. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> 11. 12. <bean id="sessionFactory" 13. class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 14. <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 15. <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> 16. </bean> 17. 18. <!-- 定義事務管理器(聲明式的事務) --> 19. <bean id="transactionManager" 20. class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 21. <property name="sessionFactory" ref="sessionFactory" /> 22. </bean> 23. 24. <!-- 配置DAO --> 25. <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl"> 26. <property name="sessionFactory" ref="sessionFactory" /> 27. </bean> 28. 29. <bean id="userDao" 30. class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> 31. <!-- 配置事務管理器 --> 32. <property name="transactionManager" ref="transactionManager" /> 33. <property name="target" ref="userDaoTarget" /> 34. <property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" /> 35. <!-- 配置事務屬性 --> 36. <property name="transactionAttributes"> 37. <props> 38. <prop key="*">PROPAGATION_REQUIRED</prop> 39. </props> 40. </property> 41. </bean> 42. </beans>
第二種方式:所有Bean共享一個代理基類
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd 8 http://www.springframework.org/schema/context 9 http://www.springframework.org/schema/context/spring-context-2.5.xsd 10 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> 11 12 <bean id="sessionFactory" 13 class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 14 <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 15 <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> 16 </bean> 17 18 <!-- 定義事務管理器(聲明式的事務) --> 19 <bean id="transactionManager" 20 class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 21 <property name="sessionFactory" ref="sessionFactory" /> 22 </bean> 23 24 <bean id="transactionBase" 25 class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" 26 lazy-init="true" abstract="true"> 27 <!-- 配置事務管理器 --> 28 <property name="transactionManager" ref="transactionManager" /> 29 <!-- 配置事務屬性 --> 30 <property name="transactionAttributes"> 31 <props> 32 <prop key="*">PROPAGATION_REQUIRED</prop> 33 </props> 34 </property> 35 </bean> 36 37 <!-- 配置DAO --> 38 <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl"> 39 <property name="sessionFactory" ref="sessionFactory" /> 40 </bean> 41 42 <bean id="userDao" parent="transactionBase" > 43 <property name="target" ref="userDaoTarget" /> 44 </bean> 45 </beans>
第三種方式:使用攔截器
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd 8 http://www.springframework.org/schema/context 9 http://www.springframework.org/schema/context/spring-context-2.5.xsd 10 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> 11 12 <bean id="sessionFactory" 13 class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 14 <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 15 <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> 16 </bean> 17 18 <!-- 定義事務管理器(聲明式的事務) --> 19 <bean id="transactionManager" 20 class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 21 <property name="sessionFactory" ref="sessionFactory" /> 22 </bean> 23 24 <bean id="transactionInterceptor" 25 class="org.springframework.transaction.interceptor.TransactionInterceptor"> 26 <property name="transactionManager" ref="transactionManager" /> 27 <!-- 配置事務屬性 --> 28 <property name="transactionAttributes"> 29 <props> 30 <prop key="*">PROPAGATION_REQUIRED</prop> 31 </props> 32 </property> 33 </bean> 34 35 <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> 36 <property name="beanNames"> 37 <list> 38 <value>*Dao</value> 39 </list> 40 </property> 41 <property name="interceptorNames"> 42 <list> 43 <value>transactionInterceptor</value> 44 </list> 45 </property> 46 </bean> 47 48 <!-- 配置DAO --> 49 <bean id="userDao" class="com.bluesky.spring.dao.UserDaoImpl"> 50 <property name="sessionFactory" ref="sessionFactory" /> 51 </bean> 52 </beans>
第四種方式:使用tx標簽配置的攔截器
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xmlns:tx="http://www.springframework.org/schema/tx" 7 xsi:schemaLocation="http://www.springframework.org/schema/beans 8 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd 9 http://www.springframework.org/schema/context 10 http://www.springframework.org/schema/context/spring-context-2.5.xsd 11 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd 12 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> 13 14 <context:annotation-config /> 15 <context:component-scan base-package="com.bluesky" /> 16 17 <bean id="sessionFactory" 18 class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 19 <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 20 <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> 21 </bean> 22 23 <!-- 定義事務管理器(聲明式的事務) --> 24 <bean id="transactionManager" 25 class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 26 <property name="sessionFactory" ref="sessionFactory" /> 27 </bean> 28 29 <tx:advice id="txAdvice" transaction-manager="transactionManager"> 30 <tx:attributes> 31 <tx:method name="*" propagation="REQUIRED" /> 32 </tx:attributes> 33 </tx:advice> 34 35 <aop:config> 36 <aop:pointcut id="interceptorPointCuts" 37 expression="execution(* com.bluesky.spring.dao.*.*(..))" /> 38 <aop:advisor advice-ref="txAdvice" 39 pointcut-ref="interceptorPointCuts" /> 40 </aop:config> 41 </beans>
第五種方式:全注解
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xmlns:tx="http://www.springframework.org/schema/tx" 7 xsi:schemaLocation="http://www.springframework.org/schema/beans 8 http://www.springframework.org/schema/beans/spring-beans-2.5.xsd 9 http://www.springframework.org/schema/context 10 http://www.springframework.org/schema/context/spring-context-2.5.xsd 11 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd 12 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> 13 14 <context:annotation-config /> 15 <context:component-scan base-package="com.bluesky" /> 16 17 <tx:annotation-driven transaction-manager="transactionManager"/> 18 19 <bean id="sessionFactory" 20 class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 21 <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 22 <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> 23 </bean> 24 25 <!-- 定義事務管理器(聲明式的事務) --> 26 <bean id="transactionManager" 27 class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 28 <property name="sessionFactory" ref="sessionFactory" /> 29 </bean> 30 31 </beans>
此時在DAO上需加上@Transactional注解,如下:
import java.util.List;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Component;
import com.bluesky.spring.domain.User;
@Transactional
@Component("userDao")
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {
public List<User> listUsers() {
return this.getSession().createQuery("from User").list();
}

}
Spring事務處理原理
問題:
1、當JPA框架對數據庫進行操作的時候,是從那里獲取Connection?
2、jdbc對事務的配置,比如事務的開啟,提交以及回滾是在哪里設置的?
3、Spring是通過aop攔截切面的所有需要進行事務管理的業務處理方法,那如何獲取業務處理方法里面對數據庫操作的事務呢?
解答:
1、既然在JPA的框架里面配置了datasource,那自然會從這個datasource里面去獲得連接。
2、jdbc的事務配置是在Connection對消里面有對應的方法,比如setAutoCommit,commit,rollback這些方法就是對事務的操作。
3、Spring需要操作事務,那必須要對Connection來進行設置。Spring的AOP可以攔截業務處理方法,並且也知道業務處理方法里面的 DAO操作的JAP框架是從datasource里面獲取Connection對象,那么Spring需要對當前攔截的業務處理方法進行事務控制,那 必然 需要得到他內部的Connection對象。整體的結構圖如下:
源碼分析:略
