1. 事務概述
事務(Transaction)是訪問並可能更新數據庫中數據的程序執行單元,通常事務開始(begin transaction)和事務結束(end transaction)之間執行的全體操作組成。例如我們平時的轉賬操作,從開始執行轉賬,到收款用戶確認收款,最終金額增加,這就是一個事務。
爲什麼我們要加入事務管理?藉助以上的轉賬實例來進行說明,假設A向B賬戶轉賬100元,那麼最基本的兩個操作就是A賬戶的金額減少100元,然後B賬戶的金額增加100元,存在一定的先後順序。倘若在A完成轉賬之後,A的金額減少了100元,但是信息傳輸途中發生了某些異常,導致B沒有接受到轉賬信息,那麼B的金額沒有增加。最終的結果是A的金額減少了100元,而B的維持不變。這樣的場景不符合我們日常生活的實際,因此我們引入事務管理,一旦在事務發生中斷,事務立即回滾到開始之前的狀態,也就是A減少的那100元金額會退還到A的賬戶。
事務作爲一個不可分割的邏輯工作單元,一般具備ACID四大特性:
1) 原子性(一個事務中的多個操作要麼都成功要麼都失敗)
2) 一致性(一個事務必須讓數據庫從一個一致性狀態到另一個一致性狀態,簡單地舉例就是轉賬前後兩個人的總金額應該不變)
3) 隔離性(事務與事務之間應該互不干擾)
4) 持久性(事務一旦提交,它對數據庫的改變是永久性的)
2. Spring事務管理
Spring爲我們提供了兩種事務管理方式。一種是通過編碼實現的編程式事務,另一種是基於Spring AOP的聲明式事務。聲明式事務管理通過切面實現將具體業務邏輯與事務處理解耦,使業務代碼邏輯不受污染, 因此在實際使用中聲明式事務用的比較多。
Spring中聲明式事務處理有兩種方式,一種是在配置文件(xml)中做相關的事務規則聲明,另一種是基於@Transactional 註解的方式。
2.1註解模式的事務管理
首先,我們在spring容器中配置一個事物管理的bean對象,DataSourceTransactionManager對象,這裏也有兩種方式,一種是在配置類中配置(全註解模式),另一種是在spring配置文件中配置(半註解模式)。
配置類中配置事物管理類,首先要開啓spring註解驅動的事務管理,可以通過在類名上方添加@ EnableTransactionManagement註解的方式實現,然後在這個類中配置事務管理對象,這裏的DataSource要與數據庫連接的數據源保持一致:
@Bean("transactionManager")
public DataSourceTransactionManager getDataSourceTransactionManager(
DataSource ds){
DataSourceTransactionManager transactionManager=
new DataSourceTransactionManager();
transactionManager.setDataSource(ds);
return transactionManager;
}
Xml配置文件中配置管理類並開啓事物管理模式:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--設置註解驅動的事務管理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
配置好事務管理註解以後,就可以在類或者方法上使用@Transactional進行事務管理。當把@Transactional註解加在類上時,表示類中的所有public修飾的方法全都配置相同的事物管理信息。當@Transactional修飾方法時,表示方法對方法進行事物管理。這裏需要注意兩點:一是@Transactional只能用來修飾public權限的方法,其餘的不起作用;二是如果類和類裏的方法都是用了@Transactional註解,方法上的事無屬性優先級別較高,會覆蓋類上的相關設置。
@Transactional註解後表示啓動spring默認的事務管理模式,也可以添加一些屬性來對方法採取不同的事務管理模式,下面總結了一些常用的屬性:
Value:當在配置文件中有多個 TransactionManager , 可以用該屬性指定選擇哪個事務管理器。
Propagation:事務的傳播行爲,默認值爲 REQUIRED。
Isolation:事務的隔離度,默認值採用 DEFAULT。
Timeout:事務的超時時間,默認值爲-1。如果超過該時間限制但事務還沒有完成,則自動回滾事務。
read-only :指定事務是否爲只讀事務,默認值爲 false;爲了忽略那些不需要事務的方法,比如讀取數據,可以設置read-only 爲 true。
rollback-for:用於指定能夠觸發事務回滾的異常類型,如果有多個異常類型需要指定,各類型之間可以通過逗號分隔。
no-rollback- for:拋出 no-rollback-for 指定的異常類型,不回滾事務。
2.2spring配置文件中的事務管理
配置文件中配置事物管理可以分爲三步。
1)在spring配置文件中配置事務管理器:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
2)定義事物策略,這裏是對所有的public方法進行相關事務管理設置:
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
也可以針對某一方法進行特殊設置:
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"
propagation="REQUIRED"
isolation="READ_COMMITTED"
timeout="-1"
read-only="false"
rollback-for="java.lang.Throwable"
no-rollback-for=”NoTransactionException”/>
</tx:attributes>
</tx:advice>
3)配置事物切面,這裏設置的一種細粒度的事物管理,對service中的所有方法進行管理,還有一種粗粒度的事務管理,expression=”within("com.jt.manage.service..*")”管理的是service包中的所有類。
<aop:config>
<aop:pointcut expression="execution(* com.jt.manage.service..*.*(..))" id=pointCut"/>
<aop:advisor advice-ref="txAdvice" ref="pointCut"/>
</aop:config>
3.spring事物拓展
3.1spring事物傳播特性
事務傳播特性(propagation):事務方法之間相互調用時,事務的傳播方式。
@Transactional(propagation=Propagation.REQUIRED) 如果沒有事務創建新事務, 如果當前有事務參與當前事務,這是事物管理的默認方式,也是需要重點掌握的。
@Transactional(propagation=Propagation.SUPPORTS)支持事務, 如果沒有事務也不會創建新事務,也是比較常用的,一般用在查詢方法中。
@Transactional(propagation=Propagation.REQUIRES_NEW)必須是新事務, 如果有當前事務, 掛起當前事務並且開啓新事務。
@Transactional(propagation=Propagation.MANDATORY)必須有事務, 如果當前沒有事務就拋異常。
@Transactional(propagation=Propagation.NEVER)絕對不能有事務, 如果在事務中調用則拋出異常。
@Transactional(propagation=Propagation.NESTED)必須被嵌套到其他事務中。
@Transactional(propagation=Propagation.NOT_SUPPORTED)不支持事務。
3.2事物隔離級別
總所周知,併發訪問時程序需要解決的一個重要問題,當多個事物併發執行時,可能會引發髒讀、不可重複讀和幻讀等問題。髒讀是一個事務中讀取到另一個事務沒有提交的數據。不可重複讀是在一個事務中兩次查詢的結果不一樣(針對update操作)。幻讀是在一個事務中兩次查詢的結果不一樣(針對insert操作)。
當多個事務併發執行時,可通過設置事務的隔離級別保證事務的完整性,一致性。
事務的隔離級別從低到高有如下幾種方式:
1)READ_UNCOMMITTED (此級別可能會出現髒讀)
2)READ_COMMITTED(此級別可能會出現不可重複讀)
3)REPEATABLE_READ(此級別可能會出現幻讀)
4)SERIALIZABLE(多事務串行執行)
spring中一般採用@Transactional(isolation=Isolation.READ_COMMITTED) 方式聲明級別, 這種方式是併發性能和安全性折中的選擇,是大多數軟件項目採用的隔離級別。