1. 程式人生 > >Spring原始碼學習--Spring事物

Spring原始碼學習--Spring事物

Spring配置檔案中關於事務配置總是由三個組成部分,分別是DataSource、TransactionManager和代理機制這三部分,無論哪種配置方式,一般變化的只是代理機制這部分。DataSource、TransactionManager這兩部分只是會根據資料訪問方式有所變化,比如使用hibernate進行資料訪問時,DataSource實際為SessionFactory,TransactionManager的實現為HibernateTransactionManager。

  具體如下圖:

1、根據代理機制的不同,總結了五種Spring事務的配置方式

配置檔案如下:

    第一種方式:每個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. <bean id="sessionFactory"

  12. class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

  13. <property name="configLocation" value="classpath:hibernate.cfg.xml" />

  14. <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />

  15. </bean>

  16. <!-- 定義事務管理器(宣告式的事務) -->

  17. <bean id="transactionManager"

  18. class="org.springframework.orm.hibernate3.HibernateTransactionManager">

  19. <property name="sessionFactory" ref="sessionFactory" />

  20. </bean>

  21. <!-- 配置DAO -->

  22. <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">

  23. <property name="sessionFactory" ref="sessionFactory" />

  24. </bean>

  25. <bean id="userDao"

  26. class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">

  27. <!-- 配置事務管理器 -->

  28. <property name="transactionManager" ref="transactionManager" />

  29. <property name="target" ref="userDaoTarget" />

  30. <property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" />

  31. <!-- 配置事務屬性 -->

  32. <property name="transactionAttributes">

  33. <props>

  34. <prop key="*">PROPAGATION_REQUIRED</prop>

  35. </props>

  36. </property>

  37. </bean>

  38. </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. <bean id="sessionFactory"

  12. class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

  13. <property name="configLocation" value="classpath:hibernate.cfg.xml" />

  14. <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />

  15. </bean>

  16. <!-- 定義事務管理器(宣告式的事務) -->

  17. <bean id="transactionManager"

  18. class="org.springframework.orm.hibernate3.HibernateTransactionManager">

  19. <property name="sessionFactory" ref="sessionFactory" />

  20. </bean>

  21. <bean id="transactionBase"

  22. class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"

  23. lazy-init="true" abstract="true">

  24. <!-- 配置事務管理器 -->

  25. <property name="transactionManager" ref="transactionManager" />

  26. <!-- 配置事務屬性 -->

  27. <property name="transactionAttributes">

  28. <props>

  29. <prop key="*">PROPAGATION_REQUIRED</prop>

  30. </props>

  31. </property>

  32. </bean>

  33. <!-- 配置DAO -->

  34. <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">

  35. <property name="sessionFactory" ref="sessionFactory" />

  36. </bean>

  37. <bean id="userDao" parent="transactionBase" >

  38. <property name="target" ref="userDaoTarget" />

  39. </bean>

  40. </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. <bean id="sessionFactory"

  12. class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

  13. <property name="configLocation" value="classpath:hibernate.cfg.xml" />

  14. <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />

  15. </bean>

  16. <!-- 定義事務管理器(宣告式的事務) -->

  17. <bean id="transactionManager"

  18. class="org.springframework.orm.hibernate3.HibernateTransactionManager">

  19. <property name="sessionFactory" ref="sessionFactory" />

  20. </bean>

  21. <bean id="transactionInterceptor"

  22. class="org.springframework.transaction.interceptor.TransactionInterceptor">

  23. <property name="transactionManager" ref="transactionManager" />

  24. <!-- 配置事務屬性 -->

  25. <property name="transactionAttributes">

  26. <props>

  27. <prop key="*">PROPAGATION_REQUIRED</prop>

  28. </props>

  29. </property>

  30. </bean>

  31. <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">

  32. <property name="beanNames">

  33. <list>

  34. <value>*Dao</value>

  35. </list>

  36. </property>

  37. <property name="interceptorNames">

  38. <list>

  39. <value>transactionInterceptor</value>

  40. </list>

  41. </property>

  42. </bean>

  43. <!-- 配置DAO -->

  44. <bean id="userDao" class="com.bluesky.spring.dao.UserDaoImpl">

  45. <property name="sessionFactory" ref="sessionFactory" />

  46. </bean>

  47. </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. <context:annotation-config />

  14. <context:component-scan base-package="com.bluesky" />

  15. <bean id="sessionFactory"

  16. class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

  17. <property name="configLocation" value="classpath:hibernate.cfg.xml" />

  18. <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />

  19. </bean>

  20. <!-- 定義事務管理器(宣告式的事務) -->

  21. <bean id="transactionManager"

  22. class="org.springframework.orm.hibernate3.HibernateTransactionManager">

  23. <property name="sessionFactory" ref="sessionFactory" />

  24. </bean>

  25. <tx:advice id="txAdvice" transaction-manager="transactionManager">

  26. <tx:attributes>

  27. <tx:method name="*" propagation="REQUIRED" />

  28. </tx:attributes>

  29. </tx:advice>

  30. <aop:config>

  31. <aop:pointcut id="interceptorPointCuts"

  32. expression="execution(* com.bluesky.spring.dao.*.*(..))" />

  33. <aop:advisor advice-ref="txAdvice"

  34. pointcut-ref="interceptorPointCuts" />

  35. </aop:config>

  36. </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. <context:annotation-config />

  14. <context:component-scan base-package="com.bluesky" />

  15. <tx:annotation-driven transaction-manager="transactionManager"/>

  16. <bean id="sessionFactory"

  17. class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

  18. <property name="configLocation" value="classpath:hibernate.cfg.xml" />

  19. <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />

  20. </bean>

  21. <!-- 定義事務管理器(宣告式的事務) -->

  22. <bean id="transactionManager"

  23. class="org.springframework.orm.hibernate3.HibernateTransactionManager">

  24. <property name="sessionFactory" ref="sessionFactory" />

  25. </bean>

  26. </beans>

