之前经常报"Transaction rolled back because it has been marked as rollback-only"这个异常
字面意思是"事务回滚了,因为它被标记了必须回滚",最开始完全不懂事务的嵌套,每次出现这个错误都想知道为什么,但是总是不能重现,后面反复折腾终于弄明白了怎么回事。
之前不能重现的一个重要原因是:同一个类,内部方法调用不走代理,spring基于注解的事务是基于代理的,不走代理,被调用的方法就不受事务管理代码的控制,自然无法重现问题.
测试代码:
TestController
-
@Autowired
-
TestRollbackService testRollbackService;
-
@RequestMapping("/test1")
-
public void test1(){
-
try{
-
testRollbackService.test1();
-
}catch(Exception e){
-
e.printStackTrace();
-
}
-
}
TestRollbackServiceImpl
-
@Autowired
-
StudentMapper studentMapper;
-
@Autowired
-
TestTransactionService testTransactionService;
-
@Transactional(rollbackFor = Exception.class)
-
public void test1(){
-
Student studentSelect = new Student();
-
studentSelect.setId(new Long(1));
-
Student student = studentMapper.selectByPrimaryKey(studentSelect);
-
try{
-
testTransactionService.test2();
-
}catch(Exception e){
-
e.printStackTrace();
-
}
-
}
TestTransactionServiceImpl
-
@Autowired
-
StudentMapper studentMapper;
-
@Transactional(rollbackFor = Exception.class)
-
public void test2(){
-
Student studentForInsert = new Student();
-
studentForInsert.setId(new Long(19));
-
studentForInsert.setName("测试11");
-
studentForInsert.setScore(new BigDecimal(69));
-
studentMapper.updateByPrimaryKey(studentForInsert);
-
System.out.println(1/0);
-
}
TestRollbackService.test1(方法A)中调用了TestTransactionService.test2(方法B),上述代码可以触发回滚异常的报错
两个方法都加了事务注解,并且两个方法都会受到到事务管理的拦截器增强,并且事务传播的方式都是默认的,也就是REQUIRED,当已经存在事务的时候就加入事务,没有就创建事务。这里A和B都受事务控制,并且是处于同一个事务的。
A调用B,A中抓了B的异常,当B发生异常的时候,B的操作应该回滚,但是A吃了异常,A方法中没有产生异常,所以A的操作又应该提交,二者是相互矛盾的。
spring的事务关联拦截器在抓到B的异常后就会标记rollback-only为true,当A执行完准备提交后,发现rollback-only为true,也会回滚,并抛出异常告诉调用者。
程序时序图如下:
1.程序入口
程序入口肯定是代理类,这里走是cglib的代理
入口方法:
org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept
该方法中
retVal = (new CglibAopProxy.CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed();
继续调用
org.springframework.aop.framework.ReflectiveMethodInvocation#proceed
然后到了事务管理的拦截器
org.springframework.transaction.interceptor.TransactionInterceptor#invoke
-
@Nullable
-
public Object invoke(MethodInvocation invocation) throws Throwable {
-
Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;
-
Method var10001 = invocation.getMethod();
-
invocation.getClass();
-
return this.invokeWithinTransaction(var10001, targetClass, invocation::proceed);
-
}
2.invokeWithinTransaction(事务管理的主方法)
-
@Nullable
-
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, TransactionAspectSupport.InvocationCallback invocation) throws Throwable {
-
TransactionAttributeSource tas = this.getTransactionAttributeSource();
-
TransactionAttribute txAttr = tas != null ? tas.getTransactionAttribute(method, targetClass) : null;
-
PlatformTransactionManager tm = this.determineTransactionManager(txAttr);
-
String joinpointIdentification = this.methodIdentification(method, targetClass, txAttr);
-
Object result;
-
if (txAttr != null && tm instanceof CallbackPreferringPlatformTransactionManager) {
-
TransactionAspectSupport.ThrowableHolder throwableHolder = new TransactionAspectSupport.ThrowableHolder();
-
try {
-
result = ((CallbackPreferringPlatformTransactionManager)tm).execute(txAttr, (status) -> {
-
TransactionAspectSupport.TransactionInfo txInfo = this.prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
-
Object var9;
-
try {
-
Object var8 = invocation.proceedWithInvocation();
-
return var8;
-
} catch (Throwable var13) {
-
if (txAttr.rollbackOn(var13)) {
-
if (var13 instanceof RuntimeException) {
-
throw (RuntimeException)var13;
-
}
-
throw new TransactionAspectSupport.ThrowableHolderException(var13);
-
}
-
throwableHolder.throwable = var13;
-
var9 = null;
-
} finally {
-
this.cleanupTransactionInfo(txInfo);
-
}
-
return var9;
-
});
-
if (throwableHolder.throwable != null) {
-
throw throwableHolder.throwable;
-
} else {
-
return result;
-
}
-
} catch (TransactionAspectSupport.ThrowableHolderException var19) {
-
throw var19.getCause();
-
} catch (TransactionSystemException var20) {
-
if (throwableHolder.throwable != null) {
-
this.logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
-
var20.initApplicationException(throwableHolder.throwable);
-
}
-
throw var20;
-
} catch (Throwable var21) {
-
if (throwableHolder.throwable != null) {
-
this.logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
-
}
-
throw var21;
-
}
-
} else {
-
TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
-
result = null;
-
try {
-
result = invocation.proceedWithInvocation();
-
} catch (Throwable var17) {
-
this.completeTransactionAfterThrowing(txInfo, var17);
-
throw var17;
-
} finally {
-
this.cleanupTransactionInfo(txInfo);
-
}
-
this.commitTransactionAfterReturning(txInfo);
-
return result;
-
}
-
}
程序执行的是最后的else分支,步骤很清晰
1.获取 TransactionAttribute
2.基于TransactionAttribute获取TransactionManager
3.基于TransactionAttribute获取 joinpointIdentification(没研究什么作用)
4. 基于1,2,3创建的对象获取 TransactionAspectSupport.TransactionInfo,transactionInfo是TransactionAspectSupport的一个内部类
5.执行业务方法
6.抓到异常就回滚,并清除事务,然后向上抛异常;没有异常就清除事务,然后提交
对象之间的关联关系
3.各个对象的获取
TransactionManager的获取比较简单,程序里获取到的其实就是自己配置的bean
创建TransactionInfo的过程中要先获取TransactionStatus,TransactionStatus又需要拿到ConnectionHolder
3.1 createTransactionIfNecessary
org.springframework.transaction.interceptor.TransactionAspectSupport#createTransactionIfNecessary
-
protected TransactionAspectSupport.TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
-
if (txAttr != null && ((TransactionAttribute)txAttr).getName() == null) {
-
txAttr = new DelegatingTransactionAttribute((TransactionAttribute)txAttr) {
-
public String getName() {
-
return joinpointIdentification;
-
}
-
};
-
}
-
TransactionStatus status = null;
-
if (txAttr != null) {
-
if (tm != null) {
-
status = tm.getTransaction((TransactionDefinition)txAttr);
-
} else if (this.logger.isDebugEnabled()) {
-
this.logger.debug("Skipping transactional joinpoint [" + joinpointIdentification + "] because no transaction manager has been configured");
-
}
-
}
-
return this.prepareTransactionInfo(tm, (TransactionAttribute)txAttr, joinpointIdentification, status);
-
}
3.2 获取TransactionStatus
这里先调用 status = tm.getTransaction((TransactionDefinition)txAttr) 创建TransactionStatus
org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction
-
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
-
Object transaction = this.doGetTransaction();
-
boolean debugEnabled = this.logger.isDebugEnabled();
-
if (definition == null) {
-
definition = new DefaultTransactionDefinition();
-
}
-
if (this.isExistingTransaction(transaction)) {
-
return this.handleExistingTransaction((TransactionDefinition)definition, transaction, debugEnabled);
-
} else if (((TransactionDefinition)definition).getTimeout() < -1) {
-
throw new InvalidTimeoutException("Invalid transaction timeout", ((TransactionDefinition)definition).getTimeout());
-
} else if (((TransactionDefinition)definition).getPropagationBehavior() == 2) {
-
throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");
-
} else if (((TransactionDefinition)definition).getPropagationBehavior() != 0 && ((TransactionDefinition)definition).getPropagationBehavior() != 3 && ((TransactionDefinition)definition).getPropagationBehavior() != 6) {
-
if (((TransactionDefinition)definition).getIsolationLevel() != -1 && this.logger.isWarnEnabled()) {
-
this.logger.warn("Custom isolation level specified but no actual transaction initiated; isolation level will effectively be ignored: " + definition);
-
}
-
boolean newSynchronization = this.getTransactionSynchronization() == 0;
-
return this.prepareTransactionStatus((TransactionDefinition)definition, (Object)null, true, newSynchronization, debugEnabled, (Object)null);
-
} else {
-
AbstractPlatformTransactionManager.SuspendedResourcesHolder suspendedResources = this.suspend((Object)null);
-
if (debugEnabled) {
-
this.logger.debug("Creating new transaction with name [" + ((TransactionDefinition)definition).getName() + "]: " + definition);
-
}
-
try {
-
boolean newSynchronization = this.getTransactionSynchronization() != 2;
-
DefaultTransactionStatus status = this.newTransactionStatus((TransactionDefinition)definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
-
this.doBegin(transaction, (TransactionDefinition)definition);
-
this.prepareSynchronization(status, (TransactionDefinition)definition);
-
return status;
-
} catch (Error | RuntimeException var7) {
-
this.resume((Object)null, suspendedResources);
-
throw var7;
-
}
-
}
-
}
3.3 获取transactionStatus前先获取DataSourceTransactionObject
程序最上面: Object transaction = this.doGetTransaction(); 创建DataSourceTransactionObject对象,这是DataSourceTransactionManager的内部类
org.springframework.jdbc.datasource.DataSourceTransactionManager#doGetTransaction
-
protected Object doGetTransaction() {
-
DataSourceTransactionManager.DataSourceTransactionObject txObject = new DataSourceTransactionManager.DataSourceTransactionObject();
-
txObject.setSavepointAllowed(this.isNestedTransactionAllowed());
-
ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(this.obtainDataSource());
-
txObject.setConnectionHolder(conHolder, false);
-
return txObject;
-
}
这里还获取了ConnectionHolder对象,这里newConnectionHolder为false
获取的方法如下:
org.springframework.transaction.support.TransactionSynchronizationManager#getResource
-
@Nullable
-
public static Object getResource(Object key) {
-
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
-
Object value = doGetResource(actualKey);
-
if (value != null && logger.isTraceEnabled()) {
-
logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
-
}
-
return value;
-
}
看代码很有意思,好像是通过一个key获取的,类似于从一个池子里面拿东西一样,其实当A方法执行的时候并没有获取到ConnectionHolder,拿到的是null
org.springframework.transaction.support.TransactionSynchronizationManager#doGetResource
-
private static Object doGetResource(Object actualKey) {
-
Map<Object, Object> map = (Map)resources.get();
-
if (map == null) {
-
return null;
-
} else {
-
Object value = map.get(actualKey);
-
if (value instanceof ResourceHolder && ((ResourceHolder)value).isVoid()) {
-
map.remove(actualKey);
-
if (map.isEmpty()) {
-
resources.remove();
-
}
-
value = null;
-
}
-
return value;
-
}
-
}
resources对象其实是一个ThreadLocal,意思是同一个线程中拿到的ConnectionHolder是相同的
3.3 doBegin方法
org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin
-
protected void doBegin(Object transaction, TransactionDefinition definition) {
-
DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)transaction;
-
Connection con = null;
-
try {
-
if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
-
Connection newCon = this.obtainDataSource().getConnection();
-
if (this.logger.isDebugEnabled()) {
-
this.logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
-
}
-
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
-
}
-
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
-
con = txObject.getConnectionHolder().getConnection();
-
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
-
txObject.setPreviousIsolationLevel(previousIsolationLevel);
-
if (con.getAutoCommit()) {
-
txObject.setMustRestoreAutoCommit(true);
-
if (this.logger.isDebugEnabled()) {
-
this.logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
-
}
-
con.setAutoCommit(false);
-
}
-
this.prepareTransactionalConnection(con, definition);
-
txObject.getConnectionHolder().setTransactionActive(true);
-
int timeout = this.determineTimeout(definition);
-
if (timeout != -1) {
-
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
-
}
-
if (txObject.isNewConnectionHolder()) {
-
TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder());
-
}
-
} catch (Throwable var7) {
-
if (txObject.isNewConnectionHolder()) {
-
DataSourceUtils.releaseConnection(con, this.obtainDataSource());
-
txObject.setConnectionHolder((ConnectionHolder)null, false);
-
}
-
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", var7);
-
}
-
}
截取该方法的重要几行:
-
Connection newCon = this.obtainDataSource().getConnection();
-
if (this.logger.isDebugEnabled()) {
-
this.logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
-
}
-
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
先获取连接(java.sql.Connection),然后创建ConnectionHolder,newConnectionHolder设置为true,如果之前已经不为空了,newConnectionHolder就为false
如果newConnectionHolder 为 true,还需要将 connectionHolder放到threadLocal里面,让后面的方法可以获取到相同的ConnectionHolder,截取的代码如下:
-
if (txObject.isNewConnectionHolder()) {
-
TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder());
-
}
到这里TransactionStatus就创建好了
3.3 获取TransactionInfo
org.springframework.transaction.interceptor.TransactionAspectSupport#prepareTransactionInfo
-
protected TransactionAspectSupport.TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, String joinpointIdentification, @Nullable TransactionStatus status) {
-
TransactionAspectSupport.TransactionInfo txInfo = new TransactionAspectSupport.TransactionInfo(tm, txAttr, joinpointIdentification);
-
if (txAttr != null) {
-
if (this.logger.isTraceEnabled()) {
-
this.logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");
-
}
-
txInfo.newTransactionStatus(status);
-
} else if (this.logger.isTraceEnabled()) {
-
this.logger.trace("Don't need to create transaction for [" + joinpointIdentification + "]: This method isn't transactional.");
-
}
-
txInfo.bindToThread();
-
return txInfo;
-
}
细看 txInfo.bindToThread();
org.springframework.transaction.interceptor.TransactionAspectSupport.TransactionInfo#bindToThread
-
private void bindToThread() {
-
this.oldTransactionInfo = (TransactionAspectSupport.TransactionInfo)TransactionAspectSupport.transactionInfoHolder.get();
-
TransactionAspectSupport.transactionInfoHolder.set(this);
-
}
java.lang.ThreadLocal#get
-
public T get() {
-
Thread t = Thread.currentThread();
-
ThreadLocalMap map = getMap(t);
-
if (map != null) {
-
ThreadLocalMap.Entry e = map.getEntry(this);
-
if (e != null) {
-
@SuppressWarnings("unchecked")
-
T result = (T)e.value;
-
return result;
-
}
-
}
-
return setInitialValue();
-
}
bindToThread()的的作用是获取oldTransactionInfo,还有线程有关(具体原理未知)
4.B方法抛异常后的回滚操作
org.springframework.transaction.interceptor.TransactionAspectSupport#completeTransactionAfterThrowing
-
protected void completeTransactionAfterThrowing(@Nullable TransactionAspectSupport.TransactionInfo txInfo, Throwable ex) {
-
if (txInfo != null && txInfo.getTransactionStatus() != null) {
-
if (this.logger.isTraceEnabled()) {
-
this.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 var6) {
-
this.logger.error("Application exception overridden by rollback exception", ex);
-
var6.initApplicationException(ex);
-
throw var6;
-
} catch (Error | RuntimeException var7) {
-
this.logger.error("Application exception overridden by rollback exception", ex);
-
throw var7;
-
}
-
} else {
-
try {
-
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
-
} catch (TransactionSystemException var4) {
-
this.logger.error("Application exception overridden by commit exception", ex);
-
var4.initApplicationException(ex);
-
throw var4;
-
} catch (Error | RuntimeException var5) {
-
this.logger.error("Application exception overridden by commit exception", ex);
-
throw var5;
-
}
-
}
-
}
-
}
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());调用transactionManager进行rollback
org.springframework.transaction.support.AbstractPlatformTransactionManager#rollback
-
public final void rollback(TransactionStatus status) throws TransactionException {
-
if (status.isCompleted()) {
-
throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction");
-
} else {
-
DefaultTransactionStatus defStatus = (DefaultTransactionStatus)status;
-
this.processRollback(defStatus, false);
-
}
-
}
进一步调自身的processRollback
org.springframework.transaction.support.AbstractPlatformTransactionManager#processRollback
-
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
-
try {
-
boolean unexpectedRollback = unexpected;
-
try {
-
this.triggerBeforeCompletion(status);
-
if (status.hasSavepoint()) {
-
if (status.isDebug()) {
-
this.logger.debug("Rolling back transaction to savepoint");
-
}
-
status.rollbackToHeldSavepoint();
-
} else if (status.isNewTransaction()) {
-
if (status.isDebug()) {
-
this.logger.debug("Initiating transaction rollback");
-
}
-
this.doRollback(status);
-
} else {
-
if (status.hasTransaction()) {
-
if (!status.isLocalRollbackOnly() && !this.isGlobalRollbackOnParticipationFailure()) {
-
if (status.isDebug()) {
-
this.logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
-
}
-
} else {
-
if (status.isDebug()) {
-
this.logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
-
}
-
this.doSetRollbackOnly(status);
-
}
-
} else {
-
this.logger.debug("Should roll back transaction but cannot - no transaction available");
-
}
-
if (!this.isFailEarlyOnGlobalRollbackOnly()) {
-
unexpectedRollback = false;
-
}
-
}
-
} catch (Error | RuntimeException var8) {
-
this.triggerAfterCompletion(status, 2);
-
throw var8;
-
}
-
this.triggerAfterCompletion(status, 1);
-
if (unexpectedRollback) {
-
throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");
-
}
-
} finally {
-
this.cleanupAfterCompletion(status);
-
}
-
}
this.triggerBeforeCompletion(status) 这个方法好像释放了连接
B不是新事务,所以最后会执行 this.doSetRollbackOnly(status);
org.springframework.jdbc.datasource.DataSourceTransactionManager#doSetRollbackOnly
-
protected void doSetRollbackOnly(DefaultTransactionStatus status) {
-
DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction();
-
if (status.isDebug()) {
-
this.logger.debug("Setting JDBC transaction [" + txObject.getConnectionHolder().getConnection() + "] rollback-only");
-
}
-
txObject.setRollbackOnly();
-
}
org.springframework.jdbc.datasource.DataSourceTransactionManager.DataSourceTransactionObject#setRollbackOnly
-
public void setRollbackOnly() {
-
this.getConnectionHolder().setRollbackOnly();
-
}
这里可以看到最终设置的是connectionHolder的rollbackonly属性
5.A方法尝试提交时的操作
org.springframework.transaction.interceptor.TransactionAspectSupport#commitTransactionAfterReturning
-
protected void commitTransactionAfterReturning(@Nullable TransactionAspectSupport.TransactionInfo txInfo) {
-
if (txInfo != null && txInfo.getTransactionStatus() != null) {
-
if (this.logger.isTraceEnabled()) {
-
this.logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
-
}
-
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
-
}
-
}
同样的,这里调用transactionManager进行提交
org.springframework.transaction.support.AbstractPlatformTransactionManager#commit
-
public final void commit(TransactionStatus status) throws TransactionException {
-
if (status.isCompleted()) {
-
throw new IllegalTransactionStateException("Transaction is already completed - do not call commit or rollback more than once per transaction");
-
} else {
-
DefaultTransactionStatus defStatus = (DefaultTransactionStatus)status;
-
if (defStatus.isLocalRollbackOnly()) {
-
if (defStatus.isDebug()) {
-
this.logger.debug("Transactional code has requested rollback");
-
}
-
this.processRollback(defStatus, false);
-
} else if (!this.shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
-
if (defStatus.isDebug()) {
-
this.logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
-
}
-
this.processRollback(defStatus, true);
-
} else {
-
this.processCommit(defStatus);
-
}
-
}
-
}
这个方法判断了一些无法提交的情况,程序这里走第二个分支,部分代码如下:
-
else if (!this.shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
-
if (defStatus.isDebug()) {
-
this.logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
-
}
-
this.processRollback(defStatus, true);
-
}
判断条件为:
1.全局不是rollbackonly的时候也提交(这个可能是一个配置的参数,配合在rollbackonly的时候也提交,也就是出现现在这种情况后,不用回滚,直接提交)
2.并且全局是rollbackonly
org.springframework.transaction.support.DefaultTransactionStatus#isGlobalRollbackOnly
-
public boolean isGlobalRollbackOnly() {
-
return this.transaction instanceof SmartTransactionObject && ((SmartTransactionObject)this.transaction).isRollbackOnly();
-
}
这里又要满足两个条件
1 .这里的transaction是DataSourceTransactionObject
DataSourceTransaction继承 JdbcTransactionObjectSupport
JdbcTransactionObjectSupport又实现 SmartTransactionObject,所以第一个条件满足
2. DatSourceTransactionObject的 RollbackOnly 的get和set方法如下
-
public void setRollbackOnly() {
-
this.getConnectionHolder().setRollbackOnly();
-
}
-
public boolean isRollbackOnly() {
-
return this.getConnectionHolder().isRollbackOnly();
-
}
之前B方法抛出异常的时候,就是调用的DataSourceTransactionObject的set方法设置rollbackonly为true,现在再用get方法获取,只要是同一个connectionHolder,A获取到的rollbackOnly就是true,就会触发回滚,执行 this.processRollback(defStatus, true);
org.springframework.transaction.support.AbstractPlatformTransactionManager#processRollback
-
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
-
try {
-
boolean unexpectedRollback = unexpected;
-
try {
-
this.triggerBeforeCompletion(status);
-
if (status.hasSavepoint()) {
-
if (status.isDebug()) {
-
this.logger.debug("Rolling back transaction to savepoint");
-
}
-
status.rollbackToHeldSavepoint();
-
} else if (status.isNewTransaction()) {
-
if (status.isDebug()) {
-
this.logger.debug("Initiating transaction rollback");
-
}
-
this.doRollback(status);
-
} else {
-
if (status.hasTransaction()) {
-
if (!status.isLocalRollbackOnly() && !this.isGlobalRollbackOnParticipationFailure()) {
-
if (status.isDebug()) {
-
this.logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
-
}
-
} else {
-
if (status.isDebug()) {
-
this.logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
-
}
-
this.doSetRollbackOnly(status);
-
}
-
} else {
-
this.logger.debug("Should roll back transaction but cannot - no transaction available");
-
}
-
if (!this.isFailEarlyOnGlobalRollbackOnly()) {
-
unexpectedRollback = false;
-
}
-
}
-
} catch (Error | RuntimeException var8) {
-
this.triggerAfterCompletion(status, 2);
-
throw var8;
-
}
-
this.triggerAfterCompletion(status, 1);
-
if (unexpectedRollback) {
-
throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");
-
}
-
} finally {
-
this.cleanupAfterCompletion(status);
-
}
-
}
到这里 unexpectedRollback为true,就抛出了 "Transaction rolled back because it has been marked as rollback-only"这个异常了。
解决方案
原场景,内层事务的异常被外层事务捕获,内层被标记rollback,而外层提交,最后事务提交校验时抛出Transaction rolled back because it has been marked as rollback-only异常
@Transactional(rollbackFor = Exception.class)
public void methodA(){
insert();
System.out.println(1 / 0);
}
@Transactional(rollbackFor = Exception.class)
public void methodB(){
try{
methodA();
}catch(Exception e){
System.out.println("有异常");
}
}
可以分两种情况来解决这个异常
如果我们需要内层异常的情况下,回滚整个事务,可以让内层事务抛出的异常被外层事务的try----catch处理,再抛出新的异常,或者外层不通过try—catch处理这个异常。
当然如果内层事务没有复用过,只是在这个地方使用,直接把内层的事务去了,让他和外层合并成一个事务也能解决这个问题。
@Transactional(rollbackFor = Exception.class)
public void methodA(){
System.out.println(1 / 0);
}
@Transactional(rollbackFor = Exception.class)
public void methodB(){
try{
insert();
methodA();
}catch(Exception e){
throw new Exception("存在异常")
}
}
如果内层事务异常的情况下只回滚内层事务,修改内层事务的事务传播方式
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void methodA(){
System.out.println(1 / 0);
}
@Transactional(rollbackFor = Exception.class)
public void methodB(){
try{
insert();
methodA();
}catch(Exception e){
System.out.println("有异常");
}
}