亚洲有码Av一区二区三区_国产高清啪啪免费视频_69色视频国产_国产成人人人爆出白浆_国产精品自在线拍国_一本久久伊人热热精品无码_午夜性刺激在线看免费带字幕_助力高品质欧美狂喷水_亚洲精品日韩无码_精品无码一区二区三区蜜臀_麻豆高清国产AV_熟妇人素无码中文字幕_亚洲a级片在线观看_国产欧美日韩三区_99国产成人高清在线观看

ARTICLE DETAIL

資訊詳情

深耕商務(wù)建站與企業(yè)官網(wǎng)運(yùn)營(yíng)的一線(xiàn)實(shí)戰(zhàn)洞察。

TransactionTemplate實(shí)戰(zhàn):Spring編程式事務(wù)的靈活控制之道

TransactionTemplate實(shí)戰(zhàn):Spring編程式事務(wù)的靈活控制之道 我個(gè)人的經(jīng)驗(yàn)里很多人對(duì) Spring 事務(wù)的第一印象都是 Transactional 注解寫(xiě)起來(lái)確實(shí)方便一行注解把事務(wù)邊界劃得清清楚楚。但它的問(wèn)題也恰恰藏在方便里——事務(wù)的開(kāi)啟、提交、回滾全都被框架接管了遇到需要精細(xì)控制事務(wù)邊界的場(chǎng)景比如一次業(yè)務(wù)里要分階段提交、要在一個(gè)事務(wù)里執(zhí)行多個(gè)回調(diào)、要把多個(gè)事務(wù)操作組合編排Transactional 就有點(diǎn)使不上勁。這時(shí)候就該 TransactionTemplate 登場(chǎng)了。TransactionTemplate 是 Spring 提供的編程式事務(wù)管理模板核心思路是把事務(wù)生命周期管理封裝起來(lái)但把控制權(quán)交還給你通過(guò)回調(diào)機(jī)制把事務(wù)邊界變成一段可執(zhí)行的代碼塊。簡(jiǎn)單說(shuō)它解決的是 Transactional 沒(méi)法覆蓋的場(chǎng)景動(dòng)態(tài)事務(wù)邊界、條件提交、細(xì)粒度回滾、事務(wù)內(nèi)多步驟編排、以及更復(fù)雜的嵌套事務(wù)組合。適合在支付回調(diào)、批量任務(wù)、消息消費(fèi)、異步處理這類(lèi)需要精細(xì)控制事務(wù)行為的業(yè)務(wù)里使用。這篇文章會(huì)從原理到實(shí)戰(zhàn)拆一遍 TransactionTemplate包括它和聲明式事務(wù)的底層關(guān)系、核心 API 的完整走讀、事務(wù)屬性怎么配、嵌套事務(wù)怎么玩、以及在真正落地的時(shí)候會(huì)踩哪些坑。雖然是一個(gè)比較老的類(lèi)但我在近期幾個(gè)項(xiàng)目里實(shí)測(cè)下來(lái)它依然是處理復(fù)雜事務(wù)場(chǎng)景最可靠的方案之一。1. 先搞清楚一個(gè)問(wèn)題為什么有 Transactional 還不夠1.1 聲明式事務(wù)的管理模型Transactional 是聲明式事務(wù)的入口它的底層是通過(guò) AOP 代理在方法調(diào)用前后織入事務(wù)邏輯。框架在方法開(kāi)始時(shí)開(kāi)啟事務(wù)方法正常返回時(shí)提交拋出異常時(shí)回滾開(kāi)發(fā)者不需要寫(xiě)任何事務(wù)代碼。這個(gè)模型的核心是方法即事務(wù)邊界——一個(gè) public 方法的執(zhí)行過(guò)程對(duì)應(yīng)一個(gè)完整的事務(wù)生命周期。這個(gè)設(shè)計(jì)在絕大多數(shù)場(chǎng)景下是合理的。比如一個(gè)下單流程從扣庫(kù)存到寫(xiě)訂單表整個(gè)方法就是一個(gè)事務(wù)要么全部成功要么全部不成功。但在實(shí)際項(xiàng)目里業(yè)務(wù)永遠(yuǎn)比理想模型復(fù)雜。我遇到過(guò)幾個(gè)典型的場(chǎng)景聲明式事務(wù)處理起來(lái)很別扭第一種事務(wù)邊界需要?jiǎng)討B(tài)變化。比如一個(gè)批量對(duì)賬任務(wù)要遍歷幾千條差異記錄每條記錄獨(dú)立的更新操作我希望每條記錄單獨(dú)一個(gè)事務(wù)某條失敗只回滾它自己不影響整體進(jìn)度。用 Transactional 放方法上所有記錄共用一個(gè)事務(wù)失敗一條全部回滾。拆成多個(gè)方法再通過(guò)外層調(diào)用來(lái)隔離事務(wù)代碼會(huì)零散得沒(méi)法看。第二種事務(wù)中間需要人為干預(yù)。比如某個(gè)網(wǎng)關(guān)同步任務(wù)落庫(kù)之后要調(diào)一個(gè)外部系統(tǒng)對(duì)方返回結(jié)果來(lái)決定整個(gè)事務(wù)要不要提交。Transactional 只有在方法異常時(shí)才回滾沒(méi)法在正常流程里主動(dòng)說(shuō)等一下我覺(jué)得這筆賬不對(duì)別提交了。第三種事務(wù)內(nèi)需要多次組合編排。比如一個(gè)復(fù)雜的賬戶(hù)遷移任務(wù)先查一批數(shù)據(jù)做一部分更新稍作邏輯判斷后再更新另一批數(shù)據(jù)每一步之間的事務(wù)狀態(tài)是連續(xù)的而且中間可能需要根據(jù)狀態(tài)決定是繼續(xù)還是終止。這不是一個(gè)簡(jiǎn)單的方法內(nèi)順序執(zhí)行而是事務(wù)控制的編排邏輯。這些場(chǎng)景的共同點(diǎn)是事務(wù)邊界經(jīng)常變化或者事務(wù)流程不可固定——聲明式事務(wù)的靜態(tài)邊界限制了大展拳腳的空間而 TransactionTemplate 正是為這類(lèi)場(chǎng)景設(shè)計(jì)的。1.2 TransactionTemplate 的事務(wù)管理模型通過(guò)回調(diào)來(lái)控制生命周期TransactionTemplate 的事務(wù)模型從根本上就不一樣。它實(shí)現(xiàn)的 TransactionOperations 接口核心就一個(gè)方法public interface TransactionOperations { T T execute(TransactionCallbackT action) throws TransactionException; }這里的核心思想是模板方法 回調(diào)??蚣軒湍闾幚硎聞?wù)的開(kāi)啟、提交、回滾、異常處理這些樣板代碼但你通過(guò) TransactionCallback 接口把業(yè)務(wù)邏輯塞進(jìn)去public interface TransactionCallbackT { T doInTransaction(TransactionStatus status); }在這個(gè)回調(diào)方法里你拿到的 TransactionStatus 對(duì)象就是你在事務(wù)里的控制手柄。這個(gè)對(duì)象可以做幾件事調(diào)用 setRollbackOnly() 主動(dòng)標(biāo)記事務(wù)回滾即使業(yè)務(wù)代碼沒(méi)有拋異常檢查當(dāng)前事務(wù)狀態(tài)比如 isNewTransaction()、isRollbackOnly()了解當(dāng)前事務(wù)處于什么階段在嵌套事務(wù)場(chǎng)景里協(xié)調(diào)不同層級(jí)的事務(wù)行為。所以整個(gè)模型就變成你定義事務(wù)邊界里要做的所有事回調(diào)框架按圖索驥把事務(wù)生命周期管理好模板。事務(wù)什么時(shí)候開(kāi)始、什么時(shí)候提交、什么時(shí)候回滾都在這段代碼塊的范圍里而這個(gè)范圍是你自己在運(yùn)行時(shí)動(dòng)態(tài)定義的。1.3 模板與回調(diào)模式讓事務(wù)控制變得靈活可控模板方法模式把這個(gè)靈活性的價(jià)值發(fā)揮得淋漓盡致。你可以把事務(wù)的開(kāi)啟、提交、回滾想象成一個(gè)流程框架比如烤肉店里客人下單 — 后廚備菜 — 上桌 — 結(jié)賬。模板要解決的是怎么把晚餐流程穩(wěn)定跑完而回調(diào)是今天晚上這一桌客人具體吃什么。框架部分無(wú)論誰(shuí)來(lái)吃都是同樣的流程菜品的差異全部封裝在回調(diào)里而你可以隨時(shí)決定這道菜不做、換成別的。TransactionTemplate 把事務(wù)相關(guān)的樣板代碼全部收編業(yè)務(wù)代碼只需要專(zhuān)注在這個(gè)事務(wù)里做什么剩下的邊界、狀態(tài)管理、資源清理全部由模板保證。最常見(jiàn)的對(duì)比是這樣的// 傳統(tǒng)的手動(dòng)事務(wù)管理 Connection conn dataSource.getConnection(); try { conn.setAutoCommit(false); // 執(zhí)行業(yè)務(wù)代碼... conn.commit(); } catch (Exception e) { conn.rollback(); throw e; } finally { conn.setAutoCommit(true); conn.close(); }跟上面的代碼相比下面的寫(xiě)法要清爽得多而且還能利用 Spring 管理的資源、統(tǒng)一異常處理機(jī)制、以及嵌套事務(wù)的協(xié)調(diào)能力transactionTemplate.execute(status - { // 執(zhí)行業(yè)務(wù)代碼... return result; });在真實(shí)項(xiàng)目里這種模板化帶來(lái)的可維護(hù)性收益在業(yè)務(wù)復(fù)雜之后會(huì)非常明顯地體現(xiàn)出來(lái)。2. 核心 API 與行為機(jī)制TransactionTemplate 的底層邏輯2.1 TransactionTemplate 本身如何完成事務(wù)管理TransactionTemplate 是一個(gè)類(lèi)它自己并不直接操作數(shù)據(jù)庫(kù)連接而是依賴(lài) Spring 事務(wù)機(jī)制里的 PlatformTransactionManager。初始化它的時(shí)候你需要給它設(shè)置一個(gè)事務(wù)管理器Configuration public class TransactionConfig { Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } Bean public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) { TransactionTemplate template new TransactionTemplate(); template.setTransactionManager(transactionManager); return template; } }DataSourceTransactionManager 是 JDBC 層面的實(shí)現(xiàn)負(fù)責(zé) DataSource 上連接的獲取、提交、回滾。如果你用的是 JPA需要配置 JpaTransactionManager如果同時(shí)操作多個(gè)數(shù)據(jù)源用 JtaTransactionManager 或者分布式事務(wù)方案。TransactionTemplate 本身是與具體數(shù)據(jù)源技術(shù)解耦的它只依賴(lài) PlatformTransactionManager 接口約定的事務(wù)生命周期。關(guān)鍵點(diǎn)在于 execute 方法執(zhí)行的完整流程拆開(kāi)看大概是這樣的根據(jù)設(shè)定的傳播行為事務(wù)管理器決定是新建一個(gè)事務(wù)還是加入已有事務(wù)如果新建事務(wù)把數(shù)據(jù)庫(kù)連接綁定到當(dāng)前線(xiàn)程自動(dòng)提交關(guān)閉執(zhí)行回調(diào)里的業(yè)務(wù)代碼拿到返回值如果業(yè)務(wù)代碼拋出運(yùn)行時(shí)異常并且沒(méi)有被標(biāo)記為不回滾事務(wù)管理器執(zhí)行回滾如果業(yè)務(wù)代碼正常返回或者拋出的異常被標(biāo)記為提交前不可回滾比如業(yè)務(wù)上主動(dòng) setRollbackOnly 后又想繼續(xù)事務(wù)管理器決定提交或回滾最后清理事務(wù)資源、恢復(fù)自動(dòng)提交狀態(tài)、把連接釋放回連接池。這里有個(gè)容易忽略的技術(shù)細(xì)節(jié)TransactionTemplate 在事務(wù)管理過(guò)程中主要依據(jù)回調(diào)拋出的異常類(lèi)型來(lái)判定是否回滾。默認(rèn)情況下它是遇到 RuntimeException 和 Error 就回滾遇到受檢異常不回滾。這個(gè)默認(rèn)行為繼承自 Spring 事務(wù)管理的整體設(shè)計(jì)——因?yàn)槭軝z異常在方法簽名中顯式聲明通常是為了捕獲后做補(bǔ)償或重試而不是為了回滾。讓我用一個(gè)實(shí)際場(chǎng)景來(lái)解釋這個(gè)機(jī)制的價(jià)值。比如一個(gè)銀行轉(zhuǎn)賬接口轉(zhuǎn)出方扣款成功之后轉(zhuǎn)入方入賬失敗正常情況下應(yīng)該整筆回滾。但如果異常已經(jīng)被外層捕獲并轉(zhuǎn)換成了受檢異常Transactional 默認(rèn)不會(huì)觸發(fā)回滾。而用 TransactionTemplate 的話(huà)由于你是直接在回調(diào)里控制狀態(tài)可以更明確地指定什么情況必須回滾??匆欢胃咏鎸?shí)業(yè)務(wù)的代碼public TransferResult transfer(TransferCommand command) { return transactionTemplate.execute(status - { try { accountService.debit(command.getFromAccount(), command.getAmount()); accountService.credit(command.getToAccount(), command.getAmount()); return new TransferResult(true, success); } catch (AccountNotFoundException e) { status.setRollbackOnly(); return new TransferResult(false, e.getMessage()); } catch (Exception e) { status.setRollbackOnly(); // 如果希望受檢異常也導(dǎo)致回滾需要在這里顯式處理 throw new RuntimeException(e); } }); }這段代碼里catch 到的受檢異常 AccountNotFoundException 本身不會(huì)自動(dòng)觸發(fā)回滾但通過(guò)顯式調(diào)用 status.setRollbackOnly()事務(wù)依然被標(biāo)記為回滾同時(shí)能返回一個(gè)結(jié)構(gòu)化的業(yè)務(wù)結(jié)果而不是把異常直接拋出去讓上層還要做一層異常處理。這就是編程式事務(wù)比較適合做 API 類(lèi)接口落到 DB 操作的場(chǎng)景的原因——事務(wù)回滾的決定權(quán)和業(yè)務(wù)返回結(jié)構(gòu)是統(tǒng)一的。2.2 事務(wù)傳播行為與 TransactionTemplate 的搭配玩法事務(wù)傳播行為Propagation是 Spring 事務(wù)模型里比較繞的一部分但跟 TransactionTemplate 搭配起來(lái)非常自然。TransactionTemplate 可以通過(guò) setPropagationBehavior 來(lái)設(shè)定事務(wù)邊界與當(dāng)前存在事務(wù)之間的關(guān)系TransactionTemplate template new TransactionTemplate(txManager); template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);常用傳播行為對(duì)照傳播行為語(yǔ)義典型場(chǎng)景PROPAGATION_REQUIRED當(dāng)前有事務(wù)則加入無(wú)則新建大多數(shù)業(yè)務(wù)默認(rèn)行為PROPAGATION_REQUIRES_NEW掛起當(dāng)前事務(wù)新建一個(gè)獨(dú)立事務(wù)日志記錄、審計(jì)、操作記錄PROPAGATION_NESTED當(dāng)前有事務(wù)時(shí)使用 Savepoint無(wú)則新建分段回滾部分失敗只回滾子段PROPAGATION_SUPPORTS當(dāng)前有事務(wù)則加入無(wú)則以非事務(wù)方式執(zhí)行只讀查詢(xún)、非關(guān)鍵輔助操作PROPAGATION_NOT_SUPPORTED始終以非事務(wù)方式執(zhí)行掛起現(xiàn)有事務(wù)事務(wù)中有非事務(wù)性操作如大文件操作PROPAGATION_MANDATORY必須有現(xiàn)有事務(wù)無(wú)則拋異常強(qiáng)約束某些操作必須在事務(wù)內(nèi)PROPAGATION_NEVER絕不能有事務(wù)有則拋異常事務(wù)里不能執(zhí)行的特定操作這里值得展開(kāi)的是 NESTED它跟 REQUIRES_NEW 經(jīng)常有人混淆。REQUIRES_NEW 的特點(diǎn)是徹底隔離它新建一個(gè)完全獨(dú)立的事務(wù)跟外部事務(wù)互不影響。外部事務(wù)回滾不影響內(nèi)部事務(wù)已經(jīng)提交的數(shù)據(jù)內(nèi)部事務(wù)回滾也不影響外部事務(wù)待提交的數(shù)據(jù)。這聽(tīng)起來(lái)很省事但代價(jià)是物理上開(kāi)了兩個(gè)數(shù)據(jù)庫(kù)事務(wù)連接占用、鎖等待、性能消耗都會(huì)增加。NESTED 則完全不同它是基于 Savepoint 的嵌套。如果外部事務(wù)存在NESTED 事務(wù)會(huì)在當(dāng)前事務(wù)里創(chuàng)建一個(gè) Savepoint內(nèi)部業(yè)務(wù)如果回滾只回滾到 Savepoint 位置外部事務(wù)仍然可以繼續(xù)。這個(gè)語(yǔ)義像把事務(wù)切了幾段某一段壞了只重做這一段。它是邏輯上的嵌套而不是另開(kāi)一個(gè)物理事務(wù)。場(chǎng)景差異就很明顯了。比如一個(gè)報(bào)表任務(wù)先匯總一筆總賬寫(xiě)到匯總表再寫(xiě)大量的明細(xì)記錄。明細(xì)中偶發(fā)一條數(shù)據(jù)格式錯(cuò)誤我希望只回滾出錯(cuò)的那段明細(xì)不動(dòng)總賬還要繼續(xù)處理后面幾條用 NESTED 比較合適。但如果是寫(xiě)審計(jì)日志要求不管主流程成不成功日志一定留下來(lái)那就得用 REQUIRES_NEW因?yàn)槿罩臼聞?wù)必須脫離主事務(wù)的生命周期。需要注意的是NESTED 依賴(lài)底層數(shù)據(jù)庫(kù)的 Savepoint 支持在 MySQL 的 InnoDB、PostgreSQL、Oracle 上都能正常工作但如果你用的數(shù)據(jù)庫(kù)不支持 Savepoint比如某些老版本 MyISAM它會(huì)在運(yùn)行時(shí)拋異常。所以選 NESTED 之前先確認(rèn)數(shù)據(jù)庫(kù)能力。在 TransactionTemplate 上設(shè)置傳播行為之后配合不同的回調(diào)組合就能很靈活地控制嵌套事務(wù)。比如外層一個(gè) template內(nèi)部某段業(yè)務(wù)又注入另一個(gè)配了 REQUIRES_NEW 的 template那這段業(yè)務(wù)會(huì)單獨(dú)事務(wù)執(zhí)行。2.3 隔離級(jí)別、超時(shí)與只讀TransactionTemplate 三個(gè)容易被忽略的屬性TransactionTemplate 除了傳播行為還可以設(shè)置事務(wù)的隔離級(jí)別、超時(shí)時(shí)間、是否只讀。這幾個(gè)屬性在實(shí)際項(xiàng)目中經(jīng)常被漏配但其實(shí)都很有用。隔離級(jí)別事務(wù)隔離級(jí)別解決的是并發(fā)下數(shù)據(jù)可見(jiàn)性的問(wèn)題。TransactionTemplate 支持標(biāo)準(zhǔn)的五個(gè)級(jí)別DEFAULT使用數(shù)據(jù)庫(kù)默認(rèn)隔離級(jí)別MySQL 默認(rèn)是 REPEATABLE_READREAD_UNCOMMITTED能讀到未提交數(shù)據(jù)可能會(huì)臟讀READ_COMMITTED只能讀到已提交數(shù)據(jù)解決臟讀REPEATABLE_READ同一個(gè)事務(wù)里多次查詢(xún)結(jié)果一致解決不可重復(fù)讀SERIALIZABLE完全串行化解決幻讀但性能影響很大。數(shù)據(jù)庫(kù)并發(fā)場(chǎng)景不同選法也不同。比如一個(gè)統(tǒng)計(jì)報(bào)表任務(wù)讀多寫(xiě)少用 REPEATABLE_READ 就能保證一次事務(wù)里多次查詢(xún)看到的快照一致如果要做賬務(wù)核對(duì)類(lèi)操作精度要求高的話(huà)就要考慮 SERIALIZABLE但要衡量對(duì)并發(fā)性能的影響。在 TransactionTemplate 上設(shè)置隔離級(jí)別template.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);超時(shí)時(shí)間事務(wù)超時(shí)是針對(duì)事務(wù)內(nèi)所有數(shù)據(jù)庫(kù)操作耗時(shí)總和的約束。設(shè)置之后如果超過(guò)時(shí)間限制Spring 會(huì)讓事務(wù)回滾并拋出異常。默認(rèn)超時(shí)是 -1表示使用底層數(shù)據(jù)源的默認(rèn)值或不設(shè)限制。這個(gè)屬性在長(zhǎng)時(shí)間運(yùn)行的批處理任務(wù)、運(yùn)營(yíng)后臺(tái)的 Excel 導(dǎo)入里很有用。沒(méi)有超時(shí)約束時(shí)如果數(shù)據(jù)庫(kù)鎖等待時(shí)間過(guò)長(zhǎng)事務(wù)會(huì)一直占著連接連接池耗盡之后整個(gè)應(yīng)用就假死了。設(shè)一個(gè)合理的超時(shí)時(shí)間比如 30 秒就能在事務(wù)卡住的時(shí)候快速失敗并釋放連接。template.setTimeout(30); // 單位秒但要注意一點(diǎn)這個(gè)超時(shí)是通過(guò) JDBC 層的定時(shí)檢查來(lái)實(shí)現(xiàn)的如果在事務(wù)里有一個(gè)特別耗時(shí)的第三方調(diào)用那是不會(huì)被這個(gè)超時(shí)約束的。它只約束數(shù)據(jù)庫(kù)操作自身的時(shí)間。只讀事務(wù)把事務(wù)標(biāo)記為只讀可以讓數(shù)據(jù)庫(kù)優(yōu)化器做一些查詢(xún)路徑上的優(yōu)化在 MySQL 里主要是提示服務(wù)器不產(chǎn)生讀鎖寫(xiě)日志等開(kāi)銷(xiāo)同時(shí)也是一種語(yǔ)義約束——在只讀事務(wù)里發(fā)生的寫(xiě)操作通常會(huì)在提交階段報(bào)錯(cuò)。在查詢(xún)類(lèi)任務(wù)里把事務(wù)標(biāo)記為只讀配合 REPEATABLE_READ 隔離級(jí)別能減少不必要的開(kāi)銷(xiāo)。比如template.setReadOnly(true);要注意的是如果這個(gè)對(duì)象還被復(fù)用于寫(xiě)操作那就不能設(shè)置只讀否則后續(xù)的寫(xiě)操作會(huì)出問(wèn)題。因此模板如果是被多個(gè)業(yè)務(wù)共享的建議針對(duì)不同用途配置多個(gè) TransactionTemplate 實(shí)例而不是一個(gè)模板走到底。2.4 使用 TransactionCallbackWithoutResult 簡(jiǎn)化無(wú)返回值場(chǎng)景TransactionTemplate.execute 需要返回一個(gè)值但很多時(shí)候我們只是在事務(wù)里執(zhí)行更新不需要返回任何對(duì)象。Spring 提供了一個(gè)子接口 TransactionCallbackWithoutResult它把返回值定義為 void執(zhí)行完成之后模板自動(dòng)提交或者根據(jù)異?;貪L。transactionTemplate.execute(new TransactionCallbackWithoutResult() { Override protected void doInTransactionWithoutResult(TransactionStatus status) { orderDao.updateStatus(orderId, OrderStatus.PAID); accountDao.incrementBalance(userId, amount); } });使用 Lambda 表達(dá)式可以更簡(jiǎn)潔transactionTemplate.executeWithoutResult(status - { orderDao.updateStatus(orderId, OrderStatus.PAID); accountDao.incrementBalance(userId, amount); });代碼里的 executeWithoutResult 是 TransactionOperations 接口的 default 方法內(nèi)部會(huì)把回調(diào)包裝成 TransactionCallback返回 null然后模板統(tǒng)一處理事務(wù)生命周期。這個(gè)寫(xiě)法適合那些通知一下、更新幾個(gè)字段、不需要拿返回值的事務(wù)操作可以讓業(yè)務(wù)方法表達(dá)出真實(shí)意圖——我不關(guān)心返回值只要這段代碼在一個(gè)事務(wù)里執(zhí)行。2.5 編程式事務(wù) vs 聲明式事務(wù)怎么選才合適很多團(tuán)隊(duì)會(huì)有這種討論到底是用 Transactional 還是 TransactionTemplate我的結(jié)論是不是二選一而是看事務(wù)邊界的可預(yù)測(cè)性。事務(wù)邊界固定整個(gè)方法就是事務(wù)方法結(jié)束即事務(wù)結(jié)束那就用聲明式 Transactional代碼最簡(jiǎn)潔意圖最清晰。事務(wù)邊界動(dòng)態(tài)需要根據(jù)運(yùn)行時(shí)的數(shù)據(jù)、狀態(tài)或者外部調(diào)用結(jié)果來(lái)決定事務(wù)怎么結(jié)束那就用 TransactionTemplate。事務(wù)粒度細(xì)分一個(gè)業(yè)務(wù)里需要多次獨(dú)立事務(wù)、嵌套事務(wù)、分段提交那也適合用 TransactionTemplate。新舊代碼混合老項(xiàng)目里已經(jīng)有聲明式事務(wù)新加的功能又需要更靈活的控制可以在同一個(gè)服務(wù)里混用只要互相清楚各自的事務(wù)邊界。我實(shí)際看到過(guò)一種比較推薦的做法是在 Service 層定義一個(gè)注入的 TransactionTemplate用它來(lái)完成所有需要編程式事務(wù)控制的方法其他地方保持 Transactional 做透明事務(wù)。兩者職責(zé)分開(kāi)不會(huì)互相干擾。3. 深入事務(wù)狀態(tài)對(duì)象TransactionStatus 背后的設(shè)計(jì)思想3.1 TransactionStatus 能做什么回調(diào)里拿到的 TransactionStatus 接口定義了幾個(gè)關(guān)鍵方法public interface TransactionStatus { boolean isNewTransaction(); boolean hasSavepoint(); void setRollbackOnly(); boolean isRollbackOnly(); void flush(); boolean isCompleted(); }逐個(gè)說(shuō)isNewTransaction()當(dāng)前事務(wù)是否是新建的。如果因?yàn)閭鞑バ袨榧尤肓送鈱右延惺聞?wù)這個(gè)方法返回 false。這個(gè)判斷在嵌套編排里特別有用——如果你需要一個(gè)獨(dú)立事務(wù)但發(fā)現(xiàn)自己是在外層事務(wù)里就要考慮調(diào)整傳播行為或者拆分模板。hasSavepoint()當(dāng)前事務(wù)是否保存了 Savepoint。跟 NESTED 傳播行為配合判斷當(dāng)前是否處于嵌套層級(jí)。setRollbackOnly()把事務(wù)標(biāo)記為只能回滾不能提交。調(diào)用這個(gè)方法之后即使回調(diào)正常結(jié)束事務(wù)在提交階段也會(huì)被強(qiáng)制回滾。這是編程式事務(wù)最核心的控制能力。isRollbackOnly()查詢(xún)當(dāng)前事務(wù)是否已經(jīng)被標(biāo)記為回滾。flush()強(qiáng)制把當(dāng)前會(huì)話(huà)中的修改同步到數(shù)據(jù)庫(kù)。在 JPA/Hibernate 這類(lèi)帶持久化上下文的場(chǎng)景下有時(shí)候需要提前 flush 以讓后續(xù)查詢(xún)看到更新或者讓數(shù)據(jù)庫(kù)約束檢查提前觸發(fā)。isCompleted()事務(wù)是否已經(jīng)結(jié)束提交或回滾。3.2 setRollbackOnly 的實(shí)際價(jià)值setRollbackOnly 在實(shí)際業(yè)務(wù)中有兩個(gè)關(guān)鍵應(yīng)用場(chǎng)景。場(chǎng)景一是業(yè)務(wù)上判定失敗但代碼沒(méi)拋異常。比如在業(yè)務(wù)校驗(yàn)中某個(gè)賬戶(hù)已經(jīng)凍結(jié)再往下執(zhí)行沒(méi)有意義但你要的是回滾而不必制造一個(gè)異常transactionTemplate.executeWithoutResult(status - { deductionService.deductPoints(userId, points); if (memberService.isFrozen(userId)) { status.setRollbackOnly(); return; } // 繼續(xù)其他正常業(yè)務(wù)... });這里如果直接 throw new RuntimeException會(huì)導(dǎo)致業(yè)務(wù)異常堆棧被包裝成框架異常調(diào)用方要解析這一層而 setRollbackOnly 直接回滾事務(wù)同時(shí)代碼還能正常返回業(yè)務(wù)狀態(tài)可以繼續(xù)處理。場(chǎng)景二是外部調(diào)用成功后本事務(wù)主動(dòng)放棄。比如支付回調(diào)本地事務(wù)先更新訂單狀態(tài)再調(diào)用外部支付系統(tǒng)確認(rèn)如果外部確認(rèn)結(jié)果要求本地事務(wù)取消則 setRollbackOnly 回滾本地變更。需要特別注意的是一旦調(diào)用了 setRollbackOnly這個(gè)事務(wù)就被打上了回滾標(biāo)簽。在同一個(gè)事務(wù)里即使你后續(xù)再執(zhí)行其他操作也無(wú)法撤銷(xiāo)這個(gè)標(biāo)記。這也是一個(gè)經(jīng)典的踩坑點(diǎn)——在一個(gè)共享事務(wù)里某個(gè)組件標(biāo)記了回滾但外層業(yè)務(wù)還繼續(xù)執(zhí)行并且最終想提交結(jié)果整個(gè)事務(wù)還是回滾了。3.3 flush() 在 ORM 場(chǎng)景中的應(yīng)用如果你在 TransactionTemplate 里使用 JPA那么 flush() 的使用頻率會(huì)比 JDBC 高得多。原因是 JPA 是一種延遲寫(xiě)的模型實(shí)體更新之后不會(huì)立刻刷到數(shù)據(jù)庫(kù)而是攢到事務(wù)提交前統(tǒng)一 flush。如果業(yè)務(wù)邏輯緊接著要查詢(xún)剛剛更新的數(shù)據(jù)或者要讓數(shù)據(jù)庫(kù)唯一約束提前生效就需要主動(dòng) flush。舉個(gè)例子事務(wù)里先給用戶(hù)分配一個(gè)優(yōu)惠碼然后立刻判斷這個(gè)優(yōu)惠碼是否已被使用過(guò)。如果依賴(lài)數(shù)據(jù)庫(kù)的唯一索引來(lái)校驗(yàn)而 flush 發(fā)生在事務(wù)提交時(shí)那么第二次查詢(xún)可能還沒(méi)把第一次的寫(xiě)入刷進(jìn)去導(dǎo)致查不到數(shù)據(jù)。顯式調(diào)用 status.flush() 會(huì)把當(dāng)前的實(shí)體變化同步到數(shù)據(jù)庫(kù)讓后續(xù)邏輯能看到最新?tīng)顟B(tài)同時(shí)唯一索引沖突也能提前暴露。這個(gè)知識(shí)點(diǎn)在先寫(xiě)后查同一事務(wù)的場(chǎng)景里很容易救你一命。4. 實(shí)操篇一個(gè)事務(wù)模板搞定復(fù)雜業(yè)務(wù)場(chǎng)景4.1 方法一標(biāo)準(zhǔn)模板配置與復(fù)用在實(shí)際項(xiàng)目里強(qiáng)烈建議不要每次 new 一個(gè) TransactionTemplate而是把它作為 Spring Bean 注入使用。原因有兩個(gè)一個(gè)實(shí)例配置一次事務(wù)屬性后續(xù)復(fù)用省去重復(fù)配置另外 Spring 容器管理生命周期也更符合統(tǒng)一配置原則。最典型的配置方式Component public class TransactionTemplates { Bean public TransactionTemplate requiredTemplate(PlatformTransactionManager tm) { TransactionTemplate template new TransactionTemplate(tm); template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); template.setTimeout(30); return template; } Bean public TransactionTemplate requiresNewTemplate(PlatformTransactionManager tm) { TransactionTemplate template new TransactionTemplate(tm); template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); return template; } }這樣你在 Service 里就能按場(chǎng)景選擇合適的模板Service public class OrderService { private final TransactionTemplate requiredTemplate; private final TransactionTemplate requiresNewTemplate; public void createOrder(OrderCommand command) { requiredTemplate.executeWithoutResult(status - { orderDao.insert(command.toOrder()); // 給用戶(hù)加積分 requiresNewTemplate.executeWithoutResult(innerStatus - { pointsDao.increment(command.getUserId(), command.getPoints()); }); }); } }這段代碼的含義是創(chuàng)建訂單和加積分在同一個(gè)事務(wù) REQUIRED 里但給用戶(hù)加積分這個(gè)動(dòng)作又單獨(dú)做了一個(gè) REQUIRES_NEW 事務(wù)。即使創(chuàng)建訂單的事務(wù)最終回滾積分增加的記錄也已經(jīng)被獨(dú)立提交了這在某些場(chǎng)景下恰恰是業(yè)務(wù)需要的比如只要用戶(hù)點(diǎn)擊了下單按鈕積分就要加不管訂單最終有沒(méi)有落庫(kù)成功。當(dāng)然真實(shí)業(yè)務(wù)要不要這么做要看產(chǎn)品定義但 TransactionTemplate 的組合使用方式就是這么靈活。4.2 方法二在一個(gè)事務(wù)里分階段執(zhí)行業(yè)務(wù)并決定提交還是回滾很多系統(tǒng)對(duì)接場(chǎng)景需要本地更新 遠(yuǎn)程確認(rèn)的組合。比如支付回調(diào)處理過(guò)程中本地先更新訂單狀態(tài)、記賬然后調(diào)用遠(yuǎn)程接口做風(fēng)控確認(rèn)確認(rèn)結(jié)果決定事務(wù)走向。用 TransactionTemplate 實(shí)現(xiàn)public PaymentResult handleCallback(PaymentCallback callback) { return transactionTemplate.execute(status - { // 階段一本地?cái)?shù)據(jù)更新 orderService.markPaid(callback.getOrderId()); paymentLogService.record(callback); // 階段二遠(yuǎn)程確認(rèn) RiskCheckResult riskResult riskClient.check(callback); if (!riskResult.isPass()) { status.setRollbackOnly(); return PaymentResult.reject(riskResult.getReason()); } // 階段三本地?cái)?shù)據(jù)二次更新 orderService.confirmPaid(callback.getOrderId()); return PaymentResult.accept(); }); }這里有幾個(gè)好處事務(wù)邊界覆蓋整個(gè)本地?cái)?shù)據(jù)更新鏈路任何一步發(fā)現(xiàn)需要終止setRollbackOnly 就能讓全部本地變更回滾返回結(jié)果是一個(gè)結(jié)構(gòu)化的業(yè)務(wù)對(duì)象不用依賴(lài)異常傳播來(lái)表達(dá)失敗語(yǔ)義遠(yuǎn)程調(diào)用的時(shí)間被包含在事務(wù)生命周期里如果超時(shí)可以設(shè)置整體事務(wù)超時(shí)兜底在回調(diào)里可以自由編排階段順序、跳過(guò)某些步驟代碼直觀(guān)。4.3 方法三批量任務(wù)中實(shí)現(xiàn)行級(jí)事務(wù)與隔離批量處理是 TransactionTemplate 比較能發(fā)揮優(yōu)勢(shì)的場(chǎng)景。比如定時(shí)任務(wù)要遍歷一個(gè)比較大的數(shù)據(jù)集合幾萬(wàn)條每條記錄執(zhí)行獨(dú)立的更新 校驗(yàn) 記錄流程。要求是單條失敗不影響后續(xù)記錄且每條記錄的事務(wù)互不干擾。實(shí)現(xiàn)方式有兩種思路取決于對(duì)事務(wù)邊界的理解。第一種每一條記錄一個(gè)獨(dú)立事務(wù)for (Record record : records) { try { transactionTemplate.executeWithoutResult(status - { validate(record); update(record); audit(record); }); } catch (Exception e) { log.error(記錄處理失敗, recordId{}, record.getId(), e); // 單條失敗不影響循環(huán)繼續(xù) } }這里的 TransactionTemplate 配 PROPAGATION_REQUIRED每次循環(huán)開(kāi)啟一個(gè)新事務(wù)處理完立即提交。某條失敗異常被 catch 捕獲本事務(wù)回滾其他記錄照常處理。這個(gè)模式在數(shù)據(jù)庫(kù)鎖競(jìng)爭(zhēng)不大、單條操作耗時(shí)不長(zhǎng)的任務(wù)中很高效。第二種如果單條處理邏輯內(nèi)部還分多個(gè)階段可以用 NESTED 做局部回滾transactionTemplate.executeWithoutResult(status - { // 匯總更新 summaryDao.updateSummary(records); // 分階段處理細(xì)分回滾段 try { transactionTemplate.executeWithoutResult(innerStatus - { for (Record record : records) { detailDao.insert(record); } }); } catch (Exception e) { // 明細(xì)插入失敗回滾到 Savepoint但匯總結(jié)論還在 log.error(明細(xì)處理失敗但匯總已更新, e); } });這里外層事務(wù)控制總體內(nèi)層用 NESTED 細(xì)分回滾讓明細(xì)失敗但匯總保留成為可能。實(shí)際項(xiàng)目中具體怎么設(shè)計(jì)取決于業(yè)務(wù)要求的隔離粒度。4.4 方法四事務(wù)模板與 CompletableFuture 異步任務(wù)的組合TransactionTemplate 在異步場(chǎng)景里也有獨(dú)特的價(jià)值。Spring 聲明式事務(wù)是綁定在線(xiàn)程上的異步線(xiàn)程里如果用 Transactional 會(huì)面臨事務(wù)上下文丟失的問(wèn)題。而 TransactionTemplate 因?yàn)榘咽聞?wù)邊界的控制權(quán)交給你可以在異步任務(wù)內(nèi)部開(kāi)啟獨(dú)立事務(wù)天然規(guī)避了這個(gè)坑。比如某個(gè)任務(wù)需要先同步做一段預(yù)處理再異步插入大量明細(xì)public void processAsync(ListData batchData) { transactionTemplate.executeWithoutResult(status - { prepareMainData(batchData); }); CompletableFuture.runAsync(() - { transactionTemplate.executeWithoutResult(status - { for (Data data : batchData) { detailDao.save(data); } }); }).exceptionally(ex - { log.error(異步寫(xiě)明細(xì)失敗, ex); return null; }); }這里同步部分先提交一個(gè)事務(wù)異步部分另起一個(gè)事務(wù)兩個(gè)事務(wù)互不干擾異步里如果失敗也不影響主流程已提交的數(shù)據(jù)。這正是 TransactionTemplate 在異步場(chǎng)景里相對(duì)聲明式事務(wù)的優(yōu)勢(shì)體現(xiàn)。當(dāng)然異步場(chǎng)景下真正需要注意的是分布式事務(wù)的一致性方案比如本地消息表 定時(shí)任務(wù)、事務(wù)消息等。但只要你把事務(wù)邊界定義清楚這些方案里的本地事務(wù)部分TransactionTemplate 都可以承擔(dān)。4.5 TransactionTemplate 和 Transactional 的混合使用技巧可能有人會(huì)問(wèn)一個(gè) Service 里能不能同時(shí)用 TransactionTemplate 和 Transactional可以的Spring 的事務(wù)管理是雙通道的只要事務(wù)管理器一致它們就共享同一個(gè)事務(wù)設(shè)施。一個(gè)常見(jiàn)的設(shè)計(jì)是對(duì)外暴露的入口方法用 Transactional 聲明統(tǒng)一事務(wù)內(nèi)部某些特殊步驟用 TransactionTemplate 做獨(dú)立的子事務(wù)REQUIRES_NEW 或 NESTED。示例如下Transactional public void processOrder(Order order) { // 主事務(wù)處理 orderDao.updateStatus(order.getId(), OrderStatus.PROCESSING); // 子事務(wù)記錄操作日志即使主事務(wù)回滾也保留 auditTemplate.executeWithoutResult(status - { logDao.insert(processOrder, order.getId()); }); }這里 processOrder 用 Transactional 劃定了主事務(wù)邊界而審計(jì)日志使用獨(dú)立的 REQUIRES_NEW 事務(wù)。這樣即使主流程后續(xù)拋異常導(dǎo)致回滾審計(jì)日志也已經(jīng)落庫(kù)了可以追溯這個(gè)訂單確實(shí)被嘗試處理過(guò)?;旌鲜褂玫年P(guān)鍵約束是參與同一個(gè)物理事務(wù)的代碼必須保持一致的傳播行為認(rèn)知不能出現(xiàn)一邊期待獨(dú)立回滾、一邊又共享事務(wù)狀態(tài)的邏輯混亂。因此混合使用之前最好寫(xiě)清楚每個(gè)方法的事務(wù)語(yǔ)義注釋。5. 踩坑記錄與排查實(shí)錄TransactionTemplate 使用中的高發(fā)問(wèn)題5.1 坑一事務(wù)管理器配置錯(cuò)了導(dǎo)致事務(wù)根本沒(méi)生效TransactionTemplate 最典型的配置錯(cuò)誤是給它塞了一個(gè)跟實(shí)際數(shù)據(jù)源不匹配的事務(wù)管理器。比如項(xiàng)目里用了 JPA卻從容器里拿了一個(gè) DataSourceTransactionManager或者配置了兩個(gè)數(shù)據(jù)源但只給事務(wù)管理器設(shè)了一個(gè)。排查思路出現(xiàn)明明在事務(wù)里執(zhí)行的代碼異常了卻沒(méi)回滾的詭異現(xiàn)象第一步去看 TransactionTemplate 的 transactionManager 到底是什么類(lèi)型。如果事務(wù)管理器跟數(shù)據(jù)源對(duì)不上整個(gè)事務(wù)就是空轉(zhuǎn)狀態(tài)——業(yè)務(wù)代碼執(zhí)行了但根本沒(méi)有物理事務(wù)來(lái)保護(hù)它。另外如果多個(gè)數(shù)據(jù)源配置了多個(gè)事務(wù)管理器那么 Spring 容器會(huì)有多個(gè) PlatformTransactionManager Bean注入 TransactionTemplate 時(shí)要通過(guò) Qualifier 明確指定Bean public TransactionTemplate orderTemplate(Qualifier(orderTransactionManager) PlatformTransactionManager tm) { return new TransactionTemplate(tm); }這個(gè)錯(cuò)誤隱蔽性比較強(qiáng)因?yàn)榇a不會(huì)編譯報(bào)錯(cuò)日志也看不出明顯異常。5.2 坑二事務(wù)鎖與連接池耗盡問(wèn)題長(zhǎng)事務(wù)占用連接是最常見(jiàn)的高危場(chǎng)景。TransactionTemplate 的 execute 從進(jìn)入回調(diào)到方法返回整個(gè)期間都持有一個(gè)數(shù)據(jù)庫(kù)連接。如果回調(diào)里有個(gè)慢查詢(xún)或者同步調(diào)用了外部接口事務(wù)就一直掛著連接遲遲不歸還。這種情況在并發(fā)上來(lái)之后容易出事連接池默認(rèn) 10 個(gè)連接10 個(gè)請(qǐng)求都在事務(wù)里等外部接口后續(xù)請(qǐng)求全部阻塞在獲取連接的環(huán)節(jié)系統(tǒng)假死。解決經(jīng)驗(yàn)回調(diào)里盡量別做耗時(shí)操作外部 HTTP 調(diào)用建議放在事務(wù)外配置合理的超時(shí)時(shí)間至少讓連接不無(wú)限等待監(jiān)控連接池使用率超過(guò)閾值要報(bào)警。如果確實(shí)需要在事務(wù)里做外部調(diào)用某些強(qiáng)一致場(chǎng)景無(wú)法避免至少要給外部調(diào)用設(shè)置單獨(dú)的短超時(shí)別讓一個(gè)事務(wù)等外部接口 60 秒。5.3 坑三setRollbackOnly 之后還想提交事務(wù)已經(jīng)回滾這個(gè)坑的迷惑性很強(qiáng)。比如某個(gè)嵌套事務(wù)里內(nèi)層事務(wù)調(diào)用了 setRollbackOnly但外層邏輯覺(jué)得不影響大局繼續(xù)提交。結(jié)果是整個(gè)事務(wù)在提交階段被強(qiáng)制回滾代碼上沒(méi)有拋出任何異??雌饋?lái)就像什么都沒(méi)發(fā)生但數(shù)據(jù)沒(méi)落庫(kù)。我碰到過(guò)一次經(jīng)典的案例一個(gè)交易系統(tǒng)主流程調(diào)用了內(nèi)部記賬模塊記賬模塊在某個(gè)分支里 setRollbackOnly 后返回了一個(gè)正常對(duì)象主流程繼續(xù)執(zhí)行其他步驟最終想提交整個(gè)事務(wù)結(jié)果數(shù)據(jù)完全沒(méi)有寫(xiě)入。當(dāng)時(shí)排查了很久才發(fā)現(xiàn)是內(nèi)部模塊的 rollbackOnly 標(biāo)記傳遞到了整個(gè)物理事務(wù)上。這個(gè)問(wèn)題的根源是在 REQUIRED 傳播行為下內(nèi)層事務(wù)和外層事務(wù)共享同一個(gè)物理事務(wù)setRollbackOnly 修改的是整個(gè)物理事務(wù)的狀態(tài)。解決辦法如果內(nèi)層業(yè)務(wù)對(duì)標(biāo)記回滾有獨(dú)立語(yǔ)義應(yīng)該配置 REQUIRES_NEW 讓內(nèi)層單獨(dú)開(kāi)物理事務(wù)或者在外層調(diào)用前檢查 status.isRollbackOnly()提前中斷別讓后續(xù)代碼繼續(xù)空跑。5.4 坑四受檢異常默認(rèn)不回滾數(shù)據(jù)部分成功回想一下TransactionTemplate 的默認(rèn)回滾策略跟 Spring 其他事務(wù)策略一致遇到 RuntimeException 和 Error 回滾受檢異常默認(rèn)不回滾。這就會(huì)導(dǎo)致一個(gè)隱蔽問(wèn)題如果你的業(yè)務(wù)代碼 catch 了一個(gè)受檢異常并向上層拋出比如轉(zhuǎn)換為自定義業(yè)務(wù)異常事務(wù)不會(huì)回滾但之前的數(shù)據(jù)改動(dòng)已經(jīng)留在數(shù)據(jù)庫(kù)里了看起來(lái)像部分?jǐn)?shù)據(jù)更新成功。例如transactionTemplate.executeWithoutResult(status - { accountDao.deduct(userId, amount); transferDao.record(transfer); throw new BusinessException(賬戶(hù)凍結(jié)); // BusinessException extends Exception });BusinessException 如果是受檢異常上面的代碼不會(huì)觸發(fā)回滾扣款和記錄都已經(jīng)提交了。這就需要顯式處理要么讓 BusinessException 繼承 RuntimeException要么在 catch 中調(diào)用 status.setRollbackOnly()。排查方法如果發(fā)現(xiàn)數(shù)據(jù)異常但沒(méi)拋錯(cuò)先從哪些異常是不會(huì)觸發(fā)回滾的查起確認(rèn)異常體系是不是用了受檢異常。5.5 坑五事務(wù)內(nèi)操作跨線(xiàn)程事務(wù)上下文丟失Spring 的事務(wù)核心是把連接綁定在 ThreadLocal 上的所以它天然是線(xiàn)程綁定的。如果你在回調(diào)里使用 new Thread 或線(xiàn)程池異步執(zhí)行數(shù)據(jù)庫(kù)操作這些異步線(xiàn)程不持有當(dāng)前線(xiàn)程的事務(wù)上下文它們跑的 SQL 會(huì)走獨(dú)立連接、獨(dú)立提交完全不受事務(wù)控制。典型錯(cuò)誤transactionTemplate.executeWithoutResult(status - { orderDao.update(order); new Thread(() - logDao.save(log)).start(); // 這個(gè) logDao.save 不在當(dāng)前事務(wù)里 });如果需要異步操作參與當(dāng)前事務(wù)必須想辦法把事務(wù)上下文傳遞過(guò)去比如 TransactionSynchronizationManager 的機(jī)制但它也只適用于少數(shù)可控線(xiàn)程池場(chǎng)景實(shí)現(xiàn)成本高。更推薦的做法是讓異步任務(wù)用自己的事務(wù)模板明確它的事務(wù)邊界不要指望它繼承外層事務(wù)。5.6 坑六事務(wù)模板被多個(gè)業(yè)務(wù)共用時(shí)屬性互相污染TransactionTemplate 實(shí)例內(nèi)部保存了事務(wù)屬性傳播行為、超時(shí)、只讀等如果有多個(gè)業(yè)務(wù)流程共用同一個(gè)實(shí)例其中一個(gè)業(yè)務(wù)修改了屬性雖然少但可能有人會(huì) set 一下其他業(yè)務(wù)也會(huì)受影響。更常見(jiàn)的是你設(shè)置了 readOnly后續(xù)的寫(xiě)操作全被拒了或者設(shè)置了超時(shí) 5 秒一個(gè)跑批業(yè)務(wù)進(jìn)來(lái)直接超時(shí)。最佳實(shí)踐是針對(duì)不同場(chǎng)景拆分模板每個(gè)模板的配置固定不要運(yùn)行時(shí)改動(dòng)屬性。如果需要?jiǎng)討B(tài)改變傳播行為可以 new 一個(gè)新的 TransactionTemplate 實(shí)例而不是去改共享實(shí)例。5.7 常見(jiàn)問(wèn)題速查表問(wèn)題表現(xiàn)可能原因排查/解決思路事務(wù)沒(méi)回滾數(shù)據(jù)成功寫(xiě)入事務(wù)管理器類(lèi)型不匹配檢查注入的事務(wù)管理器和實(shí)際數(shù)據(jù)源事務(wù)回滾了但整體沒(méi)報(bào)錯(cuò)內(nèi)部模塊 setRollbackOnly 傳播檢查嵌套調(diào)用的 propagate 配置連接池耗盡系統(tǒng)卡住長(zhǎng)事務(wù) 慢SQL / 外部調(diào)用縮短事務(wù)內(nèi)耗時(shí)配置合理超時(shí)受檢異常沒(méi)觸發(fā)回滾默認(rèn)回滾策略不含受檢異常改為 RuntimeException 或顯式 setRollbackOnly異步線(xiàn)程數(shù)據(jù)不在事務(wù)里事務(wù)上下文是線(xiàn)程綁定的異步任務(wù)用單獨(dú)模板開(kāi)事務(wù)模板屬性互相影響多個(gè)業(yè)務(wù)共用同一實(shí)例按場(chǎng)景拆分模板運(yùn)行時(shí)不改屬性6. 工具選型與設(shè)計(jì)建議讓 TransactionTemplate 發(fā)揮最大價(jià)值6.1 事務(wù)管理器的選擇與配置建議DataSourceTransactionManager 是 JDBC 場(chǎng)景的標(biāo)準(zhǔn)選擇在絕大部分單體項(xiàng)目中就夠用了。JpaTransactionManager 適合 JPA/Hibernate 項(xiàng)目它會(huì)同時(shí)管理 JPA 的 EntityManager 和底層 JDBC 連接保證兩者在同一個(gè)事務(wù)里協(xié)同。如果項(xiàng)目里同時(shí)用 MyBatis 和 JPA那可能需要兩個(gè)數(shù)據(jù)源管理器按用戶(hù)維度切分或者用 JtaTransactionManager 處理跨資源事務(wù)但后者復(fù)雜度更高。配置建議單一數(shù)據(jù)源 MyBatis/JDBCDataSourceTransactionManager單一數(shù)據(jù)源 JPA/HibernateJpaTransactionManager多數(shù)據(jù)源按數(shù)據(jù)源各配一個(gè)事務(wù)管理器業(yè)務(wù)上用路由或分布式事務(wù)方案微服務(wù)跨服務(wù)不建議直接用 TransactionTemplate 去控制分布式事務(wù)而是用 TCC、事務(wù)消息、SAGA 等專(zhuān)門(mén)方案TransactionTemplate 在其中的本地事務(wù)部分仍然可用。6.2 事務(wù)模板實(shí)例的生命周期管理在一個(gè) Spring 項(xiàng)目中TransactionTemplate 實(shí)例可以交給容器管理作為單例 Bean 使用。它本身是無(wú)狀態(tài)的屬性在構(gòu)造時(shí)設(shè)定運(yùn)行時(shí)不修改線(xiàn)程安全可以被多個(gè)線(xiàn)程并發(fā)調(diào)用。這個(gè)特性讓它可以放心地注入到任意 Service 里。如果你的服務(wù)需要不同的事務(wù)屬性組合創(chuàng)建多個(gè) Bean 是合理的方式命名上建議語(yǔ)義清晰比如 orderRequiredTemplate、auditRequiresNewTemplate、readOnlyTemplate 等。這樣代碼里一看名字就知道這個(gè)模板的語(yǔ)義比直接在調(diào)用處 new 要清晰得多。6.3 與聲明式事務(wù)的邊界設(shè)計(jì)原則在同一個(gè)項(xiàng)目里混用 TransactionTemplate 和 Transactional需要的人為約束是明確每種事務(wù)方式的管轄邊界。我的建議是三層劃分入口層Controller / API / 定時(shí)任務(wù)入口用 Transactional 做整體事務(wù)邊界一個(gè)入口對(duì)應(yīng)一個(gè)業(yè)務(wù)完整流程服務(wù)層Service內(nèi)部特殊環(huán)節(jié)用 TransactionTemplate 控制局部事務(wù)數(shù)據(jù)層Dao / Repository保持無(wú)事務(wù)語(yǔ)義事務(wù)完全由上層決定。這條邊界規(guī)則能有效避免到處開(kāi)事務(wù)的混亂。而且一旦團(tuán)隊(duì)形成這種習(xí)慣代碼評(píng)審時(shí)看到某個(gè) Service 方法里又出現(xiàn) Transactional 又要特別注意嵌套邊界就能快速判斷是否合理。6.4 測(cè)試與調(diào)試工具如何驗(yàn)證事務(wù)行為真的符合預(yù)期TransactionTemplate 寫(xiě)完之后測(cè)試驗(yàn)證是必要的。我用過(guò)的幾個(gè)比較實(shí)用的調(diào)試手段在回調(diào)里故意拋一個(gè) RuntimeException然后斷言數(shù)據(jù)庫(kù)數(shù)據(jù)沒(méi)有變化驗(yàn)證回滾路徑在回調(diào)里調(diào)用 setRollbackOnly 后正常返回?cái)嘌詳?shù)據(jù)沒(méi)有落庫(kù)驗(yàn)證邏輯回滾路徑配置一個(gè)極短超時(shí)觀(guān)察超時(shí)之后事務(wù)狀態(tài)是否被正確清理用日志輸出當(dāng)前線(xiàn)程的事務(wù)狀態(tài)TransactionSynchronizationManager.isActualTransactionActive()、isCurrentTransactionReadOnly() 可以快速確認(rèn)當(dāng)前是否真的在一個(gè)物理事務(wù)里。這些測(cè)試不需要復(fù)雜的框架寫(xiě)在集成測(cè)試?yán)锞湍茉缫稽c(diǎn)發(fā)現(xiàn)問(wèn)題尤其是那些沒(méi)有異常但數(shù)據(jù)沒(méi)落庫(kù)的隱蔽坑靠這個(gè)能提前暴露。7. 最后再分享一點(diǎn)個(gè)人經(jīng)驗(yàn)坦率地說(shuō)TransactionTemplate 并不是一個(gè)炫技的技術(shù)點(diǎn)它的價(jià)值在于讓事務(wù)控制回歸到開(kāi)發(fā)者手里。如果你所在的團(tuán)隊(duì)經(jīng)常被 Transactional 的邊界問(wèn)題困擾、批量任務(wù)經(jīng)常因一條臟數(shù)據(jù)拖垮整個(gè)事務(wù)、異步場(chǎng)景里事務(wù)上下文總是丟來(lái)丟去那么花時(shí)間把 TransactionTemplate 用熟回報(bào)率會(huì)非常高。我個(gè)人在項(xiàng)目中偏向一套固定的寫(xiě)法事務(wù)模板全部由 Spring 容器管理并按業(yè)務(wù)命名回調(diào)方法命名以 doXxxInTransaction 開(kāi)頭異常處理盡量在回調(diào)內(nèi)部消化并返回業(yè)務(wù)結(jié)果實(shí)在要拋出異常就顯式 setRollbackOnly 或者繼承 RuntimeException。這套規(guī)則一旦跑習(xí)慣事務(wù)相關(guān)的代碼不僅穩(wěn)定而且一年后回來(lái)翻代碼還能一眼看明白當(dāng)初為什么要這么寫(xiě)。最后再提一個(gè)小技巧如果團(tuán)隊(duì)里有人總是分不清一個(gè)事務(wù)到底是提交了還是回滾了可以在回調(diào)末尾打印一下 TransactionSynchronizationManager.getCurrentTransactionName() 和當(dāng)前事務(wù)的 rollbackOnly 狀態(tài)。這兩行日志在很多疑難問(wèn)題上會(huì)節(jié)省大量排查時(shí)間。
返回列表
PREV
查看更多資訊
NEXT
返回資訊列表
性色av一区二区| 小视频玖玖| 日本一二区免费| 久久久久久久伊人精品| 亚洲啪啪视频免费| 久久青娱乐| jiujiujiujingpin| xxx亚洲午夜天堂| 国产精品一区二区黄片| 91精品国产日韩欧美综合| 亚洲欧美洲综合| 久久久久网站-538在线视频-欧美永久乱码 | 亚洲第一页第二页激情| 九热中文字幕| 日韩内| 亚洲综合情色| 91视频成人福利网站在线一区 | 亚洲美女高潮喷水视频| 夜夜嗨老熟女AV一区二区三区| 日本天天人人狠狠在线日美女 | 97久久国产亚洲精品超碰热| 丝袜av一区二区三区| 国产一区二区三区中文字幕| 日日狠狠久久偷偷色综合免费| 日韩性爱电影一区| 国产成人免费观看在线视频| 五月天黄色激情视频| 亚欧毛片基地国产毛片基地| 国产在线激情视频| 约操熟妇| Blackedraw视频一区二区| 国产亚洲日韩欧| 精品一区二区三区国产| 国产精品久久| 性夜影院爽黄A爽免费动漫| 亚洲女人91| 成人资源中文字幕在线观看| 香蕉精品二区二区| 青青草好吊色| 超碰午夜| 在线综合 亚洲 欧美中文字幕| 欧美精品宗合| 日本精品中文字幕视频| Blackedraw视频一区二区| 欧美日韩日产免费网站看| 99久久久无码精品国产人| 国产精品无码成人精品| 99热官网| 99re3这里只有精品| 台湾佬中文娱乐自偷自拍| 北条麻妃99精品青青久久| 9久久9综合| 午夜久久一区二区无码中出| 人人澡人人干| 97超碰美女| 亚洲在线网站| av日韩在线观看电影| 亚洲高清无码免费观看视频| 亚洲精品九九九| 久久精品黄色| 欧美日韩香蕉| 亚洲综合113页| 日本不卡二三区| 日韩大香蕉精品在线视频| 青青青青青手机视频| 又粗又长又大国产不卡| 亚洲超碰AV| 综合激情五月丁香| 欧美情色男人的天堂| 亚洲极品| 国产人妖的免费的视频| 日韩av三四区| 男人的天堂VA| 熟女字幕| 曰本91情色| 97精品视频| 国产性爱在线视频一区二区| 欧美日韩性感| 欧美亚洲清纯| 影音先锋每日最新资源在线观看 | 亚州国产精品乱| 亚洲国产剧情少妇激情| 亚洲人妻在线一区| 欧美日韩国第一区| 久啪视频| 中国少妇XXXX做受| 婷婷激情一区二区三区俺也去| 亚洲性少妇| 人人妻人人爱人人玩| 色婷婷视频| 九九英色视频| 少妇精品久久| 狠狠穞A片一區二區三區| 啊啊啊啊啊啊啊在线| 黄色大片视频在线免费看| 久久亚洲AV成人精品无码| 国产suv精品一区二区四| 九九九九精品在线| 91在线视频免费播放| 国产无遮挡| 亚洲高清色综合| 国产黄色影片在线观看| 国产性感在线观看| 91亚洲欧美| 久久久久9| 日韩精品国模| 青娱乐国产精品| 日韩欧亚太美不卡| 神马九九| 亚洲综合另类欧美久久久| 97免费在线观看| 1769精品一区二区三区| 91少妇通奸网站| 97人人操人人摸| 国产精品视频电影| 色女女女导航| 中文字幕高清精品一区| 国产农村妇女精品一| 欧美日韩在线视频网站| 老师充足的奶水小说| 99热这里只有精品99| 色一区二区三区综合| 人人操人人操人人人操| 日日骚精品视频| 日韩亚洲国产视频| …中文字幕亚洲乱,97人妻无码费视… | 亚洲一区中文精品| 一区二区 电影 亚洲| 日韩免费福利在线观看| 欧美日韩国产人人| 亚洲av乱伦色图网站| 大香蕉92| 91N欧美| WWW.操逼.COM| 91欧美性| 国产精品一二三免费网站| 神马麻豆福利院| 日韩三级在线观看mp4| 日韩高清黄片| com 首页 18岁 禁区 女优 免费 精选 同城| 国产麻豆福利av在线播放| 国产情色在线| 黑丝少妇| 十八禁视频一区二区| 九九九九九九视频| 强奸乱亚洲| 97精品免费视频网站| 一二三啪啪专区| 国内毛片国产欧美拍| 婷婷精品国产欧美精品亚洲人人爽| 国产又粗又长又爽又色| 久久双插| 亚洲丝袜二区在线| 岛国AV一区二区电影| 中文字幕亚洲永久精品| 青青操国产夫妻| 精品少妇一区二区| 国产超碰在线一区| 亚洲激情网| 久久久啊啊啊| 欧美性爱第一页久久| 亚洲九月丁香| www.久久制服糖| 婷婷久草| 欧美 传媒 麻豆 日韩 偷拍| 亚洲日本天堂| 91在线免费观看处女| 约操熟妇| 亚洲欧美另类激情小说| 蜜桃臀一区二区三区久久| 观看免费区二区三区二| 97资源久久| 久久久久久波多野吉衣高潮| 99热网站| 婷婷五月天基地| 五十路六十路七十路熟婆| 草草草视频| 91欧美另类| 好吊色在线观看| 亚洲精品无码少妇久久| 熟妇的味道HD中文字幕| 日韩性爱免费观看视频| 久久精品中文字幕观看| 国产成人超碰在线| 久久久四区| 9久9久| 日本五十路在线| 亚洲伊人久久精品狠狠在线| 色综合一区二区三巨| 色爱国产| 亚洲欧美国产日本一区二区三区| 精品偷拍13p欧美dodk视频| 日韩激情电影中文字幕| 欧美精品二区视频在线| 蜜臀久久99精品久久久久久无删减| 91老熟女91老女人| 69一区二区三区| 99热精品在线观看| 国内毛片四区| 易易A毛视频| 97久久国产精品| 黄久在线| 激情四射婷婷六月天| 热久日综合| 黄色不卡视频| 色网1| 五月天亚洲网| 国产精品高朝久久久久久久| 啪啪啪综合网| 日本精品第一视频在'| 秋霞无码av鲁丝片一区| 中文字幕丝袜国产第一页不卡| 亚洲一区二区三区不卡国产欧美| 亚洲小电影免费涩涩成人在线高清| 超清福利精品视频在线| 九久久精品| 久艾草在线精品视频在线观看| 成人精品在线免费视频| a亚洲欧美色欲| 精品国产一区探花在线观看| 久久久久久人妻一区精品色欧美| 一级特级aaaa毛片免费观看| 色五月婷婷中文字幕| 五月婷婷丁香| 99抽插| 国产精品第一页国产大屁股视频免费区i | 亭亭在线资源| 午夜男女爽爽大片免费观看| 秋霞成人做爱| 色狠狠色| 久久精品免视看国产成人﹣蜜臀av一区. 久久精品免视看国产成人,蜜臀av一区 | 狠狠操使劲操| 性爱Av免费| 激情视屏国产乱伦强奸| 嗯嗯啊啊视频在线看| 久久熟女人| 人妻人人澡人人爽人人| 久久9免费视频| 日韩一级二级三级免费看完整版| 国产又黄又粗的视频| 岛国网址国产| 精品国产乱码久久久久久久久1| 亚洲精品久久久久久久久豆丁网| 视频黄站| av在线播放国产一区| 国产AV久久久蜜爱影集| 亚洲人妻熟妇三十三区| 少妇熟女视频一区二区三区| 欧美福利视频啊啊啊啊 | 国产91丝袜在线播放蜜月| 久久久久国产精品久久久| 一级AV性爱| 超碰九色| 久久亚洲天堂| 欧美偷拍区| 五月激情啪啪| 婷婷五月天无码| 极品少妇久久久| 精品九九国产无码| 狠狠搞 亚洲91| 99热伊人| 福利在线黄片| 蜜臀亚洲综合一二三四区| 九t超碰| 国产av强奸美女| 亚州情色j区| 国产精品午夜精品| 国产高清精品一区二区三区毛片| 久久色人体 | 99精品视频在线观看免费| 白丝1区2区3区| 五月天精品| 久操网无码在线| 91色黑人少妇| 一起草高清无码| 国产日本久久免费精品| 国产诱惑| 日本www操操操| 国产亚洲日本| 日韩av影片在线观看| 亚洲精品一区二区免费在线观看| 老外又粗又长一晚做五次| 夜夜操天| 黄片免费视频2019| 国模不卡一本二本三电影| 亚欧美色| 亚洲色图超碰在线| 亚州性色| 婷婷五月天补不补| 国产老太乱伦一区| 欧美黄页在线| 亚洲男人电影天堂| 人人妻人人操人人乐| 蜜臀久久99精品久久久久久-DVD| 国产亚洲人妻综合日韩 久久| 97久久超碰亚洲| 99久久精品国产高潮| 久久久久幕乱码| 小情侣高清国产在线视频| 亚洲国产中文字幕| 国产超碰| 天天伊人| 97色97好| 性爱乱伦一区| 成人性交免费视频| 亚洲熟妇无码一区二区三区| 日本女优在线视频福利| 国产黑白丝在线| 成片免费观看视频大全| 成人a v在线播放免费| 97精品综合久久网| 男人兔费天堂| 日日摸夜夜夜夜爽| 欧美综合天堂| 亚洲国产综合久久久性感熟妇| 丁香五六月啪啪| 国产精品欧美激在线| 久久人人爽爽人人爽人人片αV| 加勒比人妻综合| 夜夜嗨绯色| 亚洲另类小说卡通动漫| 欧美色老汉| 久久久久久久极品香蕉视频| 日韩欧美aⅴ综合网站发布| 激情内射| 亚洲情色1区| 天天综和| 操91| 亚洲图片激情综合另类| 91色插| 欧美日韩大陆黑人少妇99| 成人免费在线网站| 天天谢天天干| 超碰99在线| 美欧老女人97| 殴美,日韩国产伦精品| 欧美大香蕉久| 97色综合中文网| 噜噜噜亚洲精品| 欧美拳交在线播放| 无码国产精品午夜不卡(| 色综合V| 国产熟女一区二区| 99热线麻豆| 婷婷色香| 加勒比无码一区二区三区| 奇米四色影视777久久久| 亚洲色图殴美色图激情乱伦| 26UUU欧美日本| 精品少妇一区二区| 中文字幕人乱码中文字的预防方法 | 免费a v| 四虎影视国产精品| 欲色啪| 一区二区三区日韩欧美| 91熟女熟妇视频网站| 久久激情亚洲精品无码?V| 色吧 综合| 精品传媒在线一区| 欧美激情 亚洲色图| 久久草视频污视频| 欧美少妇性乱| 日本97久久久精品| 啊啊啊 在线观看| 狠狠干,狠狠操| 亚洲强奸乱伦影视网| 欧美一区二区一级岛国大片| 天天看天天干| 国产无码久久高清| 亚洲中文字幕精品一区| 国产强奸无码乱伦| 激情熟女12P| 亚洲男人天堂网| 东京热男人天堂| 久久9精品| 天天影视综合色| 超碰偷拍| 久久激情视频| 台湾佬大香蕉| 男人天堂新在线| 91久| 97在线免费观看| 九热中文字幕| 中文久久| 影音先锋少妇| 久草网站免费在线观看| 欧美三级中文字幕hd| 亚洲精品一区二区三区在线播放| 久久九九国产精品| 欧美性爱十八禁| 国产女生在线| 无码99| 少妇高潮99p| 色欧美天天| 欧美熟女少妇| 天天插夜夜爽| 96一区二区| 麻豆国产97在线| www.91人妻.com| 国产大学生口爆吞精合集| 黄页视频网站野外| 激情久久久| 99中出在线| 99九九久久| Aa东京男人的天堂| 美女黄站| 国产av又色又爽又黄| 久久亚洲AV成人精品无码| 偷看洗澡一二三区美女| 蜜桃精品一区二区三区ww | 国产高清免费不卡av| 99精品成人免费看| 亚洲色图久久成人| 97精品在线| 黄色小视频日本txt| 国产 v乱码一区二| 国产自产91区13区| 智利AV在线网| 久久只有精品| 日韩精品1区2区中文字幕| 欧美 亚洲精品首页| 曰韩人妻中文字幕在线 | 超碰这里有精品| 中文字幕乱亚洲美女精品一区| 乱伦熟女专区| 少妇滛荡视频| WWW.加勒比人妻一区不卡.com| 欧美激情黑人| 操操操五月天婷婷丁香影院| 国产成人自拍视频在线| 老熟女区| 精品免费一区二区三区在线亚洲人成| 国产精品午夜福利视频| 97精品| 欧美人人AAA| 一本正道久久熟女| 日韩无码一区二区三区| 91精品国产麻豆国产自产在| 美女91在线观看| 60秒不遮不挡| 熟女人妻一区二区三区| 欧美成人综合| 国产资源中文字幕在线 | 火箭成精品视频884必出精品| 精品成人亚洲午夜电影| 超碰爽人妻熟女Av| 亚洲女人91| 欧美日韩国内不卡| 亚洲青色欧美| 51国产午夜精品视频| 小泽玛利亚一二三| 中文字幕视频在线观看| 色综合九九| 自拍亚洲综合| 思思热久久成人| 精品十三区| 成年无码动漫av片无尽在线 | 欧美夜色| 91精品微拍福利| GVH-003 母子姦 青木玲-麻豆视频,麻豆视传媒短视频网站入口,麻豆视传媒官网直 | 亚洲国产精品成人久久蜜臀| 天天摸,夜夜摸| 97操碰| 成年女人18级毛片毛片免费观看| 国产女主播视频在线观看| av在线一区二区三区| 91国产美女丝袜足交精品视频| 亚洲AV成人精品网站在AV| http://qxhbdz.com| 91n欧美| 91麻豆天美国产欧美| 老熟女91视频| 无码91| 麻豆综合一区av| 黄色二级片网站| 亚洲春色一区二区三区| 久久国产精品,久久国产| 伊人网av| WWW.操逼.COM| h色99999| 午夜免费福利视频一区| 日韩亚洲欧美中文字幕| 青青操在线亚洲视频观看欧美在线| 黄人人操人人操| 欧美色图成人网一区二区 | 五月丁香综合网| 91美女视频在线观看| 啊啊啊啊啊啊啊国| 岛国片在线视频网站| 老女人老91妇女老热女| 大地资源在线观看中文第二页| 久久久96精品| 久久国产三区| 国产欧美精选自拍一区| 四虎精品永久在线观看| 后入 亚洲 美女 射| 一个国产在线综合网站| 超碰 欧美| 亚州操操穴网| 日韩一性一交一A片俄罗斯 | 尤物av网站| 韩国午夜理伦三级好看| 精品69网| 亚洲人精品久久久| 伊人五月天青青草婷婷| 人人做人人妻人人夜视频| 香蕉99秘 一区精品蜜桃臀| 日日夜夜国产综合| 99九九精品| 1204av韩国| 激情久久av一区av二区av| 3PAV乱伦视频| 激情婷婷丁香网| 一本正道久久熟女| 日韩精品一区二区三区色欲| 强奸乱伦免费网站| 久久精品国产亚洲AV片多多| 97干在线视频| 欧美亚洲清纯| 亚洲在高跟鞋自慰久久在色线| 丁香五月久久| 国产区性爱在线视频秋霞豆 | 黄色成品网站| 人妻久久久| 中日韩久久久| 亚洲伊人久久综合97| 夜夜国自区| 色偷偷色偷偷欧美日韩| 日本韩国一本产品小视频日本韩国一本产品久久久产品小视频日本韩国一本产品久 | 性色一线| 99热精品在线| 久久亚洲婷婷| 操婷婷逼| 日韩一性一交一A片俄罗斯| 日韩欧美大片免费高清啪啪| 亚洲久久久| 超碰97.com| 97免费视频在线观看视频| 国产性感在线观看| 暖暖精品二区三区观看| 中文伊人大香蕉视频| 丁香五月性| 26uuu国产免费观看| 丁香六月激情| 无码国产Av| 亚洲91极品| 日本午夜久久电影| 你懂得91| 欧美色五月| 日韩无码专区| 亚洲av无码成人精品国产| 人人干人人搞人人摸| 久久夜黄色无码A级大片| 伊人精品久久网站| 日韩一级特黄av毛片| 欧美国产欧美在线观看| 一本大道不卡一二三区| 日韩精品1区2区中文字幕| 口爆吞精在线观看| aⅴ日韩成人电影av在线免费看av大全 | 五月天黄色激情视频| 日本操BAV| 亚洲精品日日夜夜52| 亚洲aV无码成人在线观看| 亚洲免费人妻在| 一区二区娱乐网站| 日产狠狠干| 99久久这里只有精品| 日韩免费三级黄片电影| 亚洲色图欧美另类在线| 国产精品岛国片在线观看| 日韩熟女无码| AV一区观看| 国产精品呦一区二区三区| 日韩乱中文| 成人三级片无码| 国产精品成人在线| 99re这里只有精品中心播放| 国产偷仑| 自拍第一页| 精品无吗m| 欧美高清18A片| 久久无码成人| 五月丁香成人网| 天堂资源欧美| 九九人妻| 五十路三区在线| 久久精品 六十路 熟女 欧美| 成人免费看吃奶视频网站| 在线国产一区二区av| 激情五月天婷婷| av大香蕉网站| 又黄又硬又粗又长国产视频| 国产 亚洲 一二三四| 天天做日日爱夜夜爽| 夜夜爽爽爽| 国产噜噜噜噜噜久久久久久久久| 日韩精品午夜操呦呦不卡影院| 秋霞一集毛片观看| 一区二区三区视频在线观看免费| 九九久久一区二区三区| 无码人妻毛片丰满熟妇精品区| 天天综合网~91| 亚洲欧洲综合成人av一区| 超碰在线看| 天天综合网1| 久久久人妻| 黄色小视频日本txt| 日韩亚洲美女一区久久| 午夜福利一区二区三区四区五区色婷婷| 人妻少妇精品视频一区二区三区| 极品尤物自安慰| 九九热免费国产视频婷婷伊人五月| 98福利在线视频| 亚洲婷婷丁香在线| 中文字幕视频2区| 天天干夜夜操一区二区| 日韩欧美午夜一区二区| 91 手机在线播放 绯色| 久久后入制服| 日韩一性一交一A片俄罗斯| 日韩AV中文字幕电影| 久久久偷拍| 免费看污网站| 亚洲 在线| 天天肏夜夜肏| 亚洲熟女少妇免费视频| 国产女人高潮嗷嗷嗷叫小说| 亚洲少妇诱惑| 欧美欧美啪啪视频| 欧美日韩丝袜| 啊啊啊爽爽| 五月天婷婷成人网| 国产伊人精品在线| 岛国片在线观看视频亚洲| 天堂资源站| 这里只有精品视频在线观看麻豆| 男女91| 久久国产成人精品国产成人亚洲| 国产九月婷婷| 国产精品网址| 91蜜臀熟女| 诱惑网综合| 亚洲色图在线视频| 男人午夜天堂| 亚洲精品一二三四区| 中国一级αV| 中文字幕视频在线观看一区二区| 精品免费视频国产一区| 国产夜夜操| 天天躁日日躁成人字幕aⅴ| 日韩超碰精品综合| 巨爆乳一区二区爆乳区| 亚洲男人的天堂在线看| 午夜福利无毒不卡| 亚洲视频二区| 国产乱伦一二三区| 大香蕉人妻久久| 超碰久热| 中文?日韩?免费?精品| 国产性感在线观看| 综合久久久久久久综合网| 国产第二页| 亚洲性爱无码乱伦av| 美女天天干| 理论久久婷婷网 8| 国产第12页| 欧美激色| 日韩精品99999| 不卡av免费在线网址| 亚洲视频小说| 夜夜草网站| 男啪女色黄无遮挡免费观看| 国产精品久久久九九九| www.久久99| 一本色道综合久久欧美| 亚洲人人夜夜澡人人爽| 亚洲午夜免费狠狠干| 亚洲高清无毛一区二区| 人妻喷水| 精品久久久久黄少妇| 蜜乳性色无码专日粉嫩骚逼AV| 青青五月天| 少妇69中文| 91精品黄在线观看| 成人午夜高潮av猛片| 九草九九九| 欧美激情亚洲色图| 性爱乱伦一区| 乱伦日本中文自拍| 欧美图片校园春色| 亚州春色| 国产精品一区二区三区在线密挑| 无遮挡一级毛片视频免费的| 九九热只有精品| 绯色AV粉色AV蜜臀AV| 精品v日韩欧美国产| 懂色av一区二区三区天美传媒| 狠狠躁天天躁日日躁| 久久久久成人蜜桃精品| 亚洲暴力强奸AV| 天天躁日日躁狠狠躁| 精品久操| 欧美中文狠| 久久久999日本大片| 亚洲精品一二区| 精品对白久久不卡| 我中文字幕6区| 色婷婷激情| 欧美1区二区三区公司| 农村少妇久久久久久久| 超碰在线一区二区三区| 蜜臀一二三区| 超碰98综合网| 中文字幕乱碼在线| 五月综合视频| 丁香婷婷啪啪| 亚洲中文日韩精品| 插穴性爱视频在线观看| 国产精品成人AV片免费看网站| 欧美激情高清性猛交| 91处女在线视频| 久久免费精品96| 9超碰免费| 久久久久中出| 大香蕉伊人亚洲| 男人的天堂2010| 国产亚洲在线观看| 国产成人无码网站在线视频| 国产 亚洲 丝袜 制服| 东北少妇高潮zzzz| 亚洲婷婷丁香在线| 七月丁香婷婷| 欧美国产精品久久九九| 久草在线| 亚洲国产综合图区中文字幕| 91丝袜视频在线观看| 青青草在线视频播放器| 成人草草视频| www.成人无码| 精品国产综合久久福利,热99这里有精品综合久久,99热这里只有免费国产精品,精 | 91一区二区| 色爱综合网| 中文字幕在线24| 91爽啪| 午夜精品99久久久久传媒| 四虎 精品 WWW| 亚洲影院小综合| 久久 亚洲 日韩 人妻| 秋霞午夜视频一区二区| 中文久久96| 超碰在线综合97| 婷婷国产精品一区二区| 亚州色综合| 日韩精品字幕| 国产亚洲精品美女久久久久久2021| 久久精品中文字幕无码l| 中国国国产一级特黄毛片| 99抽插| 亚洲精品蜜桃久久久久久久| 久久精品成人一区二区三区蜜臀| 久久久久幕乱码| 欧美日韩欧美| 色综合V| 中文久久爆乳| 九九热免费国产视频婷婷伊人五月 | 日本日逼视频网| 欧美极品女人的天堂| 亚洲天堂另类小说男人| 欧美亚洲厕所精品偷拍91 | 激情综合五月天| AV天堂电影网| 狠狠干妹子| 97日韩欧美| 一区二区影院| 久久国产三区| 校园春色综合香蕉| 亚洲精品少妇| 二男一女成人A片| www.夜夜操| 性色av一区二区| 国产美女自拍视频| gogogo免费高清看中国国语 | 久草综合网| 久久久国产av美女私房| 欧美久久伊人| 亚洲情色五月天 | 久久99久久99精品天美传媒棢·纸:. | 欧美综合91| 色色福利| 殴美,日韩国产伦精品| 亚洲999综合| 婷婷五月天色色| 亚洲网自拍| 国产人妻久久精品一区二区三区| 亚洲无码太久| 午夜精品久久久久久久男人的天堂 | 精品久久久久瑟瑟| 久久久天堂| 国产久久av| 欧美一二三区四五区| 老熟女综合网| 黑人操一区二区| 啊啊啊啊啊在线视频| juliaann欧美丝袜办公室| 一级特级aaaa毛片免费观看| 另类亚洲图色| 免费国产视频| 能看的AV| 国产97在线播放| 国产91啪| 一牛一区二区三区久久| 精品一级毛片在线观看| 激情文学网伊人| 精品美女人人干| 中国少妇XXXX做受| 丝袜综合色图| 国产主播福利| 美女露胸露尿口| 神马午夜久久| 日本三级中国三级99人妇网站| 啊啊啊啊好疼视频| 秋霞久久亚洲精品成人| 综合亚洲情色| 成人自拍三级在线观看| 亚洲人在线| 日韩免费a级毛片无码a∨| 亚洲色天堂日韩中| 亚洲欧美黄| 欧美夜夜狠| 亚洲图片欧美色| 中文字幕五区| 国内伊人久久久久久网站视频| 97色婷婷| 91人人| 狼人综合婷婷激情四射| 日本精品五区| 日韩二级| 第四色奇米影视777| 日本不卡二三区| 精品午夜福利国产一区二区在线观看 | 锕锕好爽 死我在线观看| 蜜乳AV一区| 日本免费一区二区不卡| www…国产操逼| 日韩本不卡视频在线观看| 操一区| 夜夜爽夜夜爽| 亚洲情色一区三区| 色色色色日本| 91天堂丝袜美腿| 午夜免费视频1000| 伊人天天久久动态图| 色婷婷综合久久久久中文一区二区 | 六六久久日韩不卡| 欧美精品精品一区二区| 蜜臀AV成人精品蜜臀| 操逼片中文| 中文字幕丝袜| 蜜臀在线免费观看在线免费观看| 日韩性爱1级片视频| 国产精品乱码久久久久久久| 六月天婷婷| 最新亚洲黄色免费电影| 偷拍超碰| 色官网在线| 久久少妇| 日韩操呦呦影院在线观看| 少妇滛荡视频| 国产精品午夜AV完会免费 | 肏逼福利网站| 99re热| 一区二区偷拍拍视频| 国产精品一区二区亚洲人成毛片| 婷婷久草| 久都青青视频| 国产三区免费在线观看| 后入式免费视频| 九九九九九用不成了| 色婷五月| 国产性爱在线视频一区二区| 精品人妻一区二区三区不卡断| av操操不卡| 亚洲欧美精品福利在线| 91综合色噜噜| 亚洲熟女乱综合一区二区在线-...亚洲国产日韩欧美一区二区三区,久久久久久精 | 东京热男人的天堂精品| 99re超碰| 国产操逼视频在线观看| 大干人妻| 三级网站超变态精品| 九九热精品| 老司机深夜影院18未满| 老熟妇乱轮| 91n处女在线观看| 人妻免费观看| 蜜桃中文字日产乱幕4区| 91精品大奶人妻| 国产成年免费大片黄在线观看| 亚洲高清综合网| 精品无码产区一区二| 大香蕉天天看妹子| 亚洲精品丝袜-不卡成人免费…… 久久久久成人蜜桃精品 | 无码自拍SM| 日日夜夜国产综合| 爽爽淫人网| 欧美日韩国产色图在线| 中国一级操逼视频| 台湾一区国产高清在线| 欧美人妻少妇| 成人草草视频| 大香蕉92| 91男人天堂网| 日韩中文字幕2020| 一及黄久一点| 丰满人妻一区二区三区在线| 资源在线观一 二| 日本3级一区二区免费| 日韩一区二区三区四区五区| 秋霞成人一级在线观看| 麻豆视频国产一区二区| 神马九九| 日韩三级伊人| 91五十路| 亚洲毛片基地专区| 啊啊啊啊好爽好舒服一区二区易域| 91欧美少妇| 骚妻少妇精品性色无码四色A V| 小骚逼被操的爽不爽| 青青草乱入乱欲视频在线观看| 啊啊啊啊啊操我视频| 久久综合18p| 亚欧美天堂在线| 91在线限制级| 精品97精品97| 97看操| 狠色婷婷久久一区二区三区_| 精品久久大胆人体| 98福利在线视频| 97免费视频在线观看视频| 熟妇激情| 蜜区区视频79 | 国产JDAV无码视频在线观看| 亚洲春色欧美激情自拍| 91丨九色丨国产丨人妻在线| 国产高清免费不卡av| 黄页视频网站野外| 亚卅熟女乱色| 人妻久久久久久| 日韩美一区| av午夜玫瑰| 亚洲色图自拍| 午夜噜噜噜| 上特色A在线| 爱妃国产亚洲视频中文字幕| AV和黑人在线播放| 国语av最新自产拍在线观看| 偷窥自拍亚洲天堂网爆| 熟妇人妻精品一区二区视频色欲| 欧美少妇大量自拍视频在线观看| AV高清一区| 玖玖爱伊人玖玖爱| 91色插| 中文字幕精品一区二| 亚洲第一黄色av网站| 九色婷婷| 欧色综合| 国产又长又大又粗的视频| 天天影视亚洲| 天天做日日做| wwwss在线观看| 蜜臀久久99精品久久久久免费观| 久污| dy888午夜老子影视达达兔| 高清国产成人无码| 中文字幕在线观看视频www| 亚洲中文字幕乱码无码一区二区| 日韩无码AB| 亚洲色人阁| 国产对白刺激视频| 丁香五月婷婷啪啪| 九一精品牛牛一区二区| 久久久一区二区三区麻豆| 91露脸熟女专区| 99婷婷| 久久伦理视频久久大香蕉视频| 久久αⅴ| 中文字幕亚洲热播人妻| 天天综合AV| 青青草原成人| 免费福利视频中文字幕| 丁香六月婷婷久久综合| 人妻无码久久一区二区三区免费| 国产久久天堂资源| 嗯啊抽插大香蕉网页| 偷窥自拍亚洲色图| 欧美96交| 亚洲精品xxx| 人妻精品综合中文字幕在线| 99视频自拍区| 狠狠操夜夜| 中文一区二区婷婷视频| 97欧美色| 秋霞久久亚洲精品成人| 爱干爱射网啊啊啊| 伊人91| 日本三级韩三级99久久| 蘋果手機免費看成人Av| 国产无码成人无码| 激情久久久| 亚洲狠狠入| 欧美91在线+|+欧美| 青青草久草| 日本三级韩三级99久久| 久湿久久 | 99热自拍| 午夜九九| 黄色欧美性爱视频| 夜夜操一区二区| 国产亚洲精品激情| 中文字幕88av在线| 国产一区二区三区高清视频| 人人色97| 日韩97P| 东京热亚洲一区二区| 天天干夜夜鈤| 丁香五月激情婷婷| 夜夜夜夜久久久久| 91操操| 亚洲欧美黄| 嗯啊不要啊在线 | 青青青国产| 三上悠亚在线毛片91| 伊色综合天堂色97| 国产9区| 熟妇人妻一区二区三区| 天堂v无码免费视频| 欧美 中文字幕 一区| 精品无码久久久久久久杏吧| 超碰久久性爱| 免费精品国偷自产在线在线 | 黄片国产精品一区二区| 少妇内射www在线观看视频| 欧美另类精品xxxx| 色九九综合AV| 欧美视频中文字幕区| 97超级久久| 欧美乱色| 人人干黄色| 久久99精品国产| 亚洲成熟国产精品美女| 青青青草原| 污啪啪啪视频| 日韩成人无码| 中 文字幕一区二区三四 五 区日 日 骚| 欧美特大AA级黄片| 久久成人午夜狠狠| 欧美日综合| 91色图| 蜜桃视频一区二区三区| 日韩精品视频在线观看一卡二卡| 97久久超碰日韩精品| 99少妇精品视频| 亚洲性爱电影| nuu12国产麻豆精品| 青青草日韩无码| 伊人专区一区二区三区| 欧美日韩人妻婷婷一区| 欧美成va视频网站| 日本久久女同性恋视频| 日韩精品作爱导航| 99热欧美| {男男暴菊gay无套网站| 9997se| 亚洲熟妇乱女区二区三区| 精品中文字幕一区二区| 日韩本不卡视频在线观看| 夜夜操天天肏| 日韩av在线播放不卡| 色五月AV| 国产后入清纯| 老女人爆菊| 国产麻豆福利av在线播放| 狼人狠干| 欧美久久久| 国产人妻精品久久久一区二区三区| 伊人精品久久网站| 啊v在线观看视频| 一本久久精品中文字| 亚洲丝袜二区| 欧洲成人性爱视频| 亚洲欧美不卡线| 天天操天天7| 日本人妻伦在线中文字幕| 人妻少妇无码 | 日韩一级欧美一级国产一级台湾| 啊啊啊不要嗯嗯在线观看| www.97在线| 激情久久av一区av二区av| 人人妻人人爽 97人人看碰人免费公开视频| 女人18精品一区二区三区| 丝袜综合| 青草青青久久久久久国产| 午夜精品久久久久久久99蜜桃一| WWW.操逼.COM| 91色欧美| 十八禁电影伊人网| 男人的天堂VA在线| 精品无码一区二区| 开心五月婷婷| 熟妇人妻一区二区三在线 | 亚洲毛片基地专区| av天堂加勒比| 锕锕好爽 死我在线观看| 草蕉影视亚洲无码| 试看60秒| 水滴偷拍| 蜜桃精品视频一区二区三区| 91色噜噜狠狠| 97干日韩| 极品色社| 色欧美天天| 欧美色色色| 超碰免费人人| 亚洲欧美国产成人综合不卡| 国产欧美精选自拍一区| 无码操逼视频一下| 97超碰日韩| 伊人网高清| 97av在线观看| 97国产精品一区二区传媒公司| 蜜桃AV天堂| 久久久久深夜无码| 欧美黑人168页欧美黑人167| 亚洲drav色图| 精吧天堂| 精品国产乱码久久久久久日本公司| 欧美夜色| 蜜桃视频一区二区三区 | 26uuu国产亚洲综合| 视频一区二区免费在线| 亲子敌伦对白在线播放| 国产色产精品在线观看| 国产精品女aA片爽爽视频| a级免费在线观看| 日韩av在线精品观看| 日本性感人妻91| 亚洲精品乱码线路中文字幕| 操逼逼一区视频| 中文字幕文字幕无码一区二区三区电影99| 亚洲情色1区| 激情欧美日韩女同久久| 97在线观视频免费观看| 一级黄碟| 无码二级三级| 夜夜精品视频| 精品久久无码午夜福利| 免费精品中文字幕| 国产又粗又长又爽又色| 能看的AV|