事務(Transaction)是併發控制的單位,是用戶定義的一個操做序列。這些操做要麼都作,要麼都不作,是一個不可分割的工做單位。java
數據庫向用戶提供保存當前程序狀態的方法,叫事務提交(commit);當事務執行過程當中,使數據庫忽略當前的狀態並回到前面保存的狀態的方法叫事務回滾(rollback)spring
原子性(atomicity):將事務中所作的操做捆綁成一個原子單元,即對於事務所進行的數據修改等操做,要麼所有執行,要麼所有不執行。數據庫
一致性(Consistency):事務在完成時,必須使全部的數據都保持一致狀態,並且在相關數據中,全部規則都必須應用於事務的修改,以保持全部數據的完整性。事務結束時,全部的內部數據結構都應該是正確的。express
隔離性(Isolation):由併發事務所作的修改必須與任何其餘事務所作的修改相隔離。事務查看數據時數據所處的狀態,要麼是被另外一併發事務修改以前的狀態,要麼是被另外一併發事務修改以後的狀態,即事務不會查看由另外一個併發事務正在修改的數據。這種隔離方式也叫可串行性。服務器
持久性(Durability):事務完成以後,它對系統的影響是永久的,即便出現系統故障也是如此。session
事務隔離意味着對於某一個正在運行的事務來講,好像系統中只有這一個事務,其餘併發的事務都不存在同樣。數據結構
大部分狀況下,不多使用徹底隔離的事務。但不徹底隔離的事務會帶來以下一些問題。併發
更新丟失(Lost Update):兩個事務都企圖去更新一行數據,致使事務拋出異常退出,兩個事務的更新都白費了。框架
髒數據(Dirty Read):若是第二個應用程序使用了第一個應用程序修改過的數據,而這個數據處於未提交狀態,這時就會發生髒讀。第一個應用程序隨後可能會請求回滾被修改的數據,從而致使第二個事務使用的數據被損壞,即所謂的「變髒」。dom
不可重讀(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_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配置文件中關於事務配置老是由三個組成部分,分別是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註解,以下:
問題:
一、當JPA框架對數據庫進行操做的時候,是從那裏獲取Connection?
二、jdbc對事務的配置,好比事務的開啓,提交以及回滾是在哪裏設置的?
三、Spring是經過aop攔截切面的全部須要進行事務管理的業務處理方法,那如何獲取業務處理方法裏面對數據庫操做的事務呢?
解答:
一、既然在JPA的框架裏面配置了datasource,那天然會從這個datasource裏面去得到鏈接。
二、jdbc的事務配置是在Connection對消裏面有對應的方法,好比setAutoCommit,commit,rollback這些方法就是對事務的操做。
三、Spring須要操做事務,那必需要對Connection來進行設置。Spring的AOP能夠攔截業務處理方法,而且也知道業務處理方法裏面的 DAO操做的JAP框架是從datasource裏面獲取Connection對象,那麼Spring須要對當前攔截的業務處理方法進行事務控制,那 必然 須要獲得他內部的Connection對象。總體的結構圖以下:
源碼分析:略