Spring 事务回滚
Spring事务
基本知识
配置@Transactional注解的方法会被Spring AOP拦截,执行TransactionInterceptor的invoke方法,它是一个around advice。在执行目标方法之前创建事务,事务信息被记录在TransactionInfo对象中,TransactionInfo创建完毕后,被存储在线程变量中,如下面代码,
private static final ThreadLocal<TransactionInfo> transactionInfoHolder =
new NamedThreadLocal<TransactionInfo>("Current aspect-driven transaction");
private void bindToThread() {
// Expose current TransactionStatus, preserving any existing TransactionStatus
// for restoration after this transaction is complete.
this.oldTransactionInfo = transactionInfoHolder.get();
transactionInfoHolder.set(this);
}
取出后赋值给oldTransactionInfo,然后将当前TransactionInfo设置进去。
初始化TransactionInfo后,执行目标方法,如果抛出异常,则执行事务的异常处理,否则提交事务,在提交事务之前或者回滚事务之后,transactionInfoHolder将线程变量的值设置成oldTransactionInfo。具体看下面的代码:
private void restoreThreadLocalStatus() {
// Use stack to restore old transaction TransactionInfo.
// Will be null if none was set.
transactionInfoHolder.set(this.oldTransactionInfo);
}
整个事务处理流程在下面的方法中,
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
//初始化事务属性
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
//初始化事务管理器
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
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;
}
下面看一下事务的创建、回滚和提交三个动作
开启事务
在开启事务之前,会先初始化事务属性,信息定义在TransactionDefinition中,包括隔离级别、传播行为、超时和只读。然后创建事务管理器,Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现,事务管理器缓存在transactionManagerCache对象中。
事务 | 说明 |
---|---|
org.springframework.jdbc.datasource.DataSourceTransactionManager | 使用Spring JDBC或MyBatis进行持久化数据时使用 |
org.springframework.transaction.jta.JtaTransactionManager | 使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用 |
org.springframework.orm.Hibernate3.HibernateTransactionManager | 使用Hibernate3.0版本进行持久化数据时使用 |
接下来就是创建事务对象TransactionInfo,DataSourceTransactionObject,然后从连接池中获取一个Connection,设置隔离级别,是否自动提交等,最后将Connection设置到事务对象中并开启事务。如果已经开启事务的话,就会复用DataSourceTransactionObject对象。
事务回滚
业务代码中如果抛出异常,会调用completeTransactionAfterThrowing,Spring会检查抛出的异常是否满足回滚的条件,在事务初始化时在TransactionDefinition中初始化抛出哪些异常要回滚,如果没有定义,默认会对RuntimeException和Error的异常回滚。如果满足回滚的条件,调用PlatformTransactionManager的rollback方法,如果该事务是一个新的事务,那么调用Connection的rollback接口。如果是参与的是其它事务,那么将事务对象设置成setRollbackOnly,具体如下所示:
protected void doSetRollbackOnly(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
txObject.setRollbackOnly();
}
回滚完成后,将oldTransactionInfo设置到线程变量中。
事务提交
事务提交直接调用PlatformTransactionManager的commit方法,在commit方法中,会检查是否需要回滚。如果都不满足的话,那么就调用Connection的commit方法,参与到其它事务,不会回滚。
问题总结
参考文献
【1】https://github.com/willdonggg/tech-blog/blob/master/Transaction rolled back because it has been marked as rollback-only.md
【2】https://blog.csdn.net/xieyuooo/article/details/8269218