spring的UnexpectedRollbackException事務(wù)嵌套示例解析
UnexpectedRollbackException
/**
* Thrown when an attempt to commit a transaction resulted
* in an unexpected rollback.
*
* @author Rod Johnson
* @since 17.03.2003
*/
@SuppressWarnings("serial")
public class UnexpectedRollbackException extends TransactionException {
/**
* Constructor for UnexpectedRollbackException.
* @param msg the detail message
*/
public UnexpectedRollbackException(String msg) {
super(msg);
}
/**
* Constructor for UnexpectedRollbackException.
* @param msg the detail message
* @param cause the root cause from the transaction API in use
*/
public UnexpectedRollbackException(String msg, Throwable cause) {
super(msg, cause);
}
}UnexpectedRollbackException繼承了TransactionException,一般是事務(wù)嵌套,內(nèi)層事務(wù)拋出了異常,外層事務(wù)給catch住了,然后試圖提交事務(wù)報(bào)錯(cuò)
示例
@Transactional
public Customer createWithCatch(String name, String email) {
Customer customer = customerRepository.save(new Customer(name, email));
try {
demoService.throwExInTx(0);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return customer;
}
// demoService
@Transactional
public void throwExInTx(int i) {
int result = 1 / i;
}這里內(nèi)層事務(wù)throwExInTx拋出了異常,而外層事務(wù)createWithCatch給catch住了,最后提交事務(wù)則報(bào)UnexpectedRollbackException異常
源碼解析
invokeWithinTransaction
org/springframework/transaction/interceptor/TransactionAspectSupport.java
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
//......
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
//......
}TransactionAspectSupport的invokeWithinTransaction方法在執(zhí)行invocation.proceedWithInvocation()時(shí)會(huì)catch住異常,然后執(zhí)行completeTransactionAfterThrowing
completeTransactionAfterThrowing
org/springframework/transaction/interceptor/TransactionAspectSupport.java
/**
* Handle a throwable, completing the transaction.
* We may commit or roll back, depending on the configuration.
* @param txInfo information about the current transaction
* @param ex throwable encountered
*/
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
}
}
}completeTransactionAfterThrowing會(huì)判斷該異常是否要rollback,需要rollback的話,則執(zhí)行txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus())
processRollback
org/springframework/transaction/support/AbstractPlatformTransactionManager.java
/**
* Process an actual rollback.
* The completed flag has already been checked.
* @param status object representing the transaction
* @throws TransactionException in case of rollback failure
*/
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
triggerBeforeCompletion(status);
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
status.rollbackToHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
doRollback(status);
}
else {
// Participating in larger transaction
if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
doSetRollbackOnly(status);
}
else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
}
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;
}
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 {
cleanupAfterCompletion(status);
}
}processRollback這里會(huì)判斷,如果該事務(wù)外層還有事務(wù),則判斷status.isLocalRollbackOnly()或者是isGlobalRollbackOnParticipationFailure(默認(rèn)返回true),然后執(zhí)行doSetRollbackOnly(status)
doSetRollbackOnly
org/springframework/orm/jpa/JpaTransactionManager.java
protected void doSetRollbackOnly(DefaultTransactionStatus status) {
JpaTransactionObject txObject = (JpaTransactionObject) status.getTransaction();
if (status.isDebug()) {
logger.debug("Setting JPA transaction on EntityManager [" +
txObject.getEntityManagerHolder().getEntityManager() + "] rollback-only");
}
txObject.setRollbackOnly();
}doSetRollbackOnly會(huì)獲取txObject執(zhí)行setRollbackOnly
setRollbackOnly
org/springframework/orm/jpa/JpaTransactionManager.java
public void setRollbackOnly() {
EntityTransaction tx = getEntityManagerHolder().getEntityManager().getTransaction();
if (tx.isActive()) {
tx.setRollbackOnly();
}
if (hasConnectionHolder()) {
getConnectionHolder().setRollbackOnly();
}
}setRollbackOnly這里會(huì)執(zhí)行tx.setRollbackOnly()、getConnectionHolder().setRollbackOnly()標(biāo)記為rollbackOnly
外層事務(wù)processCommit
org/springframework/transaction/support/AbstractPlatformTransactionManager.java
/**
* Process an actual commit.
* Rollback-only flags have already been checked and applied.
* @param status object representing the transaction
* @throws TransactionException in case of commit failure
*/
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
boolean unexpectedRollback = false;
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
unexpectedRollback = status.isGlobalRollbackOnly();
status.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
unexpectedRollback = status.isGlobalRollbackOnly();
doCommit(status);
}
else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = status.isGlobalRollbackOnly();
}
// Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
// can only be caused by doCommit
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
// can only be caused by doCommit
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}
else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}
catch (RuntimeException | Error ex) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, ex);
throw ex;
}
// Trigger afterCommit callbacks, with an exception thrown there
// propagated to callers but the transaction still considered as committed.
try {
triggerAfterCommit(status);
}
finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
finally {
cleanupAfterCompletion(status);
}
}processCommit在unexpectedRollback為true的時(shí)候會(huì)拋出UnexpectedRollbackException(Transaction silently rolled back because it has been marked as rollback-only);這里是status.isGlobalRollbackOnly()被標(biāo)記為true了,因而unexpectedRollback為true
小結(jié)
UnexpectedRollbackException繼承了TransactionException,一般是事務(wù)嵌套,內(nèi)層事務(wù)拋出了異常,外層事務(wù)給catch住了,然后試圖提交事務(wù)報(bào)錯(cuò)UnexpectedRollbackException(Transaction silently rolled back because it has been marked as rollback-only)。
以上就是spring的UnexpectedRollbackException事務(wù)嵌套示例解析的詳細(xì)內(nèi)容,更多關(guān)于spring 事務(wù)嵌套的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
如何使用 Spring Boot 和 Canal 實(shí)現(xiàn) My
本文介紹了如何使用SpringBoot和Canal實(shí)現(xiàn)MySQL數(shù)據(jù)庫(kù)之間的數(shù)據(jù)同步,通過(guò)配置主庫(kù)、創(chuàng)建Canal用戶、配置CanalServer以及開(kāi)發(fā)SpringBoot客戶端,實(shí)現(xiàn)了將主庫(kù)的數(shù)據(jù)實(shí)時(shí)同步到多個(gè)從庫(kù),感興趣的朋友跟隨小編一起看看吧2025-02-02
mybatis-plus3.0.1枚舉返回為null解決辦法
這篇文章主要介紹了mybatis-plus3.0.1枚舉返回為null解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
Spring中的REST分頁(yè)的實(shí)現(xiàn)代碼
本文將介紹在REST API中實(shí)現(xiàn)分頁(yè)的基礎(chǔ)知識(shí)。我們將專注于使用Spring Boot和Spring Data 在Spring MVC中構(gòu)建REST分頁(yè),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-01-01
SpringBoot部署到騰訊云的實(shí)現(xiàn)示例
記錄一下自己第一次部署springboot項(xiàng)目,本文主要介紹了SpringBoot部署到騰訊云的實(shí)現(xiàn)示例,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08
java實(shí)現(xiàn)KFC點(diǎn)餐小程序
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)KFC點(diǎn)餐系統(tǒng)小程序,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01
javamail實(shí)現(xiàn)注冊(cè)激活郵件
這篇文章主要為大家詳細(xì)介紹了javamail實(shí)現(xiàn)注冊(cè)激活郵件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04
Springboot使用SPI注冊(cè)bean到spring容器的示例代碼
這篇文章主要介紹了Springboot使用SPI注冊(cè)bean到spring容器,主要包括mydriver接口,mysqldriver實(shí)現(xiàn)過(guò)程,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-10-10

