一. 事務(wù)特性ACID
- 原子性
- 一致性
- 隔離性
- 持久性
二. 事務(wù)管理器
Spring并不直接管理事務(wù)愧怜,而是提供事務(wù)管理器接口呀页,讓框架自己(包括JDBC、hibernate拥坛、JPA蓬蝶、JTA等)實(shí)現(xiàn)具體的事務(wù)管理。
2.1 spring提供的事務(wù)管理器接口
包括3個(gè)接口:獲取事務(wù)狀態(tài)猜惋、提交丸氛、回滾
package org.springframework.transaction;
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition var1) throws TransactionException;
void commit(TransactionStatus var1) throws TransactionException;
void rollback(TransactionStatus var1) throws TransactionException;
}
2.2 具體實(shí)現(xiàn)(以JDBC為例):
DataSourceTransactionManager通過DataSource獲取到j(luò)ava.sql.Connection,再調(diào)用java.sql.Connection來管理事務(wù)
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="具體數(shù)據(jù)源"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
三. 事務(wù)基本屬性
上文提到的spring事務(wù)管理器接口中著摔,用于獲取事務(wù)狀態(tài)的getTransaction方法參數(shù)定義了一個(gè)事務(wù)的基本屬性
TransactionStatus getTransaction(TransactionDefinition var1)
事務(wù)基本屬性包括5類:
- 傳播行為
- 隔離級別
- 是否只讀
- 超時(shí)時(shí)間
- 回滾規(guī)則
public interface TransactionDefinition {
int getPropagationBehavior();
int getIsolationLevel();
int getTimeout();
boolean isReadOnly();
String getName();
}
3.1 傳播行為 PROPAGATION
定義一個(gè)事務(wù)被另一個(gè)事務(wù)調(diào)用時(shí)缓窜,兩個(gè)事務(wù)之間的關(guān)系,如:被調(diào)用事務(wù)合并到調(diào)用事務(wù)中運(yùn)行谍咆,或者獨(dú)立開啟一個(gè)新事務(wù)運(yùn)行等禾锤。
Spring定義了七種傳播行為:
- require_new(本身執(zhí)行時(shí)是事務(wù),被事務(wù)調(diào)用時(shí)也不合并為同一個(gè)事務(wù))
- require(本身執(zhí)行時(shí)是事務(wù)摹察,被事務(wù)調(diào)用時(shí)可合并在同一個(gè)事務(wù)里)
- support(本身執(zhí)行時(shí)不是事務(wù)恩掷,被事務(wù)調(diào)用時(shí)可合并在同一個(gè)事務(wù)里)
- not_support(總是非事務(wù)地執(zhí)行,并掛起任何存在的事務(wù))
- mandatory(本身執(zhí)行時(shí)拋出異常供嚎,被事務(wù)調(diào)用時(shí)可合并在同一個(gè)事務(wù)里)
- never(總是非事務(wù)地執(zhí)行黄娘,如果被其它事務(wù)調(diào)用則拋出異常)
- nest(嵌套事務(wù)克滴,本身執(zhí)行時(shí)是事務(wù)逼争,被事務(wù)調(diào)用時(shí)也不合并為同一個(gè)事務(wù),但是外層事務(wù)失敗時(shí)內(nèi)層事務(wù)也要回滾)
3.2 隔離級別
定義了一個(gè)事務(wù)可能受其他并發(fā)事務(wù)影響的程度
1.并發(fā)事務(wù)問題:
- 臟讀(一個(gè)事務(wù)讀取了另一個(gè)事務(wù)改寫但尚未提交的數(shù)據(jù)時(shí)劝赔,改寫在稍后被回滾了誓焦,那么第一個(gè)事務(wù)獲取的數(shù)據(jù)就是無效的)
- 不可重復(fù)讀
- 幻讀(事務(wù)T1讀取了幾行數(shù)據(jù),接著另一個(gè)并發(fā)事務(wù)T2插入了一些數(shù)據(jù)罩阵,故在隨后的查詢中启摄,事務(wù)T1就會發(fā)現(xiàn)多了一些原本不存在的記錄)
不可重復(fù)讀的重點(diǎn)是同一個(gè)數(shù)據(jù)被修改了歉备,幻讀的重點(diǎn)在于有數(shù)據(jù)新增或者刪除。
從結(jié)果看, 似乎不可重復(fù)讀和幻讀都表現(xiàn)為兩次讀取的結(jié)果不一致蕾羊。但如果從控制的角度來看, 兩者的區(qū)別就比較大:
- 對于前者, 只需要鎖住滿足條件的記錄
- 對于后者, 要鎖住滿足條件及其相近的記錄
2.隔離級別
- defaul(使用數(shù)據(jù)庫默認(rèn)隔離級別)
- read_uncommit(允許讀取未commit數(shù)據(jù),會導(dǎo)致上面3種問題)
- read_commit(允許讀取commit數(shù)據(jù)书闸,僅可避免臟讀)
- repeatable_read(數(shù)據(jù)只可被自身事務(wù)修改牌借,可避免臟讀膨报、不可重復(fù)讀)
- Serializable(完全滿足ACID现柠,通常是鎖表實(shí)現(xiàn))
3.3 是否只讀
如果事務(wù)只對數(shù)據(jù)庫進(jìn)行讀操作,則數(shù)據(jù)庫可以進(jìn)行一些特定的優(yōu)化扒寄。通過將事務(wù)設(shè)置為只讀该编,你就可以給數(shù)據(jù)庫一個(gè)機(jī)會于樟,讓它應(yīng)用它認(rèn)為合適的優(yōu)化措施。
3.4 超時(shí)時(shí)間
事務(wù)超時(shí)就是事務(wù)的一個(gè)定時(shí)器传黄,在特定時(shí)間內(nèi)事務(wù)如果沒有執(zhí)行完畢膘掰,那么就會自動回滾,而不是一直等待其結(jié)束佳遣。
3.5 回滾規(guī)則
默認(rèn)情況下识埋,事務(wù)只有遇到運(yùn)行期異常時(shí)才會回滾,而在遇到檢查型異常時(shí)不會回滾(這一行為與EJB的回滾行為是一致的)
但是你可以聲明事務(wù)在遇到特定的檢查型異常時(shí)像遇到運(yùn)行期異常那樣回滾苍日。同樣惭聂,你還可以聲明事務(wù)遇到特定的異常不回滾,即使這些異常是運(yùn)行期異常相恃。
四. spring事務(wù)實(shí)現(xiàn)方式
- 編程式事務(wù)(侵入到業(yè)務(wù)代碼里辜纲,但提供了更加詳細(xì)的事務(wù)管理)
- 聲明式事務(wù)(基于AOP,不影響業(yè)務(wù)代碼的具體實(shí)現(xiàn))
4.1 編程式事務(wù)
- 使用TransactionTemplate
- 直接使用PlatformTransactionManager
4.2 聲明式事務(wù)
- 每個(gè)Bean都有一個(gè)代理
- 所有Bean共享一個(gè)代理基類
- 使用攔截器
- 使用tx標(biāo)簽配置的攔截器
- 全注解
4.3 demo(聲明式事務(wù)拦耐,全注解方式)
使用@Transactional注解
/**
* 1.propagation 指定事務(wù)的傳播行為
* 默認(rèn)取值為REQUIRED耕腾,即使用調(diào)用方法的事務(wù)
* 設(shè)置為REQUIRES_NEW,即使用自己的事務(wù)杀糯,調(diào)用方法的事務(wù)被掛起
* 2.isolation 指定事務(wù)的隔離級別扫俺,最常用的取值為READ_COMMITTED
* 3.Spring 的聲明式事務(wù)默認(rèn)對所有運(yùn)行時(shí)異常進(jìn)行回滾,也可以通過對應(yīng)的屬性進(jìn)行自行設(shè)置
* 4.readOnly 指定事務(wù)是否為只讀固翰,若這個(gè)事務(wù)只讀取數(shù)據(jù)則設(shè)置為true狼纬,可幫助數(shù)據(jù)庫引擎優(yōu)化事務(wù)
* 5.timeOut 指定強(qiáng)制回滾之前事務(wù)可以占用的時(shí)間
*/
@Transactional(propagation=Propagation.REQUIRES_NEW,
isolation=Isolation.READ_COMMITTED,
noRollbackFor={UserAccountException.class},
readOnly=true, timeout=3)
@Override
public void purchase(String username, String isbn) {
//1.獲取書的單價(jià)
int price = bookShopDao.findBookPriceByIsbn(isbn);
//2.更新書的庫存
bookShopDao.updateBookStock(isbn);
//3.更新用戶余額
bookShopDao.updateUserAccount(username, price);
}
測試事務(wù)的傳播行為
@Transactional
@Override
public void checkout(String username, List<String> isbns) {
for(String isbn : isbns) {
bookShopService.purchase(username, isbn);
}
}