问题提出
在项目开发中我们经常会在事务方法A处理一些与业务关联性较低的逻辑C,对于逻辑C,通常会加入队列或者利用Spring事务同步回调机制去处理。用Spring事务同步回调机制可以保证在业务主逻辑执行成功并提交后,再去执行其他关联逻辑,如发送钉钉消息到业务方或者通知其他业务模块做相应逻辑等。但是假如逻辑C也是事务方法,那么C方法执行过程中抛了异常,A和C的事务会回滚吗?如果事务C传播行为发生改变,C事务抛出异常,那么A和C事务又是如何呢?这个值得探讨,本文主要围绕以下两个问题进行探讨。
@Override
@Transactional(propagation = Propagation.REQUIRED)
public Result A() {
// 业务逻辑
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
// C方法是另一个事务方法,和A()方法不在同一个类中
C();
}
});
return Result.success(null);
}
问题1:
回调方法中如果存在事务方法C(默认传播行为),那么C如果报错,会回滚A吗,会回滚C报错前的更新吗?
问题2:
默认事务传播行为是REQUIRED,假如是传播行为是REQUIRED_NEW,C事务回滚是如何的?
结论
在讨论这个问题前,先把结论放在问题后面。后面会结合Spring源码分析这些问题。
1)C事务是否存在异常不影响A事务的正常提交,事务C抛出异常后,在默认隔离级别下,异常之前的更新操作不会回滚。
2)传播行为对事务C回滚策略有所影响,当C事务抛出异常,默认传播行为下,C事务方法不回滚;当传播行为为REQUIRES_NEW时,C事务方法会执行回滚策略,这主要与DefaultTransactionStatus中的newTransaction值有关。
代码演示
演示代码稍后会贴在github上。
@Override
@Transactional
public Result verify(String orderNo, Integer value, String checkName) {
OrderDTO order = orderService.findByBizCode(orderNo);
order.setValue(value);
order.updateValue(orderNo, value);
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
checkService.updateCheckName(orderNo, checkName);
}
});
return Result.success(null);
}
@Override
@Transactional
public void updateCheckName(String orderNo, String checkName) {
getDao().updateCheckName(orderNo, checkName);
throw new RuntimeException("测试事务");
}
@Test
public void verify() {
auctionBizService.verify("A20190614152128000116", 7000009, "tiankunlun88");
}
验证结果:
1)回调方法中updateCheckName(..)是否报错,不影响verify(..)方法提交;
2)对于updateCheckName(..)方法,执行过程中抛出异常时,当propagation = Propagation.Propagation.REQUIRES_NEW时,事务会回滚;当propagation = Propagation.Propagation.REQUIRED时,事务不会回滚。
源码简析
spring在扫描配置文件创建BeanDefination时会解析类中注解作为其属性,在后续实例及初始化过程中,Spring会根据是否存在@Transaction注解,利用后置处理器BeanPostProcessor进行代理对象的创建。当执行类中某一方法时首先调用org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept方法,最终判断是否由代理对象执行方法。关于spring事务网上由很多资料,这里我就本次解决问题,稍微讲一下事务源码。
DynamicAdvisedInterceptor#intercept
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Class<?> targetClass = null;
Object target = null;
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// May be null. Get as late as possible to minimize the time we
// "own" the target, in case it comes from a pool...
target = getTarget();
if (target != null) {
targetClass = target.getClass();
}
// 获取执行器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// 判断切面是否为空或方法是否是public方法,如果为空或不是public方法就直接反射调用方法即可
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// 创建method invocation,然后进行事务增强
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null) {
releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
分析: 通过intercept方法,最终调用ReflectiveMethodInvocation的proceed方法,最终通过TransactionAspectSupport#invokeWithinTransaction,执行事务方法,这也是Spring事务源码中最核心的方法之一了。下面我们还看下该方法都做了什么事情。
TransactionAspectSupport#invokeWithinTransaction
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
//通过事务属性源TransactionAttributeSource读取事务的属性配置,即调用上面名称匹配
//事务属性源NameMatchTransactionAttributeSource的方法
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
//获取Spring事务管理IoC容器配置的事务处理器
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
//获取目标类指定方法的事务连接点
final String joinpointIdentification = methodIdentification(method, targetClass);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// 创建事务,将当前事务状态和信息保存到TransactionInfo对象中
// 这个方法很很重要,设置传播行为、隔离级别、手动提交、开启事务,另外一点非常重要
// 绑定事务txInfo到当前线程,这里面用了使用许多缓存和threadLocal对象,方便获取连接信息等
// 重要一点,设置newTransaction标识位!!!!!!!!!!
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
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) {
//在调用拦截器拦过程中出现异常,则根据事务配置进行提交或回滚处理
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
//清除与当前线程绑定的事务信息
cleanupTransactionInfo(txInfo);
}
// 这个方法用来提交事务,回调方法也是在这里触发的。
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
//It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
...................... 略去
}
}
分析: 这个方法是Spring事务处理核心方法,我们简要介绍下。
- createTransactionIfNecessary方法创建了TransactionInfo对象,设置传播行为、隔离级别、手动提交(autoCommit=false),绑定事务信息到当前线程中。我们本次要分析的重点对象newTransaction标识位就在这个方法中设置值的。
- proceedWithInvocation方法,主要执行目标对象方法。
- completeTransactionAfterThrowing,见名知意,在目标对象方法出现异常后,判断是否进行回滚处理。
- cleanupTransactionInfo方法主要清除当前线程绑定的事务信息。
- commitTransactionAfterReturning,这个方法主要用来提交事务。
言归正传,我们还是继续分析newTransaction是如何设置以及它如何影响事务提交和回滚的。
设置事务A newTransaction值
TransactionAspectSupport#createTransactionIfNecessary
protected TransactionInfo createTransactionIfNecessary(
PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {
// If no name specified, apply method identification as transaction name.
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
// 获取事务状态,newTransaction就在这个方法中设置的。
status = tm.getTransaction(txAttr);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +"] because no transaction manager has been configured");
}
}
}
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
AbstractPlatformTransactionManager#getTransaction
@Override
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
Object transaction = doGetTransaction();
// Cache debug flag to avoid repeated checks.
boolean debugEnabled = logger.isDebugEnabled();
if (definition == null) {
// 如果没有配置事务属性,则使用默认的事务属性
definition = new DefaultTransactionDefinition();
}
// 检查当前线程是否存在事务,如果已存在事务,那么需要根据在事务属性中定义的事务传播属性来处理事务的产生
if (isExistingTransaction(transaction)) {
// 所以afterCommit方法中的事务C方法就是通过这个方法获取TransactionStatus,
且newTransaction标识位就是在这里设置。
return handleExistingTransaction(definition, transaction, debugEnabled);
}
//检查事务属性中timeout超时属性设置是否合理
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
// 以下代码就是对事务传播行进行处理
// 如果事务传播特性配置的是mandatory,当前没有事务存在,抛出异常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
// PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED的逻辑如下
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
}
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 开启事务,开启手动提交、设置超时时间、绑定资源到当前线程
doBegin(transaction, definition);
// 初始化和同步事务状态
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException ex) {
// 恢复上一个事务资源
resume(null, suspendedResources);
throw ex;
}
catch (Error err) {
resume(null, suspendedResources);
throw err;
}
}
else {
// Create "empty" transaction: no actual transaction, but potentially synchronization.
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + definition);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
// 走到这步newTransaction标志为true,也就是说当事务A中newTransaction标志为true。
return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
}
}
分析: getTransaction方法根据isExistingTransaction(transaction)判断当前线是否存在事务不会,如果有事务,走handleExistingTransaction方法。如果没有,会开启事务(即mysql中的begin),设置autoCommit = false,设置事务隔离级别,绑定session到到当前线程等,最终创建DefaultTransactionStatus,这个过程会设置newTransaction值。通过源码可以发现,当当前线程不存在其他事务时,只要事务A的隔离级别不为PROPAGATION_MANDATORY,newTransaction都是为true的。所以事务A的newTransaction标志为true。
事务A提交流程分析
当retVal = invocation.proceedWithInvocation()执行完updateValue后,没有异常,执行finally方法cleanupTransactionInfo(txInfo)后,进行事务提交操作,即执行commitTransactionAfterReturning(txInfo)方法。
TransactionAspectSupport#invokeWithinTransaction
-> invocation.proceedWithInvocation()
-> commitTransactionAfterReturning(txInfo)
-> 提交事务AbstractPlatformTransactionManager#processCommit
-> DataSourceTransactionManager#doCommit
-> 执行con.commit(); 这时候事务A更新操作持久化了
-> 执行AbstractPlatformTransactionManager#triggerAfterCommit -> TransactionSynchronizationUtils#invokeAfterCommit,执行回调
小结: 执行invokeAfterCommit后,进入updateCheckName,此时,事务A已经提交了。回答了问题1中:的部分问题C如果报错,会回滚A吗?Spring执行afterCommit中的回调方法,不管C事务是否存在异常,都不是影响事务A,因为A此时已经commit持久化了!!!事务C提交流程分析
执行流程与事务A执行流程大都相似,但获取TransactionStatus方法有所不同,在事务C执行流程抛出了异常,这时事务又是如何回滚的呢,这部分内容我们会得到问题2的答案。
设置事务C newTransaction值
AbstractPlatformTransactionManager#getTransaction
@Override
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
Object transaction = doGetTransaction();
// Cache debug flag to avoid repeated checks.
boolean debugEnabled = logger.isDebugEnabled();
if (definition == null) {
// Use defaults if no transaction definition given.
definition = new DefaultTransactionDefinition();
}
// 这时后线程存在事务,走handleExistingTransaction方法,而事务C的newTransaction
// 标志位就在这个方法中设置。
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return handleExistingTransaction(definition, transaction, debugEnabled);
}
........
}
分析: 此时当前线程存在事务,执行handleExistingTransaction方法。接下来我们来看下handleExistingTransaction源码。
AbstractPlatformTransactionManager#handleExistingTransaction
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
// 如果事务传播特性为:never,则抛出异常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
// 如果事务传播特性是PROPAGATION_NOT_SUPPORTED
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) {
logger.debug("Suspending current transaction");
}
// 当前线程存在事务,则将事务挂起
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
// 创建非事务的事务状态,让方法不走事务
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}
// 重点来来,如果传播行为PROPAGATION_REQUIRES_NEW
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
}
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 设置newnewTransaction位true
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
catch (Error beginErr) {
resumeAfterBeginException(transaction, suspendedResources, beginErr);
throw beginErr;
}
}
// 这一段主要是分析嵌套事务的,我们基本不用。
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
//如果不允许事务嵌套,则抛出异常
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
}
// 如果允许使用savepoint保存点保存嵌套事务,为当前事务创建一个保存点
if (useSavepointForNestedTransaction()) {
// Create savepoint within existing Spring-managed transaction,
// through the SavepointManager API implemented by TransactionStatus.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
status.createAndHoldSavepoint();
return status;
}
else {
// Nested transaction through nested begin and commit/rollback calls.
// Usually only for JTA: Spring synchronization might get activated here
// in case of a pre-existing JTA transaction.
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, null);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
}
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
if (debugEnabled) {
logger.debug("Participating in existing transaction");
}
// 下面这个if主要是判断隔离级别和数据库默认隔离级别是否一致,如果不一致,判断当前的隔离级别和definition的隔离级别
// 是否一致,不一致就抛异常。这段代码主要是隔离级别的参数校验。
if (isValidateExistingTransaction()) {
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
Constants isoConstants = DefaultTransactionDefinition.constants;
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] specifies isolation level which is incompatible with existing transaction: " +
(currentIsolationLevel != null ?
isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
"(unknown)"));
}
}
if (!definition.isReadOnly()) {
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] is not marked as read-only but existing transaction is");
}
}
}
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// 剩下的就是PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED,从这里可以看出newTransaction为false
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
小结: 这个方法主要是根据事务传播属性,判断是否要重新创建事务,还是使用原来的事务。如果是默认传播行为,还是使用原来的事务,此时newTranaction值为false,在这里确定了事务C的newTranaction值为false。 如果是传播行为REQUIRES_NEW,则会新建事务,newTranaction即为true。
newTranaction如何影响事务C提交
在事务事务C抛出异常后,事务C是否进行会回滚呢,可能我们感官上以为出现了异常,应该会回滚的,但是事实可能并非我们想象得那般。分了分析问题2,我们需要再次贴上事务执行的部分源代码,方便理解。 TransactionAspectSupport#invokeWithinTransaction
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
// 略......
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
//在调用拦截器拦过程中出现异常,则根据事务配置进行提交或回滚处理
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
//清除与当前线程绑定的事务信息
cleanupTransactionInfo(txInfo);
}
// 这个方法用来提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
...................... 略去
}
}
当invocation.proceedWithInvocation()方法报错,即updateCheckName抛异常时,进入completeTransactionAfterThrowing方法,完成事务操作。 TransactionAspectSupport#completeTransactionAfterThrowing
protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.hasTransaction()) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
// 满足配置的异常回滚策略,会进入回滚方法rollback
if (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 ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by rollback error", ex);
throw err;
}
}
else {
// 提交事务
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 ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by commit error", ex);
throw err;
}
}
}
}
分析: 从completeTransactionAfterThrowing方法中可以看出,并不是所有的异常都会使事务在出现异常时回滚。对于不支持回滚的异常,Spring依然会提交事务。 AbstractPlatformTransactionManager#processRollback
private void processRollback(DefaultTransactionStatus status) {
try {
try {
// 触发完成前的回调操作
triggerBeforeCompletion(status);
//嵌套事务回滚处理
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
//回滚挂起在保存点的事务
status.rollbackToHeldSavepoint();
}
// 当前事务中新事务的回滚操作,就是根据newTransaction标志位进行判断的
// 所以在这里事务C方法是默认传播行为时不会进行回滚操作,当传播行为为required_new
// 时,会进入doRollback方法回滚之前的操作。
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
//回滚处理,由具体的事务处理器实现
doRollback(status);
}
// 这个方法没明白到底起什么作用,就不分析了。
else 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");
}
}
catch (RuntimeException ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
catch (Error err) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw err;
}
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
}
finally {
cleanupAfterCompletion(status);
}
}
分析: 当执行processRollback方法时,会判断事务C是否是新事务,有前面当分析我们已经得知newTransaction为false。因此,不会执行doRollback,也就是说事务C不会执行回滚操作!
此时,回答了问题1:事务C异常,会回滚C报错前的更新吗? 但是由引来了一个问题,事务C的数据是何时持久化的呢?接下来,我们分析下,在默认隔离级别下,事务C数据在何时进行持久化的。
事务C持久化分析
当事务C抛出异常后,意味着事务A执行执行triggerAfterCommit失败,进入finally流程,执行cleanupAfterCompletion(status)方法,也就是在这方法中事务C的数据落地了。 DataSourceTransactionManager#cleanupAfterCompletion
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
status.setCompleted();
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.clear();
}
// 此时回到事务A的执行流程中,isNewTransaction为true
if (status.isNewTransaction()) {
// 事务结束后的清理工作,接下来会详细分析
doCleanupAfterCompletion(status.getTransaction());
}
if (status.getSuspendedResources() != null) {
if (status.isDebug()) {
logger.debug("Resuming suspended transaction after completion of inner transaction");
}
resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());
}
}
DataSourceTransactionManager#doCleanupAfterCompletion
@Override
protected void doCleanupAfterCompletion(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// Remove the connection holder from the thread, if exposed.
if (txObject.isNewConnectionHolder()) {
//从TransactionSynchronizationManager中解绑相应的connectionHolder
TransactionSynchronizationManager.unbindResource(this.dataSource);
}
//还原Connection
Connection con = txObject.getConnectionHolder().getConnection();
try {
if (txObject.isMustRestoreAutoCommit()) {
// 当这段代码执行后,数据落地了。
con.setAutoCommit(true);
}
DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
}
catch (Throwable ex) {
logger.debug("Could not reset JDBC Connection after transaction", ex);
}
if (txObject.isNewConnectionHolder()) {
if (logger.isDebugEnabled()) {
logger.debug("Releasing JDBC Connection [" + con + "] after transaction");
}
//如果是newConnection将这个链接关闭,如果是连接池将还给连接池
DataSourceUtils.releaseConnection(con, this.dataSource);
}
// 设置transactionActive为false
txObject.getConnectionHolder().clear();
}
分析: 将将连接autoCommit设置为true时,事务C的数据就落地了。
传播行为对事务C回滚的影响分析
事务C的传播行为为PROPAGATION_REQUIRES_NEW时,newTransaction值为true,在抛出异常后执行doRollback方法,回滚数据。 此时回答了问题2,也就是说当传播行为不同时,事务C回滚策略不一样。
AbstractPlatformTransactionManager#processRollback
private void processRollback(DefaultTransactionStatus status) {
try {
try {
// 触发完成前的回调操作
triggerBeforeCompletion(status);
//嵌套事务回滚处理
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
//回滚挂起在保存点的事务
status.rollbackToHeldSavepoint();
}
// 当前事务中新事务的回滚操作,就是根据newTransaction标志位进行判断的
// 所以在这里事务C方法是默认传播行为时不会进行回滚操作,当传播行为为required_new
// 时,会进入doRollback方法回滚之前的操作。
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
//回滚处理,由具体的事务处理器实现
doRollback(status);
}
........
}
finally {
cleanupAfterCompletion(status);
}
}
DataSourceTransactionManager#doRollback
@Override
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
举一反三
如果事务C没有抛出异常,事务C的提交会在哪里呢?
如果是默认传播行为,事务C没有异常,它的数据仍然是在事务A提交事务后才进行落地的,也就是doCleanupAfterCompletion方法中设置autoCommit=true时。但是当传播行为为REQUIRES_NEW时,就不一样了,事务C的提交,在自己的事务中进行。
总结
1.Spring事务同步回调afterCommit方法中的事务方法C抛异常,不影响主方法A正常提交;
2.不同传播行为对事务C回滚影响不同,默认传播行为时,C事务方法不回滚。当传播行为REQUIRES_NEW时,C事务方法在异常是,会执行回滚策略,这主要与DefaultTransactionStatus中的newTransaction值有关;
3.事务A方法执行DataSourceTransactionManager#doCleanupAfterCompletionautoComumit设置为true时,C事务中的数据就会被持久化。当然如果是隔离级别发生变化,落地时机也会发生改变,这主要与事务C的传播行为有关,本质上还是与newTransaction有关。
4.如果要保证afterCommit中的事务方法有效,需要将传播行为设置为Required_New,这一点Spring也有过建议。