1.引
上一節(jié)分析了嵌套事物的創(chuàng)建,本節(jié)分析每種傳播特性的回滾處理過(guò)程。由于這一部分的組合情況會(huì)很多琅摩,我們只分析其中的一兩種情況,更多的大家還是要多看源碼锭硼、多測(cè)試房资! 注意:這里最外層的事物一定要開(kāi)啟,如果將最外層的事物特性設(shè)置為PROPAGATION_NOT_SUPPORTED檀头,則不會(huì)引發(fā)嵌套事物的問(wèn)題志膀。
2.processRollback回顧
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
// 1.事物完成之前的觸發(fā)器調(diào)用
triggerBeforeCompletion(status);
// 2.如果有保存點(diǎn),則調(diào)用rollbackToHeldSavepoint回滾到保存點(diǎn)
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
status.rollbackToHeldSavepoint();
}
// 3.如果當(dāng)前事物是一個(gè)新的事物,則調(diào)用doRollback執(zhí)行給定事物的回滾
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
doRollback(status);
}
else {
// Participating in larger transaction
// 4.如果當(dāng)前事物并非獨(dú)立事物,則將當(dāng)前事物的rollbackOnly屬性標(biāo)記為true,等到事物鏈完成之后,一起執(zhí)行回滾
// 如果當(dāng)前存在事物,但是
// 事物的rollbackOnly屬性已經(jīng)被標(biāo)記為true
// 或者globalRollbackOnParticipationFailure(返回是否僅在參與事務(wù)失敗后才將現(xiàn)有事務(wù)全局標(biāo)記為回滾)為true
if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
System.out.println("==當(dāng)前事物并非獨(dú)立事物,且RollbackOnly為true\n");
// 則將ConnectionHolder中的rollbackOnly標(biāo)記為true
doSetRollbackOnly(status);
}
else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
}
// 5.如果當(dāng)前不存在事物,則不會(huì)回滾
// 例如配置了 @Transactional(propagation = Propagation.NOT_SUPPORTED)
else {
logger.debug("Should roll back transaction but cannot - no transaction available");
}
// Unexpected rollback only matters here if we're asked to fail early
if (!isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = false;
}
}
}
catch (RuntimeException | Error ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
// 6.事物完成之后的觸發(fā)器調(diào)用
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
// Raise UnexpectedRollbackException if we had a global rollback-only marker
if (unexpectedRollback) {
throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");
}
}
finally {
//7.事物完成后清理資源
cleanupAfterCompletion(status);
}
}
3.NOT_SUPPORTED
BankService、PersonService鳖擒、AccountService的事物傳播特性依次是:
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void save() throws RuntimeException {
System.out.println("==調(diào)用BankService的save方法\n");
System.out.println("==準(zhǔn)備調(diào)用PersonService的save方法\n");
personService.save();
System.out.println("==準(zhǔn)備調(diào)用PersonService的save方法\n");
accountService.save();
throw new RuntimeException("==AccountService的save方法手動(dòng)拋出異常");
}
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED, rollbackFor = Exception.class)
public void save() throws RuntimeException {
System.out.println("==調(diào)用PersonService的save方法\n");
jdbcTemplate.update(insert_sql);
throw new RuntimeException("==PersonService手動(dòng)拋出異常");
}
@Transactional(propagation = Propagation.NOT_SUPPORTED, rollbackFor = Exception.class)
public void save() throws RuntimeException {
System.out.println("==調(diào)用AccountService的save方法\n");
jdbcTemplate.update(insert_sql);
throw new RuntimeException("==AccountService的save方法手動(dòng)拋出異常");
}
該特性下PersonService溉浙、AccountService并不會(huì)開(kāi)啟事物,在processRollback方法中會(huì)走到第6點(diǎn)蒋荚,所以即便PersonService戳稽、AccountService拋出異常,也不會(huì)回滾期升。但是最外層的BankService是開(kāi)啟事物的惊奇,所以如果BankService里有針對(duì)數(shù)據(jù)庫(kù)的寫(xiě)操作并拋出異常,依然會(huì)回滾播赁。
但是這里是嵌套事物的回滾颂郎,當(dāng)內(nèi)層事物回滾之后不要忘記,外層事物還處在被掛起的狀態(tài)容为,那么外層被掛起的事物如何恢復(fù)呢乓序?就在processRollback方法中的第7點(diǎn)。
4.恢復(fù)掛起事物
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
// 1.將當(dāng)前事物狀態(tài)標(biāo)記為已完成
status.setCompleted();
// 2.清除synchronization
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.clear();
}
// 3.事務(wù)完成后清理資源坎背。
if (status.isNewTransaction()) {
doCleanupAfterCompletion(status.getTransaction());
}
// 4.從嵌套事物中恢復(fù)被掛起的資源
if (status.getSuspendedResources() != null) {
if (status.isDebug()) {
logger.debug("Resuming suspended transaction after completion of inner transaction");
}
Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
}
}
關(guān)于其他的資源清理涉及的代碼很多替劈,留在后面介紹,這里我們只看如何恢復(fù)被掛起的事物得滤。
protected final void resume(@Nullable Object transaction, @Nullable SuspendedResourcesHolder resourcesHolder) throws TransactionException {
if (resourcesHolder != null) {
Object suspendedResources = resourcesHolder.suspendedResources;
if (suspendedResources != null) {
// 恢復(fù)掛起資源
doResume(transaction, suspendedResources);
}
List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
// 恢復(fù)掛起的事物同步回調(diào)接口
if (suspendedSynchronizations != null) {
TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
doResumeSynchronization(suspendedSynchronizations);
}
}
}
protected void doResume(@Nullable Object transaction, Object suspendedResources) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), suspendedResources);
}
public static void bindResource(Object key, Object value) throws IllegalStateException {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Assert.notNull(value, "Value must not be null");
Map<Object, Object> map = resources.get();
// set ThreadLocal Map if none found
if (map == null) {
map = new HashMap<>();
resources.set(map);
}
Object oldValue = map.put(actualKey, value);
// Transparently suppress a ResourceHolder that was marked as void...
if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
oldValue = null;
}
if (oldValue != null) {
throw new IllegalStateException("Already value [" + oldValue + "] for key [" +actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
}
if (logger.isTraceEnabled()) {
logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" +Thread.currentThread().getName() + "]");
}
}
該方法比較簡(jiǎn)單陨献,獲取被掛起事物后重新綁定到resources對(duì)象即可。resources的定義如下:
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources");
5.PROPAGATION_REQUIRES_NEW
該情況下懂更,因?yàn)镻ROPAGATION_REQUIRES_NEW每次都會(huì)新建事物眨业,并掛起已有事物急膀,所以在該傳播特性下,如果PersonService或AccountService發(fā)生異常龄捡,就會(huì)執(zhí)行回滾操作卓嫂。當(dāng)PersonService或AccountService完成回滾之后,恢復(fù)并回滾之前掛起的事物即可墅茉。至于外層被掛起的事物會(huì)不會(huì)回滾命黔,則要根據(jù)外層事物的傳播特性而定。假如外層事物的傳播特性為NOT_SUPPORTED就斤,那么即便外層事物操作了數(shù)據(jù)庫(kù)并拋出異常也不會(huì)被回滾悍募。而且不會(huì)有嵌套事物的問(wèn)題。
6.PROPAGATION_NESTED
該特性下就有可能會(huì)走到processRollback方法的弟2步洋机,即回滾到保存點(diǎn)坠宴。前提是外層開(kāi)啟事物,且內(nèi)層事物必須有異常拋出绷旗。針對(duì)本例即BankService必須開(kāi)啟事物喜鼓,PersonService或AccountService拋出異常。如果PersonService或AccountService沒(méi)有拋出異常衔肢,雖然兩者存在保存點(diǎn)庄岖,但是并不會(huì)回滾,這樣一來(lái)即便BankService拋出異常并回滾也不會(huì)回滾到保存點(diǎn)角骤,因?yàn)楸4纥c(diǎn)是建立在PersonService和AccountService對(duì)應(yīng)的事物上的隅忿。
下面來(lái)看回滾到保存點(diǎn)的實(shí)現(xiàn):
/**
* 回滾到事物的保存點(diǎn)并釋放保存點(diǎn)資源
* Roll back to the savepoint that is held for the transaction
* and release the savepoint right afterwards.
*/
public void rollbackToHeldSavepoint() throws TransactionException {
Object savepoint = getSavepoint();
if (savepoint == null) {
throw new TransactionUsageException("Cannot roll back to savepoint - no savepoint associated with current transaction");
}
// 回滾到保存點(diǎn)
getSavepointManager().rollbackToSavepoint(savepoint);
// 釋放保存點(diǎn)資源
getSavepointManager().releaseSavepoint(savepoint);
setSavepoint(null);
}
public void rollbackToSavepoint(Object savepoint) throws TransactionException {
// 回滾到保存點(diǎn)并重置RollbackOnly屬性
ConnectionHolder conHolder = getConnectionHolderForSavepoint();
try {
conHolder.getConnection().rollback((Savepoint) savepoint);
conHolder.resetRollbackOnly();
}
catch (Throwable ex) {
throw new TransactionSystemException("Could not roll back to JDBC savepoint", ex);
}
}
public void releaseSavepoint(Object savepoint) throws TransactionException {
ConnectionHolder conHolder = getConnectionHolderForSavepoint();
try {
// 釋放保存點(diǎn)資源
conHolder.getConnection().releaseSavepoint((Savepoint) savepoint);
}
catch (Throwable ex) {
logger.debug("Could not explicitly release JDBC savepoint", ex);
}
}
7.PROPAGATION_SUPPORTS和PROPAGATION_REQUIRED
對(duì)于這兩種特性,既不會(huì)開(kāi)啟一個(gè)新的事物邦尊,也不會(huì)在原有事物中嵌套運(yùn)行背桐,而是把本身的事物交給已有事物。該情況下會(huì)執(zhí)行到processRollback方法的第4點(diǎn)蝉揍,將rollbackOnly標(biāo)記為true链峭,等到最外層事物事物回滾的時(shí)候一起回滾。即使最外層的事物沒(méi)有拋出異常又沾,內(nèi)層事物的異常也會(huì)被外層事物截獲并將整個(gè)事物進(jìn)行回滾弊仪。
8.總結(jié)
關(guān)于嵌套事物的處理,就先分析到這里捍掺,大家還是要多結(jié)合事物創(chuàng)建過(guò)程撼短、事物傳播特性等多多分析。