七种事务传播特性:
本篇文章主要讲解Spring事务的传播属性,先看一下下表:
传播特性名称 | |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事物,则新建一个事物;如果已经存在一个事物,则加入到这个事物中 |
PROPAGATION_SUPPORTS | 支持使用当前事务,如果当前事务不存在,则不使用事务。** 解释:** 如果ServiceA.method开了事务,那么ServiceB就将自己加入ServiceA中来运行,如果ServiceA.method没有开事务,那么ServiceB自己也不开事务 |
PROPAGATION_MANDATORY | 必须被一个开启了事务的方法来调用自己,否则报错 |
PROPAGATION_REQUIRES_NEW | 创建一个新事务,如果当前事务存在,把当前事务挂起。 解释: ServiceB.method强制性自己开启一个新的事务,然后ServiceA.method的事务会卡住,等ServiceB事务完了自己再继续。这就是影响的回滚了,如果ServiceA报错了,ServiceB是不会受到影响的,ServiceB报错了,ServiceA也可以选择性的回滚或者是提交。 |
PROPAGATION_NOT_SUPPORTED | 无事务执行,如果当前事务存在,把当前事务挂起。 解释: 就是ServiceB.method不支持事务,ServiceA的事务执行到ServiceB那儿,就挂起来了,ServiceB用非事务方式运行结束,ServiceA事务再继续运行。这个好处就是ServiceB代码报错不会让ServiceA回滚。 |
PROPAGATION_NEVER | 无事务执行,如果当前有事务则抛出Exception。 解释: 不能被一个事务来调用,ServiceA.method开事务了,但是调用了ServiceB会报错 |
PROPAGATION_NESTED | 嵌套事务,如果当前事务存在,那么在嵌套的事务中执行。如果当前事务不存在,则表现跟REQUIRED一样。 解释: 开启嵌套事务,ServiceB开启一个子事务,如果回滚的话,那么ServiceB就回滚到开启子事务的这个save point。 |
当前不存在事务的情况下
每次创建一个TransactionInfo的时候都会去new一个Transaction,然后去线程变量Map中拿holder,当此时线程变量的Map中holder为空时,就会视为当前情况下不存在事务,所以transaction中holder = null。
1. PROPAGATION_MANDATORY
-
使用当前事务,如果当前没有事务,则抛出异常
在getTransaction方法中可以看到如下代码:
// 走到这里说明此时没有存在事务,如果事务的传播特性是 MANDATORY 则抛出异常
// No existing transaction found -> check propagation behavior to find out how to proceed.
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
2. REQUIRED、REQUIRES_NEW、NESTED
// 如果此时不存在事务,当传播特性是 REQUIRED REQUIRES_NEW NESTED 都会进入if语句块
else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// PROPAGATION_REQUIRED PROPAGATION_REQUIRES_NEW PROPAGATION_NESTED 都需要新建事务,、
// 因为此时不存在事务,将null 挂起
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
}
try {
// 注意这个方法
// new 一个status,存放刚刚创建的transaction,然后将其标记为新事务
// 新开一个连接的地方,非常重要
return startTransaction(def, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
startTransaction方法:
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
Boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {
Boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
// new 一个status,存放刚刚创建的transaction,然后将其标记为新事务
// 这里的 transaction 后面的一个参数决定是否是新事务
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 新开一个连接的地方,非常重要
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
此时会将null 挂起,此时的status变量为:
DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
此时的transaction中的holder依然为null,标记为新事务,接着就会执行doBegin方法了:
- 看源码(
DataTransactionManager.java
)
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
// 判断如果transaction 没有holder的话,才去dataSource中获取一个新的连接
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
// 通过 dataSource获取
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JD