現在spring的配置都喜歡用注解,但是在這之前,還是有必要復習下Spring在xml配置里配置事務
然后咱們再來說看@Transactional
一、如何開啟@Transactional支持
要使用@Transactional,spring的配置文件applicationContext.xml中還是要寫些東西的,那就看看最流行的兩個ORM框架,如何配置,spring所有的事務管理策略類都繼承自org.springframework.transaction.PlatformTransactionManager接口。
1.spring+mybatis 事務配置
1 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 定義事務管理器 --> 2 <property name="dataSource" ref="dataSource" /> 3 </bean> 4 <!--使用注釋事務 --> 5 <tx:annotation-driven transaction-manager="transactionManager" />
2.spring+hibernate 事務配置
1 <!-- 事務管理器配置,單數據源事務 --> 2 <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 3 <property name="sessionFactory" ref="sessionFactory" /> 4 </bean> 5 6 <!-- 使用annotation定義事務 --> 7 <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
二、如何使用@Transactional
1.@Transactional一般作用於類上,像類似struts2的action類上,springmvc的controller類上,或者webservice的路徑類上,等等涉及到數據庫操作的類上,另外可以在該類的具體方法上在覆蓋一個個性化的@Transactional。
2.@Transactional也可以作用於接口、接口方法上,但是 Spring 建議不要在接口或者接口方法上使用該注解,因為這只有在使用基於接口的代理時它才會生效。
3.@Transactional只對public方法有效,當然如果非要在 protected、private 或者默認可見性的方法上使用 @Transactional 注解,也不會拋出任何異常,只不過將被忽略。
4.子類繼承父類,如果父類配置了@Transactional,子類同樣適用事務配置
三、@Transactional屬性理解
屬性 | 類型 | 描述 |
---|---|---|
value | String | 可選的限定描述符,指定使用的事務管理器 |
propagation | enum: Propagation | 可選的事務傳播行為設置 |
isolation | enum: Isolation | 可選的事務隔離級別設置 |
readOnly | boolean | 讀寫或只讀事務,默認讀寫 |
timeout | int (in seconds granularity) | 事務超時時間設置 |
rollbackFor | Class對象數組,必須繼承自Throwable | 導致事務回滾的異常類數組 |
rollbackForClassName | 類名數組,必須繼承自Throwable | 導致事務回滾的異常類名字數組 |
noRollbackFor | Class對象數組,必須繼承自Throwable | 不會導致事務回滾的異常類數組 |
noRollbackForClassName | 類名數組,必須繼承自Throwable | 不會導致事務回滾的異常類名字數組 |
1.value
就是我們在xml里配置的那個事務管理器
2.propagation 事務傳播行為
所謂事務的傳播行為是指,如果在開始當前事務之前,一個事務上下文已經存在,此時有若干選項可以指定一個事務性方法的執行行為。在TransactionDefinition定義中包括了如下幾個表示傳播行為的常量:
- TransactionDefinition.PROPAGATION_REQUIRED:如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務。這是默認值。
- TransactionDefinition.PROPAGATION_REQUIRES_NEW:創建一個新的事務,如果當前存在事務,則把當前事務掛起。
- TransactionDefinition.PROPAGATION_SUPPORTS:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,如果當前存在事務,則把當前事務掛起。
- TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,如果當前存在事務,則拋出異常。
- TransactionDefinition.PROPAGATION_MANDATORY:如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。
- TransactionDefinition.PROPAGATION_NESTED:如果當前存在事務,則創建一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED。
3.isolation 事務隔離級別
隔離級別是指若干個並發的事務之間的隔離程度。TransactionDefinition 接口中定義了五個表示隔離級別的常量:
- TransactionDefinition.ISOLATION_DEFAULT:這是默認值,表示使用底層數據庫的默認隔離級別。對大部分數據庫而言,通常這值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED:該隔離級別表示一個事務可以讀取另一個事務修改但還沒有提交的數據。該級別不能防止臟讀,不可重復讀和幻讀,因此很少使用該隔離級別。比如PostgreSQL實際上並沒有此級別。
- TransactionDefinition.ISOLATION_READ_COMMITTED:該隔離級別表示一個事務只能讀取另一個事務已經提交的數據。該級別可以防止臟讀,這也是大多數情況下的推薦值。
- TransactionDefinition.ISOLATION_REPEATABLE_READ:該隔離級別表示一個事務在整個過程中可以多次重復執行某個查詢,並且每次返回的記錄都相同。該級別可以防止臟讀和不可重復讀。
- TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾,也就是說,該級別可以防止臟讀、不可重復讀以及幻讀。但是這將嚴重影響程序的性能。通常情況下也不會用到該級別。
4.timeout 事務超時
所謂事務超時,就是指一個事務所允許執行的最長時間,如果超過該時間限制但事務還沒有完成,則自動回滾事務。在 TransactionDefinition 中以 int 的值來表示超時時間,其單位是秒。
默認設置為底層事務系統的超時值,如果底層數據庫事務系統沒有設置超時值,那么就是none,沒有超時限制。
5.readOnly 事務只讀屬性
“只讀事務”並不是一個強制選項,它只是一個“暗示”,提示數據庫驅動程序和數據庫系統,這個事務並不包含更改數據的操作,那么JDBC驅動程序和數據庫就有可能根據這種情況對該事務進行一些特定的優化,比方說不安排相應的數據庫鎖,以減輕事務對數據庫的壓力,畢竟事務也是要消耗數據庫的資源的。
但是你非要在“只讀事務”里面修改數據,也並非不可以,只不過對於數據一致性的保護不像“讀寫事務”那樣保險而已。因此,“只讀事務”僅僅是一個性能優化的推薦配置而已,並非強制你要這樣做不可。
6. rollbackFor rollbackForClassName noRollbackFor noRollbackForClassName spring事務回滾規則
指示spring事務管理器回滾一個事務的推薦方法是在當前事務的上下文內拋出異常。spring事務管理器會捕捉任何未處理的異常(也就是如果主動try catch了某類異常,那么spring會忽略該異常,不會回滾),然后依據規則決定是否回滾拋出異常的事務。
默認配置下,spring只有在拋出的異常為運行時unchecked異常時才回滾該事務,也就是拋出的異常為RuntimeException的子類(Errors也會導致事務回滾),而拋出checked異常則不會導致事務回滾。可以明確的配置在拋出那些異常時回滾事務,包括checked異常。也可以明確定義那些異常拋出時不回滾事務。還可以編程性的通過setRollbackOnly()方法來指示一個事務必須回滾,在調用完setRollbackOnly()后你所能執行的唯一操作就是回滾。