Q1、事務(wù)的四大特性是什么?
答案:
即ACID:
- 原子性,Atomicity,即事務(wù)包含的所有操作要么同成功,要么同失敗
- 一致性,Cosistency,即事務(wù)必須使得數(shù)據(jù)庫(kù)從一個(gè)一致性狀態(tài)到兩一個(gè)一致性狀態(tài)。如用戶A和用戶B兩者的錢加起來一共5000,那么不管A和B之間如何轉(zhuǎn)賬,轉(zhuǎn)幾次賬,事務(wù)結(jié)束后兩個(gè)用戶的錢相加來應(yīng)該還得是5000,這就是事務(wù)的一致性
- 隔離性,Isolation,多個(gè)事務(wù)并發(fā)時(shí),之間要相互隔離,不能被其他事務(wù)干擾
- 持久性,Durability,事務(wù)一旦提交,對(duì)數(shù)據(jù)庫(kù)中數(shù)據(jù)的改變就是永久的,即使數(shù)據(jù)庫(kù)故障也不會(huì)丟失提交事務(wù)的操作
Q2、Spring支持的事務(wù)管理類型有哪些?Spring事務(wù)實(shí)現(xiàn)方式有哪些?
答案: 支持的事務(wù)管理類型有兩種
Spring支持兩種類型的事務(wù)管理:
- 編程式事務(wù)管理:靈活性高,但難維護(hù)
@Autowired
TransactionTemplate transactionTemplate ;
-
聲明式事務(wù)管理:業(yè)務(wù)代碼和事務(wù)管理分離,只需用注解和xml配置來管理事務(wù)。以下為基于注解
@Transactional
:
答案: 實(shí)現(xiàn)聲明式事務(wù)的三種方式
- 基于接口:Spring早期版本時(shí)用,更接近底層源碼,有基于TransactionInterceptor的聲明式事務(wù),基于TransactionProxyFactoryBean的聲明式事務(wù)
-
基于
<tx>和<aop>
的xml聲明式事務(wù)管理:和Spring AOP結(jié)合,利用切點(diǎn)表達(dá)式使得事務(wù)管理更加靈活 - 基于@Transactional的全注解方式:在需要實(shí)施事務(wù)管理的方法或者類上加@Transactional注解,指定事務(wù)規(guī)則即可實(shí)現(xiàn)事務(wù)管理
Q3、說一下Spring的事務(wù)傳播行為
答案:
兩個(gè)事務(wù)方法之間的嵌套調(diào)用時(shí)
,這個(gè)事務(wù)方法如何進(jìn)行,即事務(wù)的傳播特性。
@Transactional
public void trans(){
sub();
log();
query();
}
@Transactional //SUPPORTS
public info query(){
}
@Transactional //REQUIRES_NEW
public void log(){
}
以上面的query方法為例,其用SUPPORTS,即單獨(dú)執(zhí)行時(shí)不開啟事務(wù)(就一個(gè)查詢,當(dāng)然不用開啟),被有事務(wù)的外部方法調(diào)用時(shí),則融入到這個(gè)外部方法的事務(wù)中,與他們同成功,同失敗。(到大學(xué)了,和其余室友住一個(gè)屋子,還是你自己外面租一個(gè)新房子)
Q4、說一下Spring的事務(wù)隔離
答案:
事務(wù)隔離用來解決并發(fā)事務(wù)所產(chǎn)生的一些問題:
- 臟讀
- 不可重復(fù)讀
- 幻影讀
通過設(shè)置不同的隔離級(jí)別,可解決以上問題。
臟讀:
事務(wù)2只是改了余額,但并未提交,事務(wù)1就把這個(gè)沒提交的值讀走了,如果以后事務(wù)2最終回滾,就出問題了。即一個(gè)事務(wù)讀取了另一個(gè)事務(wù)中沒有提交的數(shù)據(jù),會(huì)在本事務(wù)中產(chǎn)生數(shù)據(jù)不一致的問題。
@Transactional(isolation = isolation.READ_COMMITTED)
設(shè)置事務(wù)隔離策略為讀已提交,只讀別的并發(fā)事務(wù)已提交的修改
。
不可重復(fù)讀:
事務(wù)1先讀后去處理其他事兒,然后期間事務(wù)2修改并commit,等事務(wù)1再讀,則產(chǎn)生數(shù)據(jù)不一致的問題。
@Transactional(isolation = isolation.REPEATABLE_READ)
設(shè)置事務(wù)隔離策略為可重復(fù)讀REPEATABLE_READ,確保事務(wù)1可以多次從一個(gè)字段中讀到相同的值,即事務(wù)1執(zhí)行期間禁止其他事務(wù)對(duì)這個(gè)字段進(jìn)行更新(行鎖)
。
幻影讀:
不可重復(fù)讀是針對(duì)一行數(shù)據(jù),而幻影讀則是針對(duì)整個(gè)表
,比如兩次讀取,表中多出了一行數(shù)據(jù):
即一個(gè)事務(wù)所在的方法中,多次進(jìn)行整表數(shù)據(jù)讀取,結(jié)果不一樣,產(chǎn)生數(shù)據(jù)不一致問題。
@Transactional(isolation = isolation.SERIALIZABLE)
需要設(shè)置事務(wù)級(jí)別為串行化SERIALIZABLE
,確保事務(wù)1可以多次從一個(gè)表中讀到相同的行數(shù),事務(wù)1執(zhí)行期間,禁止其他事務(wù)對(duì)這個(gè)表進(jìn)行增刪改,但這樣性能十分低下(表鎖)
最后,當(dāng)不設(shè)置事務(wù)隔離級(jí)別時(shí),將默認(rèn)使用底層所選數(shù)據(jù)庫(kù)自身的默認(rèn)事務(wù)隔離級(jí)別。
SELECT @@tx_isolation;
Q5、Spring事務(wù)的實(shí)現(xiàn)原理
以JavaConfig的方式為例,使用是:
//啟動(dòng)事務(wù),這樣可以使用@Transactional注解
@EnableTransactionManagement
答案:
沒有Spring之前,單靠JDBC來操作是這樣的:
try {
//...
//將事務(wù)提交機(jī)制改為手動(dòng)提交
conn.setAutoCommit(false);
//業(yè)務(wù)邏輯
//在這里事務(wù)結(jié)束,手動(dòng)提交數(shù)據(jù)
conn.commit();
}
Spring事務(wù)是把上面業(yè)務(wù)邏輯前后的事務(wù)開啟與提交用AOP包了一下,即原理是:Spring事務(wù)底層是基于數(shù)據(jù)庫(kù)事務(wù)和AOP機(jī)制。
- 為使用了@Transactional注解的Bean創(chuàng)建一個(gè)動(dòng)態(tài)代理對(duì)象(bean初始化后調(diào)用bean的后置處理器來創(chuàng)建動(dòng)態(tài)代理)
- 如果是事務(wù)方法(類上面、接口上面、方法上面、接口方法上面),則開啟事務(wù):
try{
- 創(chuàng)建數(shù)據(jù)庫(kù)連接
- 修改數(shù)據(jù)庫(kù)連接的autocommit屬性為false,禁止此連接自動(dòng)提交
- 執(zhí)行當(dāng)前方法,方法中會(huì)執(zhí)行數(shù)據(jù)庫(kù)操作的業(yè)務(wù)SQL
-
}catch{
- 若出現(xiàn)異常,且這個(gè)異常需要回滾,則回滾事務(wù)
}
- 沒有發(fā)生異常,則提交事務(wù)
Q6、Spring事務(wù)傳播行為的實(shí)現(xiàn)原理是什么?
答案:
Spring的事務(wù)信息是存于ThreadLocal中的,所以一個(gè)線程永遠(yuǎn)只能有一個(gè)事務(wù)。對(duì)于被調(diào)用的事務(wù)方法,當(dāng):
- 融入:當(dāng)傳播行為是融入外部事務(wù),則拿到ThreadLocal中的Connection,共享一個(gè)數(shù)據(jù)庫(kù)連接,來共同提交與回滾
- 創(chuàng)建新事務(wù):當(dāng)傳播行為是創(chuàng)建新的事務(wù),則會(huì)把嵌套的新事務(wù)存入ThreadLocal,再將外部暫存起來,當(dāng)嵌套事務(wù)提交或回滾后,再將暫存的外部事務(wù)信息恢復(fù)到ThreadLocal來提交或回滾
詳細(xì)流程:
- 外部:創(chuàng)建數(shù)據(jù)庫(kù)連接Connection并存入ThreadLocal,修改數(shù)據(jù)庫(kù)連接的autocommit屬性為false
- 外部:返回事務(wù)狀態(tài)信息(
TransactionInfo.newTransaction=true
) - 外部:往下執(zhí)行方法,中途發(fā)現(xiàn)
內(nèi)部調(diào)用了另一個(gè)事務(wù)方法
- 內(nèi)嵌:判斷當(dāng)前ThreadLoacl是否已有Connection,有即是內(nèi)嵌事務(wù),需要判斷事務(wù)傳播行為,到此分兩種情況
情況一,當(dāng)傳播行為是融入
:
- 不會(huì)創(chuàng)建connection,返回事務(wù)狀態(tài)信息(
TransactionInfo.newTransaction=false
),即不是一個(gè)新事務(wù) - 內(nèi)部被調(diào)用的事務(wù)方法開始執(zhí)行相關(guān)SQL
- 執(zhí)行完后,判斷TransactionInfo.newTransaction是否為true,此時(shí)是融入,這個(gè)值為false,不提交
- 內(nèi)部被調(diào)用的事務(wù)方法執(zhí)行完成
- 外部方法繼續(xù)往下執(zhí)行
- 執(zhí)行完后判斷TransactionInfo.newTransaction是否為true,外部為true
- 拿到ThreadLocal中的connection進(jìn)行提交
情況二,當(dāng)傳播行為是創(chuàng)建新的事務(wù)
:
- 把外層方法事務(wù)相關(guān)的事務(wù)信息(包括connection、隔離級(jí)別、是否只讀…)暫存到TransactionInfo中,同時(shí)會(huì)把ThreadLocal中的事務(wù)信息置空
- 創(chuàng)建新的connection,返回事務(wù)狀態(tài)信息(
TransactionInfo.newTransaction=true
),即新事務(wù),并放入ThreadLocal當(dāng)中 - 內(nèi)部被調(diào)用的事務(wù)方法往下執(zhí)行
- 執(zhí)行完后判斷TransactionInfo.newTransaction是否為true?是?于是提交
- 判斷是否暫存了事務(wù) ? 是? 再把上面暫存的外部方法的事務(wù)信息放回ThreadLocal中
- 內(nèi)部被調(diào)用的事務(wù)方法執(zhí)行完成
- 外部事務(wù)方法接著執(zhí)行
- 執(zhí)行完后判斷TransactionInfo.newTransaction是否為true,外部為true
- 拿到ThreadLocal中的connection進(jìn)行提交
Q7、Spring多線程事務(wù),能否保證事務(wù)的一致性?
問題分析:兩個(gè)事務(wù)方法A和B,在兩個(gè)線程中,對(duì)應(yīng)的事務(wù)能否同時(shí)提交或回滾?
答案:
Spring不支持,因?yàn)镾pring事務(wù)信息存于ThreadLocal中的Connection,一個(gè)線程永遠(yuǎn)只能有一個(gè)事務(wù),所以無法實(shí)現(xiàn)兩個(gè)事務(wù)的一致性??梢酝ㄟ^編程式事務(wù)自己控制或者分布式事務(wù)來解決(二階段提交的方式)。
Q8、Spring事務(wù)失效的原因?
Spring事務(wù)底層是基于數(shù)據(jù)庫(kù)事務(wù)和AOP機(jī)制,因此,參考AOP失效,可以知道Spring事務(wù)失效的原因:
答案:
- 方法的內(nèi)部調(diào)用導(dǎo)致事務(wù)傳播失效
文章來源:http://www.zghlxwxcb.cn/news/detail-732499.html
- 方法是private會(huì)失效,解決: 改成public
- 目標(biāo)類沒有配置為Bean也會(huì)失效 解決: 配置為Bean,交給Spring管理
- 自己捕獲了異常 解決: 不要捕獲處理
- 使用cglib動(dòng)態(tài)代理,但是@Transactional聲明在接口上面
后面幾種本質(zhì)上是使用不當(dāng)導(dǎo)致的失效。文章來源地址http://www.zghlxwxcb.cn/news/detail-732499.html
到了這里,關(guān)于【Spring面試】八、事務(wù)相關(guān)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!