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ù)報錯
示例
@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ù)則報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()時會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會判斷該異常是否要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這里會判斷,如果該事務(wù)外層還有事務(wù),則判斷status.isLocalRollbackOnly()或者是isGlobalRollbackOnParticipationFailure(默認返回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會獲取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這里會執(zhí)行tx.setRollbackOnly()、getConnectionHolder().setRollbackOnly()標記為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的時候會拋出UnexpectedRollbackException(Transaction silently rolled back because it has been marked as rollback-only);這里是status.isGlobalRollbackOnly()被標記為true了,因而unexpectedRollback為true
小結(jié)
UnexpectedRollbackException繼承了TransactionException,一般是事務(wù)嵌套,內(nèi)層事務(wù)拋出了異常,外層事務(wù)給catch住了,然后試圖提交事務(wù)報錯UnexpectedRollbackException(Transaction silently rolled back because it has been marked as rollback-only)。
以上就是spring的UnexpectedRollbackException事務(wù)嵌套示例解析的詳細內(nèi)容,更多關(guān)于spring 事務(wù)嵌套的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
如何使用 Spring Boot 和 Canal 實現(xiàn) My
本文介紹了如何使用SpringBoot和Canal實現(xiàn)MySQL數(shù)據(jù)庫之間的數(shù)據(jù)同步,通過配置主庫、創(chuàng)建Canal用戶、配置CanalServer以及開發(fā)SpringBoot客戶端,實現(xiàn)了將主庫的數(shù)據(jù)實時同步到多個從庫,感興趣的朋友跟隨小編一起看看吧2025-02-02
mybatis-plus3.0.1枚舉返回為null解決辦法
這篇文章主要介紹了mybatis-plus3.0.1枚舉返回為null解決辦法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-12-12
Springboot使用SPI注冊bean到spring容器的示例代碼
這篇文章主要介紹了Springboot使用SPI注冊bean到spring容器,主要包括mydriver接口,mysqldriver實現(xiàn)過程,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-10-10

