前言
Spring在TransactionDefinition接口中规定了7种类型的事务传播行为。事务传播行为是Spring框架独有的事务增强特性,他不属于的事务实际提供方数据库行为。这是Spring为我们提供的强大的工具箱,使用事务传播行可以为我们的开发工作提供许多便利。要想正确的使用工具首先需要了解工具。本文对七种事务传播行为做代码级别的解析。
基础概念
1.什么是事务传播行为?
事务传播行为用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法的时事务如何传播。
用伪代码说明:
public void methodA(){
methodB();
//doSomething
}
@Transaction(Propagation=XXX)
public void methodB(){
//doSomething
}
代码中methodA()方法嵌套调用了methodB()方法,methodB()的事务传播行为由@Transaction(Propagation=XXX)设置决定。这里需要注意的是methodA()并没有开启事务,某一个事务传播行为修饰的方法并不是必须要在开启事务的外围方法中调用。
2.Spring中七种事务传播行为
事务传播行为类型 | 说明 |
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
源码解析
核心类图结构
Spring-tx版本:4.3.20.RELEASE
PlatformTransactionManager顶级接口定义了最核心的事务管理方法,定义了3个核心方法(获取事务状态、事务提交、事务回滚)。下面有2个具体实现:
1:DataSourceTransactionmanager,即JDBC单数据库事务管理器,基于Connection实现,
2:JtaTransactionManager,即多数据库事务管理器(又叫做分布式事务管理器),其实现了JTA规范,使用XA协议进行两阶段提交。
spring事务核心代码:
public interface PlatformTransactionManager {
// 获取事务状态
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
// 事务提交
void commit(TransactionStatus status) throws TransactionException;
// 事务回滚
void rollback(TransactionStatus status) throws TransactionException;
}
事务传播
业务逻辑分2块,是否已经存在事务:
1.当前已存在事务:isExistingTransaction()判断是否存在事务,存在事务handleExistingTransaction()根据不同传播机制不同处理
2.当前不存在事务: 不同传播机制不同处理
@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();
}
// 如果当前已经存在事务
if (isExistingTransaction(transaction)) {
// 根据不同传播机制不同处理
return handleExistingTransaction(definition, transaction, debugEnabled);
}
// 超时不能小于默认值
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'");
}// 当前不存在事务,传播机制=REQUIRED/REQUIRED_NEW/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 {
// 当前不存在事务当前不存在事务,且传播机制=PROPAGATION_SUPPORTS/PROPAGATION_NOT_SUPPORTED/PROPAGATION_NEVER,这三种情况,创建“空”事务:没有实际事务,但可能是同步。警告:定义了隔离级别,但并没有真实的事务初始化,隔离级别被忽略有隔离级别但是并没有定义实际的事务初始化,有隔离级别但是并没有定义实际的事务初始化,
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);
return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
}
}
开始事务
如果是sql:
SET autocommit = 0;
start transaction;
spring-tx的开始事务逻辑为:
1:如果事务还没有connection或者connection在事务同步状态,重置新的connectionHolder
2:设置新的连接为事务同步中
3:自动提交改为false,由框架决策什么时候提交事务
4:设置connection持有者的事务开启状态
5:设置超时时间
6:绑定connection持有者到当前线程
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {// 如果事务还没有connection或者connection在事务同步状态,重置新的connectionHolder
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = this.dataSource.getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}// 重置新的connectionHolder
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
//设置新的连接为事务同步中
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
//conn设置事务隔离级别,只读
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);//DataSourceTransactionObject设置事务隔离级别
// 如果是自动提交切换到手动提交
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
// 如果只读,执行sql设置事务只读
prepareTransactionalConnection(con, definition);
txObject.getConnectionHolder().setTransactionActive(true);// 设置connection持有者的事务开启状态
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);// 设置超时秒数
}
// 绑定connection持有者到当前线程
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, this.dataSource);
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
挂起事务
suspend()是AbstractPlatformTransactionManager抽象类的模板方法,各实线类需要具体重写doSuspend(),否则报错。
以org.springframework.jdbc.datasource.DataSourceTransactionManager#doSuspend实现为例,具体逻辑为:
1.把当前事务的connectionHolder数据库连接持有者清空。
2.当前线程解绑datasource.其实就是ThreadLocal移除对应变量(TransactionSynchronizationManager类中定义的private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<Map<Object, Object>>("Transactional resources");)
TransactionSynchronizationManager事务同步管理器,该类维护了多个线程本地变量ThreadLocal
public abstract class TransactionSynchronizationManager {
private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);
// 事务资源:map<k,v> 两种数据对。1.会话工厂和会话k=SqlsessionFactory v=SqlSessionHolder 2.数据源和连接k=DataSource v=ConnectionHolder
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<Map<Object, Object>>("Transactional resources");
// 事务同步
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<Set<TransactionSynchronization>>("Transaction synchronizations");
// 当前事务名称
private static final ThreadLocal<String> currentTransactionName =
new NamedThreadLocal<String>("Current transaction name");
// 当前事务的只读属性
private static final ThreadLocal<Boolean> currentTransactionReadOnly =
new NamedThreadLocal<Boolean>("Current transaction read-only status");
// 当前事务的隔离级别
private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
new NamedThreadLocal<Integer>("Current transaction isolation level");
// 是否存在事务
private static final ThreadLocal<Boolean> actualTransactionActive =
new NamedThreadLocal<Boolean>("Actual transaction active");
。。。
}
protected final SuspendedResourcesHolder suspend(Object transaction) throws TransactionException {
if (TransactionSynchronizationManager.isSynchronizationActive()) {// 1.当前存在同步,
List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
try {
Object suspendedResources = null;
if (transaction != null) {// 事务不为空,挂起事务
suspendedResources = doSuspend(transaction);
}// 解除绑定当前事务各种属性:名称、只读、隔离级别、是否是真实的事务.
String name = TransactionSynchronizationManager.getCurrentTransactionName();
TransactionSynchronizationManager.setCurrentTransactionName(null);
boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
TransactionSynchronizationManager.setActualTransactionActive(false);
//返回当前事务元信息快照
return new SuspendedResourcesHolder(
suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
}
catch (RuntimeException ex) {
// doSuspend failed - original transaction is still active...
doResumeSynchronization(suspendedSynchronizations);
throw ex;
}
catch (Error err) {
// doSuspend failed - original transaction is still active...
doResumeSynchronization(suspendedSynchronizations);
throw err;
}
}// 2.没有同步但,事务不为空,挂起事务
else if (transaction != null) {
// Transaction active but no synchronization active.
Object suspendedResources = doSuspend(transaction);
return new SuspendedResourcesHolder(suspendedResources);
}// 2.没有同步但,事务为空,什么都不用做
else {
// Neither transaction nor synchronization active.
return null;
}
}
org.springframework.jdbc.datasource.DataSourceTransactionManager#doSuspend
@Override
protected Object doSuspend(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
//把当前事务的connectionHolder数据库连接持有者清空。
txObject.setConnectionHolder(null);
return TransactionSynchronizationManager.unbindResource(this.dataSource);
}
org.springframework.transaction.support.TransactionSynchronizationManager#unbindResource
/**
当前线程解绑datasource.其实就是ThreadLocal移除对应变量(TransactionSynchronizationManager类中定义的private static final
ThreadLocal<Map<Object, Object>> resources
= new NamedThreadLocal<Map<Object, Object>>("Transactional
resources");)
TransactionSynchronizationManager事务同步管理器,该类维护了多个线程本地变量ThreadLocal
* Unbind a resource for the given key from the current thread.
* @param key the key to unbind (usually the resource factory)
* @return the previously bound value (usually the active resource object)
* @throws IllegalStateException if there is no value bound to the thread
* @see ResourceTransactionManager#getResourceFactory()
*/
public static Object unbindResource(Object key) throws IllegalStateException {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Object value = doUnbindResource(actualKey);
if (value == null) {
throw new IllegalStateException(
"No value for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
}
return value;
}
org.springframework.transaction.support.TransactionSynchronizationManager#doUnbindResource
/**
* Actually remove the value of the resource that is bound for the given key.
*/
private static Object doUnbindResource(Object actualKey) {
Map<Object, Object> map = resources.get();
if (map == null) {
return null;
}
Object value = map.remove(actualKey);
// Remove entire ThreadLocal if empty...
if (map.isEmpty()) {
resources.remove();
}
// Transparently suppress a ResourceHolder that was marked as void...
if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
value = null;
}
if (value != null && logger.isTraceEnabled()) {
logger.trace("Removed value [" + value + "] for key [" + actualKey + "] from thread [" +
Thread.currentThread().getName() + "]");
}
return value;
}
savePoint
什么是savePoint: 设置保存点,并和rollback结合使用,实现回滚到指定保存点,嵌套事务、事务回滚会用到
示例:
事务描述:用户a给用户b汇款x,数据库表中会有两个操作,a用户的账目数减少x,b用户账目数增加x。
只有当b用户收到钱时,两个操作才回执行,否则都不执行,两人账户不变。
USE bank;
SET autocommit = 0;
start transaction;
#用户a给用户b汇款x
INSERT a_bank VALUES(NULL,'-x');
INSERT b_bank VALUES(NULL,'+x');
#设置保存点
savepoint x;
#插入流水表,可用于凭条打印
INSERT result VALUES(NULL,'a->b:x');
#打印凭条,假如数据库异常,查询超时
seltct * from result id=?
#回滚到设置好的保存点x
ROLLBACK TO x;
public class BankExample {
private static String conStr = "jdbc:mysql://localhost/test";
private String root = "root";
private String pwd = "123";
static{
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public Connection getCon(){
try {
return DriverManager.getConnection(conStr, root, pwd);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
BankExample be = new BankExample();
Connection con = null;
Savepoint sp = null;
int x = 0;
try {
con = be.getCon();
con.setAutoCommit(false);//设置自动递交关闭
//操作一 a用户减去x
String sql = "insert into a_bank values(null,?)";
PreparedStatement pstmt = con.prepareStatement(sql);
pstmt.setInt(1, 1000 - x);
pstmt.executeUpdate();
//操作二 b用户 加上 x
String sql1 = "insert into b_bank values(null,?)";
PreparedStatement pstmt1 = con.prepareStatement(sql1);
pstmt1.setInt(1, 1000 + x);
pstmt1.executeUpdate();
sp = con.setSavepoint();//设置断点
//打印凭条
PreparedStatement ps = con.prepareStatement("seltct * from result id=?");
ps.setString(1, "33");
ResultSet rs = ps.executeQuery();
System.out.println(rs.getString("name"));
} catch (SQLException e) {
try {
con.rollback(sp);//滚回断点
} catch (SQLException e1) {
e1.printStackTrace();
}
}
try {
con.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
spring-mysql源码:
/**
* This implementation creates a JDBC 3.0 Savepoint and returns it.
* @see java.sql.Connection#setSavepoint
*/
@Override
public Object createSavepoint() throws TransactionException {
ConnectionHolder conHolder = getConnectionHolderForSavepoint();
try {
if (!conHolder.supportsSavepoints()) {
throw new NestedTransactionNotSupportedException(
"Cannot create a nested transaction because savepoints are not supported by your JDBC driver");
}
return conHolder.createSavepoint();
}
catch (SQLException ex) {
throw new CannotCreateTransactionException("Could not create JDBC savepoint", ex);
}
}
public Savepoint setSavepoint(String name) throws SQLException {
synchronized(this.getConnectionMutex()) {
//mysql-connector 提供接口设置savepoint
MysqlSavepoint savepoint = new MysqlSavepoint(name, this.getExceptionInterceptor());
this.setSavepoint(savepoint);
return savepoint;
}
}
private void setSavepoint(MysqlSavepoint savepoint) throws SQLException {
synchronized(this.getConnectionMutex()) {
if (!this.versionMeetsMinimum(4, 0, 14) && !this.versionMeetsMinimum(4, 1, 1)) {
throw SQLError.createSQLFeatureNotSupportedException();
} else {
this.checkClosed();
StringBuilder savePointQuery = new StringBuilder("SAVEPOINT ");
savePointQuery.append('`');
savePointQuery.append(savepoint.getSavepointName());
savePointQuery.append('`');
java.sql.Statement stmt = null;
//拼接sql,执行sql
try {
stmt = this.getMetadataSafeStatement();
stmt.executeUpdate(savePointQuery.toString());
} finally {
this.closeStatement(stmt);
}
}
}
}
Commit
@Override
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");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
if (defStatus.isLocalRollbackOnly()) {// 如果事务明确标记为回滚,
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus);//执行回滚
return;
}//如果不需要全局回滚时提交 且 全局回滚
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}//执行回滚
processRollback(defStatus);
// 仅在最外层事务边界(新事务)或显式地请求时抛出“未期望的回滚异常”
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
return;
}
// 执行提交事务
processCommit(defStatus);
}
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {//3个前置操作
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;//3个前置操作已调用
boolean globalRollbackOnly = false;//新事务 或 全局回滚失败
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
globalRollbackOnly = status.isGlobalRollbackOnly();
}//1.有保存点,即嵌套事务
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}//释放保存点
status.releaseHeldSavepoint();
}//2.新事务
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}//调用事务处理器提交事务
doCommit(status);
}
// 3.非新事务,且全局回滚失败,但是提交时没有得到异常,抛出异常
if (globalRollbackOnly) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
// 触发完成后事务同步,状态为回滚
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}// 事务异常
catch (TransactionException ex) {
// 提交失败回滚
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}// 触发完成后回调,事务同步状态为未知
else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}// 运行时异常
catch (RuntimeException ex) {
// 如果3个前置步骤未完成,调用前置的最后一步操作
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}// 提交异常回滚
doRollbackOnCommitException(status, ex);
throw ex;
}// 其它异常
catch (Error err) {
// 如果3个前置步骤未完成,调用前置的最后一步操作
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}// 提交异常回滚
doRollbackOnCommitException(status, err);
throw err;
}
// 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);
}
}
Rollback
private void processRollback(DefaultTransactionStatus status) {
try {
try {// 解绑当前线程绑定的会话工厂,并关闭会话
triggerBeforeCompletion(status);
if (status.hasSavepoint()) {// 1.如果有保存点,即嵌套式事务
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}//回滚到保存点
status.rollbackToHeldSavepoint();
}//2.如果就是一个简单事务
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}//回滚核心方法
doRollback(status);
}//3.当前存在事务且没有保存点,即加入当前事务的
else if (status.hasTransaction()) {//如果已经标记为回滚 或 当加入事务失败时全局回滚(默认true)
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {//debug时会打印:加入事务失败-标记已存在事务为回滚
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}//设置当前connectionHolder:当加入一个已存在事务时回滚
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) {//关闭会话,重置SqlSessionHolder属性
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);
}
}