4. Mybatis 事务和Spring事务关系

大体上分为两种情况:方法上添加了事务注解@Transactional 和方法上没有添加事务注解@Transactional。

  • 添加了@Transactional 注解的在注入 bean 的时候就会被创建代理类,在代理类中使用增强逻辑进行事务处理。
  • 没有添加@Transactional 注解的,在 SqlSession 执行 sql方法的时候,执行完sql 后提交事务。

方法上没有事务注解@Transactional

我们需要知道大体流程:

  1. 与Spring 框架集成后的 mybatis 的 SqlSession 会被SqlSessionTemplate代理。即当我们使用 mybatis 执行 sql 的时候必然会走 SqlSessionTemplate 的 invoke 方法
  2. 在SqlSessionTemplate的 invoke 方法中。会先获取一个 Session 然后在执行被代理的方法(执行增删改查的sql)。
  3. 执行完Sql 之后会判断是否有Spring事务,如果没有Spring事务就提交。如果有 Spring 事务,当前 invoke 方法什么也不做,事务交给 Spring 事务管理器来做。

这个时候sql 执行完自动提交。SQL 执行失败就失败。这个时候 sql 的执行会走SqlSession 的代理逻辑 SqlSessionTemplate 中的invoke 方法。

方法上有事务注解@Transactional

这个时候会用到 Spring 的事务管理器。

当然首先是 AOP 针对@Transactional 注解进行代理。在 AOP 的 BeanPostProcessor 扫描@Transactional 注解的时候,会解析目标类和方法的属性并包装成事务属性对象,并对含有 Transactional 注解的类创建动态代理对象。

动态代理对象的增强逻辑或者叫代理逻辑为TransactionInterceptor。

此外我们还需要知道:

Mybatis事务和Spring事务的沟通桥梁就是 TransactionSynchronizationManager。TransactionSynchronizationManager会和DataSourceTransactionManager进行交互。

Transactional的代理逻辑

入口是TransactionInterceptor​,TransactionInterceptor 本身是个Advisice。是AOP的切面。其切面逻辑主要是调用抽象父类TransactionAspectSupport的invokeWithinTransaction​这个方法(以事务的方式调用这个方法)。

  1. 获取AnnotationTransactionAttributeSource​ 事务属性源。

  2. 获取事务属性,从事务属性源里获取当前被调用方法的事务属性。

  3. 获取事务管理器。根据事务管理器的类型选择对应的逻辑执行。

  4. 获取事务唯一标识: joinpointIdentification​ 这个算是事务的唯一key吧。通常是类的全路径名+方法名

  5. 创建一个事务:如果该方法对应的事务属性不为空,调用createTransactionIfNecessary​方法。该方法未必会一定创建事务,如果事务存在就不会创建事务。

    1. 如果事务存在,调用getTransaction​获取已存在的事务。并按照事务传播机制进行处理

      1. PROPAGATION_NEVER:抛异常
      2. PROPAGATION_NOT_SUPPORTED:挂起当前事务
      3. PROPAGATION_REQUIRES_NEW:挂起当前事务,开启一个新的事务
      4. 。。。
    2. 处理事务超时时间

    3. 如果没有发现已存在的事务,处理传播机制。

      1. startTransaction开始执行事务:PROPAGATION_REQUIRED

        1. doBegin方法中将事务自动提交改为false。
  6. invocation.proceedWithInvocation()执行增强方法(环绕通知);

  7. 如果事务方法执行未发生异常,则调用commitTransactionAfterReturning(txInfo)进行提交事务;

  8. 如果事务方法执行发生异常,则调用completeTransactionAfterThrowing(txInfo, ex)进行异常回滚;

Mybatis ->TransactionSynchronizationManager

Mybatis事务和Spring事务的沟通桥梁就是 TransactionSynchronizationManager。TransactionSynchronizationManager会和DataSourceTransactionManager进行交互。

我们先看一下mybatis是如何和TransactionSynchronizationManager对接的。一切都在SqlSessionTemplate这个类的反射逻辑中。

SqlSessionTemplate

SqlSessionTemplate禁用掉了手动事务操作。

java 复制代码
public void commit(boolean force) {
    throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
}

public void rollback() {
    throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
}
获取sqlSession的逻辑

在MybatisAutoConfiguration中创建了SqlSessionTemplate代理DefaultSqlSession以实现DefaultSqlSession的复用。

SqlSessionTemplate是SqlSession的实现之一。其目的主要是适应Spring的容器化环境和Spring 事务。

在SqlSessionTemplate的代理逻辑中会有获取 SqlSession的逻辑。这里会优先从TransactionSynchronizationManager获取一个SqlSession。如果获取不到,再创建一个并注册到TransactionSynchronizationManager中。下次直接用就可以。

java 复制代码
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
    Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
    Assert.notNull(executorType, "No ExecutorType specified");
    SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
        return session;
    } else {
        LOGGER.debug(() -> {
            return "Creating a new SqlSession";
        });
        session = sessionFactory.openSession(executorType);
        registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
        return session;
    }
}
判断sqlSession是否支持事务
java 复制代码
public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory) {
    Assert.notNull(session, "No SqlSession specified");
    Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
    SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
    return holder != null && holder.getSqlSession() == session;
}

DataSourceTransactionManager -> TransactionSynchronizationManager

DataSourceTransactionManager 是Spring事务中事务管理器​PlatformTransactionManager​的一种实现。

PlatformTransactionManager有三个方法分别是:getTransaction、commit、rollback。由于getTransaction最为简洁直观,所以我们单看这个类就可以。

java 复制代码
protected Object doGetTransaction() {
	DataSourceTransactionObject txObject = new DataSourceTransactionObject();
	txObject.setSavepointAllowed(isNestedTransactionAllowed());
	ConnectionHolder conHolder =
			(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
	txObject.setConnectionHolder(conHolder, false);
	return txObject;
}
相关推荐
lunzi_fly2 小时前
【源码解读之 Mybatis】【核心篇】--第5篇:Executor执行器体系详解
mybatis
珹洺3 小时前
Java-Spring入门指南(二十一)Thymeleaf 视图解析器
java·开发语言·spring
EnCi Zheng3 小时前
Spring Security 最简配置完全指南-从入门到精通前后端分离安全配置
java·安全·spring
鸽鸽程序猿6 小时前
【项目】基于Spring全家桶的论坛系统 【下】
后端·spring·restful
Lisonseekpan7 小时前
Spring Boot 中使用 Caffeine 缓存详解与案例
java·spring boot·后端·spring·缓存
小许学java7 小时前
Spring AI快速入门以及项目的创建
java·开发语言·人工智能·后端·spring·ai编程·spring ai
韩立学长8 小时前
【开题答辩实录分享】以《走失人口系统档案的设计与实现》为例进行答辩实录分享
mysql·mybatis·springboot
kfepiza9 小时前
Spring 如何解决循环依赖 笔记251008
java·spring boot·spring
kfepiza10 小时前
Spring的三级缓存原理 笔记251008
笔记·spring·缓存
Flash Dog12 小时前
【MyBatis】——执行过程
java·mybatis