事务(Transaction)是数据库区别于文件系统的重要特性之一。目前国际认可的数据库设计原则是ACID特性,用以保证数据库事务的正确执行。spring事务领头人叫Juergen Hoeller,他写了几乎全部的spring事务代码。
目录
1. 基本概念
事务特性ACID:原子性、一致性、隔离性、持久性
数据事务并发带来的问题:
脏读(Drity Read):事务A更新记录但未提交,事务B查询出A未提交记录。
不可重复读(Non-repeatable read): 事务A读取一次,此时事务B对数据进行了更新或删除操作,事务A再次查询数据不一致。
幻读(Phantom Read): 事务A读取一次,此时事务B插入一条数据事务A再次查询,记录多了。
不可重复读和幻读区别:不可重复读在于记录的值,幻读在于记录的数量。
2. spring事务
spring官方对事务的属性的定义:
- 事务名称,用户可手动指定事务的名称,当多个事务的时候,可区分使用哪个事务。对应注解中的属性value、transactionManager
- 隔离级别,为了解决数据库容易出现的问题,分级加锁处理策略。 对应注解中的属性isolation
- 超时时间,定义一个事务执行过程多久算超时,以便超时后回滚。可以防止长期运行的事务占用资源.对应注解中的属性timeout
- 是否只读,表示这个事务只读取数据但不更新数据, 这样可以帮助数据库引擎优化事务.对应注解中的属性readOnly
- 传播机制,对事务的传播特性进行定义,共有7种类型。对应注解中的属性propagation
- 回滚机制,定义遇到异常时回滚策略。对应注解中的属性rollbackFor、noRollbackFor、rollbackForClassName、noRollbackForClassName
2.1 隔离级别
在MVCC中,读操作可以分成两类,快照读(Snapshot read)和当前读(current read),而当前读开启的锁都必须处于事务中,事务commit,锁释放。InnoDB默认Repeatable Read。
1. Read Uncommitted(读取未提交内容):可能读取其它事务未提交的数据。有脏读+不可重复读+幻读
2. Read Committed(读取提交内容):一个事务只能看见已经提交事务所做的改变。有不可重复读+幻读
select...from : 一致性非锁定读的数据快照(MVCC)是最新版本的,但其他事务可能会有新的commit,所以同一select可能返回不同结果。有不可重复读问题
select...from for update : record lock(行级锁)
3. Repeatable Read(可重读)
select…from :同一事务内多次一致性非锁定读,取第一次读取时建立的快照版本(MVCC),保证了同一事务内部的可重复读。狭义的幻读问题得到解决(Db插入了数据,只不过读不到)
select...from for update (FOR UPDATE or LOCK IN SHARE MODE), UPDATE, 和 DELETE :
- 对于具有唯一搜索条件的唯一索引,innoDB只锁定找到的索引记录. (next-key lock 降为 record lock)
- 对于其他非索引或者非唯一索引,InnoDB会对扫描的索引范围进行锁定,使用next-key locks,阻塞其他session对间隙的insert操作。彻底解决广义的幻读问题(DB没插入数据)
4. Serializable(可串行化):这是最高的隔离级别,它是在每个读的数据行上加上共享锁(LOCK IN SHARE MODE)。在这个级别,可能导致大量的超时现象和锁竞争,主要用于分布式事务。
2.2 传播特性
org.springframework.transaction包下有一个事务定义接口TransactionDefinition,定义了7种事务传播机制:
PROPAGATION_REQUIRED | 支持当前事务;如果不存在,创建一个新的。这通常是事务定义的默认设置,通常定义事务同步作用域。 |
PROPAGATION_SUPPORTS | 支持当前事务;如果不存在事务,则以非事务方式执行。 注意: 对于具有事务同步的事务管理器,PROPAGATION_SUPPORTS与没有事务稍有不同,因为它可能在事务范围内定义了同步。因此,相同的资源(JDBC的Connection、Hibernate的Session等)将在整个指定范围内共享。确切的行为取决于事务管理器的实际同步配置! 小心使用PROPAGATION_SUPPORTS!特别是,不要依赖PROPAGATION_REQUIRED或PROPAGATION_REQUIRES_NEW,在PROPAGATION_SUPPORTS范围内(这可能导致运行时的同步冲突)。如果这种嵌套不可避免,请确保适当地配置事务管理器(通常切换到“实际事务上的同步”)。 |
PROPAGATION_MANDATORY | 支持当前事务;如果当前事务不存在,抛出异常。 PROPAGATION_MANDATORY范围内的事务同步总是由周围的事务驱动。 |
PROPAGATION_REQUIRES_NEW | 创建一个新事务,如果存在当前事务,则挂起当前事务。 实际事务挂起不会在所有事务管理器上开箱即用。这一点特别适用于JtaTransactionManager,它需要TransactionManager的支持。 PROPAGATION_REQUIRES_NEW范围总是定义自己的事务同步。现有同步将被挂起并适当地恢复。 |
PROPAGATION_NOT_SUPPORTED | 不支持当前事务,存在事务挂起当前事务;始终以非事务方式执行。 注意:实际事务挂起不会在所有事务管理器上开箱即用。这一点特别适用于JtaTransactionManager,它需要TransactionManager的支持。 |
PROPAGATION_NEVER | 不支持当前事务;如果当前事务存在,抛出异常 注意:事务同步在PROPAGATION_NEVER范围内不可用。 |
PROPAGATION_NESTED | 如果当前事务存在,则在嵌套事务中执行,如果当前没有事务,类似PROPAGATION_REQUIRED(创建一个新的)。 注意:实际创建嵌套事务只对特定的事务管理器有效。开箱即用,这只适用于 DataSourceTransactionManager(JDBC 3.0驱动)。一些JTA提供者也可能支持嵌套事务。 |
注意:JtaTransactionManager的类注释上说:Transaction suspension (REQUIRES_NEW, NOT_SUPPORTED) is just available with a JTA TransactionManager being registered." 这是片面的,只是说JTA TransactionManager支持挂起,并没有说DSTM(DataSourceTransactionManager)不支持。经过第四节实测,发现完全是支持的。网上很多说REQUIRES_NEW、NOT_SUPPORTED必须要JTA TransactionManager才行的完全是错误的说法。
传播机制 | 事务名称 | 描述 | 事务管理器要求 | 是否支持事务 | 是否开启新事务 | 回滚规则 |
REQUIRED | 要求 | 存在加入,不存在创建新 | 无 | 是 | 不一定 | 存在一个事务:1.外部有事务加入,异常回滚;2.外部没事务创建新事务,异常回滚 |
SUPPORTS | 支持 | 存在加入,不存在非事务 | 无 | 是 | 否 | 最多只存在一个事务: 1.外部有事务加入,异常回滚;2.外部没事务,内部非事务,异常不回滚 |
MANDATORY | 强制 | 存在加入,不存在抛异常 | 无 | 是 | 否 | 最多只存在一个事务: 1.外部存在事务加入,异常回滚;2.外部不存在事务,异常无法回滚 |
REQUIRES_NEW | 要求 | 存在挂起创建新,不存在创建新 | 无 | 是 | 是 | 可能存在1-2个事务:1.外部存在事务挂起,创建新,异常回滚自己的事务 2.外部不存在事务,创建新, 异常只回滚新事务 |
NOT_SUPPORTED | 不支持 | 存在挂起,不存在非事务 | 无 | 否 | 否 | 最多只存在一个事务:1. 外部有事务挂起,外部异常回滚;内部非事务,异常不回滚2.外部无事务,内部非事务,异常不回滚 |
NEVER | 坚决不 | 存在抛异常 | 无 | 否 | 否 | 最多只存在一个事务:1.外部有事务,外部异常回滚;内部非事务不回滚 2.外部非事务,内部非事务,异常不回滚 |
NESTED | 嵌套 | 存在嵌套,不存在创建新 | DSTM | 是 | 否 | 存在一个事务:1. 外部有事务,嵌套事务创建保存点,外部异常回滚全部事务;内部嵌套事务异常回滚到保存点;2.外部不存在事务,内部创建新事务,内部异常回滚 |
3. 实现方式
事务有两种实现方式:
- 编程式事务管理: 编程式事务管理使用底层源码可实现更细粒度的事务控制。spring推荐使用TransactionTemplate,典型的模板模式。
- 申明式事务管理: 添加@Transactional注解,并定义传播机制+回滚策略。基于Spring AOP实现,本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
4. 源码
4.1 TransactionTemplate
编程式事务,TransactionTemplate,其中TransactionOperations这个接口用来执行事务的回调方法。
//源码TransactionTemplate
@Override
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
// 内部封装好的事务管理器
if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
}// 需要手动获取事务,执行方法,提交事务的管理器
else {// 1.获取事务状态
TransactionStatus status = this.transactionManager.getTransaction(this);
T result;
try {// 2.执行业务逻辑
result = action.doInTransaction(status);
}
catch (RuntimeException ex) {
// 应用运行时异常 -> 回滚
rollbackOnException(status, ex);
throw ex;
}
catch (Error err) {
// Error异常 -> 回滚
rollbackOnException(status, err);
throw err;
}
catch (Throwable ex) {
// 未知异常 -> 回滚
rollbackOnException(status, ex);
throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
}// 3.事务提交
this.transactionManager.commit(status);
return result;
}
}
execute()才是核心方法,其实不复杂。
4.2 @Transactional
申明式事务使用的是spring AOP,即面向切面编程。申明式事务整体调用过程,可以抽出2条线:
1.使用代理模式,生成代理增强类。
2.根据代理事务管理配置类,配置事务的织入,在业务方法前后进行环绕增强,增加一些事务的相关操作。例如获取事务属性、提交事务、回滚事务。
从springboot 容器启动时的自动配置载入开始找,TransactionAutoConfiguration和JtaAutoConfiguration。
@Configuration
@ConditionalOnClass(PlatformTransactionManager.class)
@AutoConfigureAfter({ JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
Neo4jDataAutoConfiguration.class })
@EnableConfigurationProperties(TransactionProperties.class)
public class TransactionAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public TransactionManagerCustomizers platformTransactionManagerCustomizers(
ObjectProvider<List<PlatformTransactionManagerCustomizer<?>>> customizers) {
return new TransactionManagerCustomizers(customizers.getIfAvailable());
}
@Configuration
@ConditionalOnSingleCandidate(PlatformTransactionManager.class)
public static class TransactionTemplateConfiguration {
private final PlatformTransactionManager transactionManager;
public TransactionTemplateConfiguration(
PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
@Bean
@ConditionalOnMissingBean
public TransactionTemplate transactionTemplate() {
return new TransactionTemplate(this.transactionManager);
}
}
@Configuration
@ConditionalOnBean(PlatformTransactionManager.class)
@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
public static class EnableTransactionManagementConfiguration {
@Configuration
@EnableTransactionManagement(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
public static class JdkDynamicAutoProxyConfiguration {
}
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
public static class CglibAutoProxyConfiguration {
}
}
}
EnableTransactionManagementConfiguration支持2种代理方式:
- 1.JdkDynamicAutoProxyConfiguration:
@EnableTransactionManagement(proxyTargetClass = false),即proxyTargetClass = false表示是JDK动态代理支持的是:面向接口代理。
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false),即spring.aop.proxy-target-class=false时生效,且没有这个配置不生效。
- 2.CglibAutoProxyConfiguration:
@EnableTransactionManagement(proxyTargetClass = true),即proxyTargetClass = true标识Cglib代理支持的是子类继承代理。
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true),即spring.aop.proxy-target-class=true时生效,且没有这个配置默认生效。
注意了,默认没有配置,走的Cglib代理。说明@Transactional注解支持直接加在类上。
好吧,看了这么多配置类,终于到了@EnableTransactionManagement这个注解了。
重点看类注解@Import(TransactionManagementConfigurationSelector.class)
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
/**
* {@inheritDoc}
* @return {@link ProxyTransactionManagementConfiguration} or
* {@code AspectJTransactionManagementConfiguration} for {@code PROXY} and
* {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()}, respectively
*/
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
default:
return null;
}
}
}
4.3 核心源码
核心类图
PlatformTransactionManager定义了最核心的事务管理方法。
DataSourceTransactionmanager,即JDBC单数据库事务管理器,基于Connection实现
JtaTransactionManager,即多数据库事务管理器(又叫做分布式事务管理器),其实现了JTA规范,使用XA协议进行两阶段提交。
public interface PlatformTransactionManager {
// 获取事务状态
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
// 事务提交
void commit(TransactionStatus status) throws TransactionException;
// 事务回滚
void rollback(TransactionStatus status) throws TransactionException;
}
4.3.1 时序图
4.3.2 getTransaction
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();
}
// 如果当前已经存在事务
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);
}
}
按上图源码分成了2条处理线:
1.当前已存在事务:isExistingTransaction()判断是否存在事务,存在事务handleExistingTransaction()根据不同传播机制不同处理
2.当前不存在事务: 不同传播机制不同处理
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
// 1.NERVER(不支持当前事务;如果当前事务存在,抛出异常)报错
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
// 2.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);
}
// 3.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);
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;
}
}
// 4.NESTED嵌套事务
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() + "]");
}// 是否支持保存点:非JTA事务走这个分支。AbstractPlatformTransactionManager默认是true,JtaTransactionManager复写了该方法false,DataSourceTransactionmanager没有复写,还是true,
if (useSavepointForNestedTransaction()) {
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
status.createAndHoldSavepoint();// 创建保存点
return status;
}
else {
// JTA事务走这个分支,创建新事务
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, null);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
}
if (debugEnabled) {
logger.debug("Participating in existing transaction");
}
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");
}
}
}// 到这里PROPAGATION_SUPPORTS 或 PROPAGATION_REQUIRED或PROPAGATION_MANDATORY,存在事务加入事务即可,prepareTransactionStatus第三个参数就是是否需要新事务。false代表不需要新事物
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
当前线程已存在事务情况下,新的不同隔离级别处理情况:
1.NERVER:不支持当前事务;如果当前事务存在,抛出异常:"Existing transaction found for transaction marked with propagation 'never'"
2.NOT_SUPPORTED:不支持当前事务,现有同步将被挂起:suspend()
3.REQUIRES_NEW挂起当前事务,创建新事务:1)suspend()
2)doBegin()
4.NESTED嵌套事务1)非JTA事务:createAndHoldSavepoint()创建JDBC3.0保存点,不需要同步
2) JTA事务:开启新事务,doBegin()+prepareSynchronization()需要同步
suspend()以及doBegin()源码:
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;
}
}
/**
* 核心操作:
* 1.DataSourceTransactionObject“数据源事务对象”,设置ConnectionHolder,再给ConnectionHolder设置各种属性:自动提交、超时、事务开启、隔离级别。
* 2.给当前线程绑定一个线程本地变量,key=DataSource数据源 v=ConnectionHolder数据库连接。
*/
@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);
}
}
doSuspend(),挂起事务,AbstractPlatformTransactionManager抽象类doSuspend()会报错:不支持挂起,如果具体事务执行器支持就复写doSuspend(),DataSourceTransactionManager实现如下:
@Override
protected Object doSuspend(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
txObject.setConnectionHolder(null);
return TransactionSynchronizationManager.unbindResource(this.dataSource);
}
挂起DataSourceTransactionManager事务的核心操作就是:
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 {
// 事务资源: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");
}
4.3.3 commit
讲解源码之前先看一下资源管理类:SqlSessionSynchronization,是SqlSessionUtils的一个内部类,继承自TransactionSynchronizationAdapter抽象类,实现了事务同步接口TransactionSynchronization。
TransactionSynchronization接口定义了事务操作时的对应资源的(JDBC事务那么就是SqlSessionSynchronization)管理方法:
// 挂起事务
void suspend();
// 唤醒事务
void resume();
void flush();
// 提交事务前
void beforeCommit(boolean readOnly);
// 提交事务完成前
void beforeCompletion();
// 提交事务后
void afterCommit();
// 提交事务完成后
void afterCompletion(int status);
AbstractPlatformTransactionManager的commit源码,各种判断:
- 1.如果事务明确标记为本地回滚,-》执行回滚
- 2.如果不需要全局回滚时提交 且 全局回滚-》执行回滚
- 3.提交事务,核心方法processCommit()
@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);
}
}
commit事务时,有6个核心操作,分别是3个前置操作,3个后置操作,如下:
1.prepareForCommit(status);源码是空的,没有拓展目前。
2.triggerBeforeCommit(status); 提交前触发操作
protected final void triggerBeforeCommit(DefaultTransactionStatus status) {
if (status.isNewSynchronization()) {
if (status.isDebug()) {
logger.trace("Triggering beforeCommit synchronization");
}
TransactionSynchronizationUtils.triggerBeforeCommit(status.isReadOnly());
}
}
public static void triggerBeforeCommit(boolean readOnly) {
for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
synchronization.beforeCommit(readOnly);
}
}
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal<Set<TransactionSynchronization>>("Transaction synchronizations");
TransactionSynchronizationManager类定义了多个ThreadLocal(线程本地变量),其中synchronizations 用以保存当前线程的事务同步。遍历事务同步器,把每个事务同步器都执行“提交前”操作,比如咱们用的jdbc事务,那么最终就是SqlSessionUtils.beforeCommit()->this.holder.getSqlSession().commit();提交会话。
3.triggerBeforeCompletion(status);完成前触发操作,
如果是jdbc事务,那么最终就是SqlSessionUtils.beforeCompletion->TransactionSynchronizationManager.unbindResource(sessionFactory); 解绑当前线程的会话工厂,this.holder.getSqlSession().close();关闭会话。
4.triggerAfterCommit(status);提交事务后触发操作。
//TransactionSynchronizationUtils.triggerAfterCommit();->TransactionSynchronizationUtils.invokeAfterCommit,
public static void invokeAfterCommit(List<TransactionSynchronization> synchronizations) {
if (synchronizations != null) {
for (TransactionSynchronization synchronization : synchronizations) {
synchronization.afterCommit();//在TransactionSynchronizationAdapter中复写过
}
}
}
5. triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
afterCompletion:对于JDBC事务来说,最终:
1)如果会话任然活着,关闭会话,
2)重置各种属性:SQL会话同步器(SqlSessionSynchronization)的SQL会话持有者(SqlSessionHolder)的referenceCount引用计数、synchronizedWithTransaction同步事务、rollbackOnly只回滚、deadline超时时间点。
//TransactionSynchronizationUtils.TransactionSynchronizationUtils.invokeAfterCompletion
public static void invokeAfterCompletion(List<TransactionSynchronization> synchronizations, int completionStatus) {
if (synchronizations != null) {
for (TransactionSynchronization synchronization : synchronizations) {
try {
synchronization.afterCompletion(completionStatus);
}
catch (Throwable tsex) {
logger.error("TransactionSynchronization.afterCompletion threw exception", tsex);
}
}
}
}
6.cleanupAfterCompletion(status);
1)设置事务状态为已完成。
2) 如果是新的事务同步,解绑当前线程绑定的数据库资源,重置数据库连接
3)如果存在挂起的事务(嵌套事务),唤醒挂起的老事务的各种资源:数据库资源、同步器
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
status.setCompleted();//设置事务状态完成
//如果是新的同步,清空当前线程绑定的除了资源外的全部线程本地变量:包括事务同步器、事务名称、只读属性、隔离级别、真实的事务激活状态
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.clear();
}//如果是新的事务同步
if (status.isNewTransaction()) {
doCleanupAfterCompletion(status.getTransaction());
}//如果存在挂起的资源
if (status.getSuspendedResources() != null) {
if (status.isDebug()) {
logger.debug("Resuming suspended transaction after completion of inner transaction");
}//唤醒挂起的事务和资源(重新绑定之前挂起的数据库资源,唤醒同步器,注册同步器到TransactionSynchronizationManager)
resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());
}
}
//DataSourceTransactionManager,doCleanupAfterCompletion源码
protected void doCleanupAfterCompletion(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// 如果是最新的连接持有者,解绑当前线程绑定的<数据库资源,ConnectionHolder>
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.unbindResource(this.dataSource);
}
// 重置数据库连接(隔离级别、只读)
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");
}// 资源引用计数-1,关闭数据库连接
DataSourceUtils.releaseConnection(con, this.dataSource);
}
// 重置连接持有者的全部属性
txObject.getConnectionHolder().clear();
}
4.3.4 rollback
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");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
processRollback(defStatus);
}
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);
}
}
//DataSourceTransactionManager的doRollback()源码
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);
}
}
5. 测试验证
需求:模拟用户去银行转账,用户A转账给用户B,需要保证用户A扣款,用户B加款同时成功或失败回滚。
//mysql5.6
CREATE TABLE `user_balance` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID主键',
`name` varchar(20) DEFAULT NULL COMMENT '姓名',
`balance` decimal(10,0) DEFAULT NULL COMMENT '账户余额',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8;
INSERT INTO `user_balance` VALUES ('1', '张三', '1000'), ('2', '李四', '1000');
select * from user_balance;
/**
+----+--------+---------+
| id | name | balance |
+----+--------+---------+
| 1 | 张三 | 1000 |
| 2 | 李四 | 1000 |
+----+--------+---------+
**/
1.开启/提交事务:开启:begin/start transaction都行,提交:commit;
2.查询事务级别:select @@transaction_isolation;
3.修改事务级别:set global transaction_isolation='read-uncommitted';
注意:修改完了后要exit退出再重新连接mysql
5.1 Read Uncommitted
测试步骤:
- 开启2个会话连接mysql,会话1开始事务A,会话2开始事务B。
- 事务A中执行update把张三的余额1000-100=900,事务A查询结果为900。
- 此时事务A并没有提交,事务B查询结果也是900,即:读取了未提交的内容(MVCC快照读的最新版本号数据)。
总结:明显不行,因为事务A内部的处理数据不一定是最后的数据,很可能事务A后续再加上1000,那么事务B读取的数据明显就错了,即脏读!
5.2 Read Committed
测试步骤:
- 开启2个会话连接mysql,会话1开始事务A,会话2开始事务B。
- 事务A中执行update把张三的余额1000-100=900,事务A查询结果为900。只要事务A未提交,事务B查询数据都没有变化还是1000.
- 事务A提交,事务B查询立即变成900了,即:读已提交
总结:解决了脏读问题,但此时事务B还没提交,即出现了在一个事务中多次查询同一sql数据不一致的情况,即不可重复读!
5.3 Repeatable Read
测试步骤:
- 开启2个会话连接mysql,会话1开始事务A,会话2开始事务B。
- 事务A中执行update把张三的余额1000-100=900,事务A查询结果为900。事务A提交,事务B查询数据还是1000不变.
- 会话1再开始一个事务C插入一条“王五”数据,并提交,事务B查询还是2条数据,且数据和第一次查询一致,即:读已提交+可重复读。
- 会话2中的事务B也插入一条相同ID的数据,报错:已经存在相同ID=3的数据插入失败!,即出现了幻读。
要防止幻读,可以事务A中for update加上范围,最终会生成间隙锁,阻塞其它事务插入数据,并且当事务A提交后,事务B立即可以插入成功。
5.4 Serializable
测试步骤:
- 开启2个会话连接mysql,会话1开始事务A,会话2开始事务B。
- 事务A,查询id=2的记录,事务B更新id=2的记录,update操作被阻塞一直到超时(事务A提交后,事务B update可以立即执行)。
结论:Serializable级别下,读也加锁!如果是行锁(查询一行),那么后续对这一行的修改操作会直接阻塞等待第一个事务完毕。如果是表锁(查询整张表),那么后续对这张表的所有修改操作都阻塞等待。可见仅仅一个查询就锁住了相应的查询数据,性能实在是不敢恭维。
5.5 传播机制实测
注意:日志打印为debug级别,这样才能看见事务过程!
@Service
public class UserBalanceImpl implements UserBalanceService {
@Resource
private UserService userService;
@Resource
private UserBalanceRepository userBalanceRepository;
/**
* 创建用户
*
* @param userBalance
* @return
*/
@Override
public void addUserBalance(UserBalance userBalance) {
this.userBalanceRepository.insert(userBalance);
}
/**
* 创建用户并创建账户余额
*
* @param name
* @param balance
* @return
*/
@Transactional(propagation= Propagation.REQUIRED, rollbackFor = Exception.class)
@Override
public void addUserBalanceAndUser(String name, BigDecimal balance) {
log.info("[addUserBalanceAndUser] begin!!!");
//1.新增用户
userService.addUser(name);
//2.新增用户余额
UserBalance userBalance = new UserBalance();
userBalance.setName(name);
userBalance.setBalance(new BigDecimal(1000));
this.addUserBalance(userBalance);
log.info("[addUserBalanceAndUser] end!!!");
}
}
@Service
public class UserServiceImpl implements UserService{
@Resource
private UserRepository userRepository;
@Transactional(propagation= Propagation.REQUIRED, rollbackFor = Exception.class)
@Override
public void addUser(String name) {
log.info("[addUser] begin!!!");
User user = new User();
user.setName(name);
userRepository.insert(user);
log.info("[addUser] end!!!");
}
}
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)
public class BaseTest {
}
public class UserBalanceTest extends BaseTest{
@Resource
private UserBalanceService userBalanceService;
@Test
public void testAddUserBalanceAndUser(){
userBalanceService.addUserBalanceAndUser("赵六",new BigDecimal(1000));
}
}
5.5.1 REQUIRED
如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
外部方法,内部方法都是REQUIRED。外部方法开启事务,由于不存在事务,Registering注册一个新事务;内部方法Fetched获取已经存在的事务并使用,符合预期。
5.5.2 SUPPORTS
支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
外部方法required,内部SUPPORTS。外部方法创建一个事务,传播机制是required,内部方法Participating in existing transaction即加入已存在的外部事务,并最终一起提交事务,符合预期。
5.5.3 MANDATORY
支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
外部没有事务,内部MANDATORY,报错,符合预期。
5.5.4 REQUIRES_NEW
创建新事务,如果存在当前事务,则挂起当前事务。新事务执行完毕后,再继续执行老事务。
外部方法REQUIRED创建新事务,内部方法REQUIRES_NEW挂起老事务,创建新事务,新事务完毕后,唤醒老事务继续执行。符合预期。
5.5.5 NOT_SUPPORTED
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
外部方法REQUIRED,内部方法NOT_SUPPORTED。外部方法创建事务A,内部方法不支持事务,挂起事务A,内部方法执行完毕,唤醒事务A继续执行。符合预期。
5.5.6 NEVER
以非事务方式执行,如果当前存在事务,则抛出异常。
外部方法REQUIRED创建事务,内部方法NEVER如果当前存在事务报错,符合预期。
5.5.7 NESTED
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与REQUIRED类似的操作。
外部方法REQUIRED创建事务,内部方法NESTED构造一个内嵌事务并创建保存点,内部事务运行完毕释放保存点,继续执行外部事务。最终和外部事务一起commit.上图只有一个sqlSession对象,commit时也是一个。符合预期。
5.5.8 小结
NESTED和REQUIRES_NEW区别?
1.回滚:NESTED在创建内层事务之前创建一个保存点,内层事务回滚只回滚到保存点,不会影响外层事务。外层事务回滚则会连着内层事务一起回滚;REQUIRES_NEW构造一个新事务,和外层事务是两个独立的事务,互不影响。
2.提交:NESTED是嵌套事务,是外层事务的子事务。外层事务commit则内部事务一起提交,只有一次commit;REQUIRES_NEW是新事务,完全独立的事务,独立进行2次commit。
NESTED嵌套事务能够自己回滚到保存点,但是嵌套事务方法中的上抛的异常,外部方法也能捕获,那么外部事务也就回滚了,所以如果期望实现内部嵌套异常回滚不影响外部事务,那么需要捕获嵌套事务的异常。