spring 事物之一----基本概論
Spring 事物基本概念
什麼是事物
事務是邏輯上的一組操作,組成這組操作的各個邏輯單元,要麼一起成功,要麼一起失敗。
簡單舉例,假設有使用者A,B的賬戶各有餘額100元,A給B轉100元,轉錢的結果:第一種結果轉賬成功,A賬戶的餘額為0元,B賬戶的餘額為200元;第二種結果轉賬失敗,A賬戶的餘額為100元,B賬戶的餘額為100元;事物就是用來保證在轉賬的過程中不會出現A賬戶的餘額為100元,B賬戶的餘額為200元。或是A賬戶的餘額為0元,B賬戶的餘額為100元。
事務特性(ACID)
原子性 (Atomicity) :事務的不可分割性
A給B轉賬100元,A-100成功,B+100成功;A-100失敗,B+100失敗;兩個操作必須保持一致,不可再分。
一致性 (Consistency):事務的執行的前後資料的完整性保持一致
A,B賬戶各100元,A給B轉賬100元,不管成功或者失敗,A,B賬戶合計都為200元;此為強一致性; 題外話(最終一致性,有興趣的話可以看看分散式事物),可以在某個時間視窗賬戶共計不為200元,但是最終結果為200元。
隔離性 (Isolation) :一個事務執行的過程中,不應該受到其他事務的干擾
A給B轉賬100元過程中,叫做事物1,有個操作查詢A餘額,叫做事物2,事物2查詢A餘額的只會0元或者100元,也就是說只能查詢到事物1執行事物之前或者之後的狀態,不會查詢到事情1執行過程中的中間狀態。
永續性 (Durability) :事務一旦結束,資料就持久到資料庫
A,B賬戶各100元,A給B轉賬100元,A-100成功,B+100成功;A的賬戶變成0元,B的賬戶變成200元,最終會在資料庫做資料持久化。
事物隔離級別
如果不考慮事物隔離性,多個事物在執行情況下將會出現如下安全性問題:
1.髒讀 : 一個事務讀到了另一個事務的未提交的資料 。
事物1做update操作未提交,事物2做select操作,此時如果事物1回滾,那麼事物2就讀取了事物1操作後的結果。導致髒讀。
2.不可重複讀 : 一個事務讀到了另一個事務已經提交的 update 的資料導致多次查詢結果不一致.。
事物1第一次做select操作,此時事物2做update操作並提交,事物1第二次做select操作,發現和第一次查詢的結果不一樣。
3.虛幻讀 : 一個事務讀到了另一個事務已經提交的 insert 的資料導致多次查詢結果不一致。
事物1做update操作將賬戶的餘額全部改為0,並進行第一次select操作,發現餘額都為0,此時事物2插入一條餘額為100元的記錄,事物1做第二次操作,發現賬戶還有大於0的資料,有點虛幻的感覺,明明已經全部餘額改為0了。
**那麼問題來了,如何來解決上述情況呢,其實很簡單我們通過設定事務隔離級別**
事務隔離級別(5種)
DEFAULT
預設的隔離級別,使用資料庫預設的事務隔離級別.
未提交讀(Read Uncommitted)
最低的隔離級別,如果一個事務已開始寫資料,則另外一個事務不允許同時進行寫操作,但允許其他事務讀此行資料。髒讀,不可重複讀,虛讀都有可能發生 。
已提交讀(Read Committed)
讀取資料的事務允許其他事務繼續訪問該行資料,但是未提交的寫事務將會禁止其他事務訪問該行,避免髒讀。但不可重複讀和虛讀有可能發生。
可重複讀(Repeatable Read)
讀取資料的事務將會禁止寫事務(但允許讀事務),寫事務則禁止任何其他事務,相當於程式select…for update動作,可避免髒讀和不可重複讀,但虛讀有可能發生。
序列化讀(Serializable)
事務序列執行,不能併發執行。相當於每次執行之前先鎖全表,雖可避免以上所有讀問題,但效能低下。
Mysql 預設:可重複讀
Oracle 預設:讀已提交
事務的傳播行為
PROPAGATION_REQUIRED 支援當前事務,如果不存在 就新建一個(預設)
PROPAGATION_REQUIRES_NEW 如果有事務存在,掛起當前事務,建立一個新的事務
PROPAGATION_NESTED 如果當前事務存在,則巢狀事務執行
PROPAGATION_SUPPORTS 支援當前事務,如果不存在,就不使用事務
PROPAGATION_NOT_SUPPORTED 以非事務方式執行,如果有事務存在,掛起當前事務
PROPAGATION_MANDATORY 支援當前事務,如果不存在,丟擲異常
PROPAGATION_NEVER 以非事務方式執行,如果有事務存在,丟擲異常
事務的管理方式
spring支援程式設計式事務管理和宣告式事務管理兩種方式。
1.程式設計式事務管理使用TransactionTemplate或者直接使用底層PlatformTransactionManager。
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(jdbcTemplate.getDataSource());
TransactionStatus status = transactionManager.getTransaction(def);
try {
String insertSql = this.getInsertSql(newItems);
List<Object[]> batchArgs = this.getInsertValueList(newItems);
jdbcTemplate.batchUpdate(insertSql, batchArgs);
} catch (Exception e) {
// 事務回滾,一條條執行
transactionManager.rollback(status);
throw e;
}
transactionManager.commit(status);
2.宣告式事務管理也有兩種常用的方:
一種是基於tx和aop名字空間的xml配置檔案
<!-- 1.宣告式事務,基於tx和aop名字空間的xml配置檔案 -->
<bean id="web_transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:advice id="web_txAdvice" transaction-manager="web_transactionManager">
<tx:attributes>
<tx:method name="delete*" propagation="REQUIRED" read-only="false" rollback-for="BusinessException.class"/>
<tx:method name="insert*" propagation="REQUIRED" read-only="false" rollback-for="BusinessException.class" />
<tx:method name="update*" propagation="REQUIRED" read-only="false" rollback-for="BusinessException.class"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="query*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="interceptorPointCuts"
expression="execution(* com.kook.web.base.dao.*.*(..))" />
<aop:advisor advice-ref="web_txAdvice"
pointcut-ref="interceptorPointCuts" />
</aop:config>
一種就是基於@Transactional註解。
<!--2.宣告式事務,基於@Transactional註解-->
<tx:annotation-driven transaction-manager="txManager" />
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
@Transactional註解
@Transactional屬性
屬性 | 型別 | 描述 |
---|---|---|
value | String | 指定使用的事務管理器,可選的 |
propagation | enum: Propagation | 可選的事務傳播行為設定(7種) |
isolation | enum: Isolation | 可選的事務隔離級別設定(5種) |
readOnly | boolean | 讀寫或只讀事務,預設讀寫 |
timeout | int | 事務超時時間設定(單位秒) |
rollbackFor | Class物件陣列,繼承自Throwable | 導致事務回滾的異常類陣列 |
rollbackForClassName | 類名陣列,繼承自Throwable | 導致事務回滾的異常類名字陣列 |
noRollbackFor | Class物件陣列,繼承自Throwable | 不會導致事務回滾的異常類陣列 |
noRollbackForClassName | 類名陣列,必須繼承自Throwable | 不會導致事務回滾的異常類名字陣列 |
@Transactional用法
[email protected] 可以用於介面、介面方法、類以及類方法上,一般情況不要在介面和介面方法上使用該註解
[email protected] 註解只能被應用到 public 方法上,如果你在 protected、private 或者預設可見性的方法上使用 @Transactional 註解,註解將會被忽略,也不會丟擲任何異常。
3.預設情況下,只有來自外部的方法呼叫才會被AOP代理捕獲,也就是,類內部方法呼叫本類內部的其他方法並不會引起事務行為