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;
}
相关推荐
ExiFengs1 小时前
实际项目Java1.8流处理, Optional常见用法
java·开发语言·spring
瓜牛_gn2 小时前
依赖注入注解
java·后端·spring
一元咖啡3 小时前
SpringCloud Gateway转发请求到同一个服务的不同端口
spring·spring cloud·gateway
java亮小白19974 小时前
Spring循环依赖如何解决的?
java·后端·spring
苏-言5 小时前
Spring IOC实战指南:从零到一的构建过程
java·数据库·spring
草莓base5 小时前
【手写一个spring】spring源码的简单实现--容器启动
java·后端·spring
冰帝海岸11 小时前
01-spring security认证笔记
java·笔记·spring
没书读了12 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
jokerest12312 小时前
web——sqliabs靶场——第十三关——报错注入+布尔盲注
mybatis