此時在DAO上需加上@Transactional註解,如下:

  1. @Transactional

  2. @Component("userDao")

  3. public class UserDaoImpl extends HibernateDaoSupport implements UserDao {

  4. public List<User> listUsers() {

  5. return this.getSession().createQuery("from User").list();

  6. }

  7. }

總結:以上5種方法是Spring配置事物常用到的配置方法。

2、spring裡面事務的傳播屬性和事務隔離級別

一、Propagation (事務的傳播屬性)
Propagation :  key屬性確定代理應該給哪個方法增加事務行為。這樣的屬性最重要的部份是傳播行為。有以下選項可供使用:  
PROPAGATION_REQUIRED--支援當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。
PROPAGATION_SUPPORTS--支援當前事務,如果當前沒有事務,就以非事務方式執行。
PROPAGATION_MANDATORY--支援當前事務,如果當前沒有事務,就丟擲異常。
PROPAGATION_REQUIRES_NEW--新建事務,如果當前存在事務,把當前事務掛起。
PROPAGATION_NOT_SUPPORTED--以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
PROPAGATION_NEVER--以非事務方式執行,如果當前存在事務,則丟擲異常。

1: PROPAGATION_REQUIRED
加入當前正要執行的事務不在另外一個事務裡,那麼就起一個新的事務
比如說,ServiceB.methodB的事務級別定義為PROPAGATION_REQUIRED, 那麼由於執行ServiceA.methodA的時候,
ServiceA.methodA已經起了事務,這時呼叫ServiceB.methodB,ServiceB.methodB看到自己已經執行在ServiceA.methodA的事務內部,就不再起新的事務。而假如ServiceA.methodA執行的時候發現自己沒有在事務中,他就會為自己分配一個事務。這樣,在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_REQUIRED 的事務區別在於事務的回滾程度了。因為ServiceB.methodB是新起一個事務,那麼就是存在兩個不同的事務。如果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的事務和他的父事務是相依的,他的提交是要等和他的父事務一塊提交的。也就是說,如果父事務最後回滾,他也要回滾的。

二、Isolation Level(事務隔離等級):

1、Serializable:最嚴格的級別,事務序列執行,資源消耗最大;

2、REPEATABLE READ:保證了一個事務不會修改已經由另一個事務讀取但未提交(回滾)的資料。避免了“髒讀取”和“不可重複讀取”的情況,但是帶來了更多的效能損失。

3、READ COMMITTED:大多數主流資料庫的預設事務等級,保證了一個事務不會讀到另一個並行事務已修改但未提交的資料,避免了“髒讀取”。該級別適用於大多數系統。

4、Read Uncommitted:保證了讀取過程中不會讀取到非法資料。隔離級別在於處理多事務的併發問題。
我們知道並行可以提高資料庫的吞吐量和效率,但是並不是所有的併發事務都可以併發執行,這需要檢視資料庫教材的可序列化條件判斷了。這裡就不闡述。

我們首先說併發中可能發生的3中不討人喜歡的事情


1: Dirty reads--讀髒資料。也就是說,比如事務A的未提交(還依然快取)的資料被事務B讀走,如果事務A失敗回滾,會導致事務B所讀取的的資料是錯誤的。

2: non-repeatable reads--資料不可重複讀。比如事務A中兩處讀取資料-total-的值。在第一讀的時候,total是100,然後事務B就把total的資料改成 200,事務A再讀一次,結果就發現,total竟然就變成200了,造成事務A資料混亂。

3: phantom reads--幻象讀資料,這個和non-repeatable reads相似,也是同一個事務中多次讀不一致的問題。但是non-repeatable reads的不一致是因為他所要取的資料集被改變了(比如total的資料),但是phantom reads所要讀的資料的不一致卻不是他所要讀的資料集改變,而是他的條件資料集改變。比如Select account.id where account.name="ppgogo*",第一次讀去了6個符合條件的id,第二次讀取的時候,由於事務b把一個帳號的名字由"dd"改成"ppgogo1",結果取出來了7個數據。


三、readOnly

事務屬性中的readOnly標誌表示對應的事務應該被最優化為只讀事務。
這是一個最優化提示。在一些情況下,一些事務策略能夠起到顯著的最優化效果,例如在使用Object/Relational對映工具(如:Hibernate或TopLink)時避免dirty checking(試圖“重新整理”)。


四、Timeout
在事務屬性中還有定義“timeout”值的選項,指定事務超時為幾秒。在JTA中,這將被簡單地傳遞到J2EE伺服器的事務協調程式,並據此得到相應的解釋