事務的特性:
原子性:事務是不可分割的最小單元,事務裏的操作要麼都做要麼都不做
隔離性:在併發程序中,併發事務之間互不影響,在對一個事務的操作不會對其他事務產生影響,事務的隔離性需要事務的隔離級別來完成。
一致性:在事務執行前後數據庫的數據都處於正確的狀態,
持久性:事務一旦執行成功,那麼對數據庫數據的改變是永久的,不會因爲因爲斷點或者系統故障而發生數據丟失。
在實際項目開發中,事務一般都是併發執行的,事務併發執行一般會遇到以下問題:
丟失更新:兩個事務先後更新同一行數據,後執行的事務的更新覆蓋掉了前一個事務的更新,這樣會導致前一個事務更新的數據丟失,這是由於沒有加鎖造成的。
髒讀:一個事務看到了另一個事務未提交的更新的數據。
不可重複讀:在同一事物中,多次讀取同一條數據確返回不同的結果。因爲有其他的事務更改了這些數據。
幻讀:一個事務在執行過程中讀取到了另一個事務已提交的插入數據,即第一個事務開始時讀取到一批數據,此時另一個事務又插入數據並提交,然後第一個事務又讀取這批數據單發現多了一條。
爲了解決事務併發的問題,規定了四種隔離級別:
1.未提交讀:最低的隔離級別,一個事務能夠讀取到別的事務未提交的更新數據,這很不安全,可能會導致丟失更新,髒讀,不可重複讀,幻讀。
2.提交讀:一個事務能讀取到別的事務提交的更新數據,不能看到未提交的更新數據,這樣避免了丟失更新,但是肯能會產生髒讀、不可重複讀、幻讀。
3.可重複讀:保證同一個事務中先後執行多次查詢都返回同一個結果,不受其他事務的影響,解決了丟失更新、髒讀、不可重複讀的問題,但是可能產生幻讀。
4.序列化:最高隔離級別,不允許事務併發執行,只能串行執行,避免了,丟失更新、髒讀、不可重複讀、幻讀等問題。
spring中對事務的支持分爲物理事物和邏輯事務。
物理事物就是底層數據庫提供的事務支持,例如JDBC
邏輯事務是spring管理的事務,邏輯事務提供更豐富的控制,在spring中如果沒有強調一般就是邏輯事務。
DefaultTransactionDefinition:事務定義,定義如隔離級別、傳播行爲,事務超時時間,事務是否只讀等。
例如:
- DefaultTransactionDefinition def = new DefaultTransactionDefinition();
- def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
- def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
定義事務的隔離級別爲提交讀,傳播行爲爲必須有事務,如果沒有事務則創建一個事務。
TransactionStatus:表示事務的狀態,獲取事務狀態後,spring根據事務的傳播行爲開啓事務。
事務隔離級別
spring 中使用TransactionDefinition的靜態變量來制定事務的隔離級別:
ISOLATION_DEFAULT:默認的隔離級別,使用底層的數據庫的默認的隔離級別。
IOSLATION_READ_UNCOMMITED:未提交讀
ISOLATION_READ_COMMITED:提交讀
IOSLATION_REPEATABLE_READ:可重複讀
IOSLATION_SERIALIZABLE:序列化
事務的傳播行爲:
事務的傳播行爲指定在多個事務方法間調用時,事務是如何在這些方法間傳播的。
spring共支持7種事務傳播行爲:
1.reuqired:必須有事務,否則新建一個事務,使用PROPAGATION_REQUIRED指定。
2.requiresNew:表示每次都創建新的邏輯事務,使用PROPAGATION_REQUIRES_NEW指定。
3.Supports:支持當前事務,使用PROPAGATION_SUPPORTS指定,如果當前存在邏輯事務,就加入到該邏輯事務中,如果當前沒有邏輯事務就以非邏輯事務的方式運行。
4.NotSupported:不支持事務,如果當前存在事務,則暫停該事物,以非事務的方式運行,使用PROPAGATION_NOT_SUPPORTED指定
5.Mandatory:使用當前事務執行,如果當前沒有事務則拋出異常,用PROPAGATION_MANDATORY指定
6.NEVER:不支持事務,如果當前存在事務則拋出異常,使用PROGATION_NEVER指定。
7.NESTED:嵌套事務支持,如果當前存在事務,則在嵌套事務內執行,如果當前事務不存在,則創建新的事務。嵌套事務使用數據庫中的保存點來執行,即嵌套事務回滾將不影響外部事務,外部事務回滾將導致嵌套事務回滾。
- <tx:advice id="txAdvice" transaction-manager="txManager">
- <tx:attributes>
- <tx:method name="save*" propagation="REQUIRED" />
- <tx:method name="add*" propagation="REQUIRED" />
- <tx:method name="create*" propagation="REQUIRED" />
- <tx:method name="insert*" propagation="REQUIRED" />
- <tx:method name="update*" propagation="REQUIRED" />
- <tx:method name="merge*" propagation="REQUIRED" />
- <tx:method name="del*" propagation="REQUIRED" />
- <tx:method name="remove*" propagation="REQUIRED" />
- <tx:method name="put*" propagation="REQUIRED" />
- <tx:method name="get*" propagation="SUPPORTS" read-only="true" />
- <tx:method name="count*" propagation="SUPPORTS" read-only="true" />
- <tx:method name="find*" propagation="SUPPORTS" read-only="true" />
- <tx:method name="list*" propagation="SUPPORTS" read-only="true" />
- <tx:method name="*" propagation="SUPPORTS" read-only="true" />
- </tx:attributes>
- </tx:advice>
註解式事務聲明:
@Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED)
@Override
public void save(final UserModel user) {
userDao.save(user);
user.getAddress().setUserId(user.getId());
addressService.save(user.getAddress());
}
@Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED, readOnly=true)
@Override
public int countAll() {
return userDao.countAll();
}