第08篇:Mybatis事务处理

article/2025/9/16 3:37:10

一、Jdk底层实现

Java JDK中提供了标准接口Connection,不同的数据库驱动负责具体的实现。后面无论是Spring还是Mybatis对事务的处理,无论怎么的封装,最终究其到底都是由Connection来提供的能力。

public interface Connection  extends WrapperAutoCloseable {
    Statement createStatement() throws SQLException;
    void commit() throws SQLException;
    void rollback() throws SQLException;
}

例如 com.mysql.cj.jdbc.ConnectionImpl。具体负责跟mysql进行通信执行命令。

二、Mybatis实现

首先我们来看Mybatis是如何对Connection进行事务的封装。首先我们先来看一个图。

alt

2.1 调用流程

根据上面的图我们看,都是一层一层的封装进行委派最终由Connection的具体数据库驱动来进行实现的。

  • SqlSession
  • Executor
  • Transaction
public interface SqlSession extends Closeable {
  void commit();
  void rollback();
}
public interface Executor {
  void commit();
  void rollback();
}
public interface Transaction {
  void commit() throws SQLException;
  void rollback() throws SQLException;
}

2.2 实现原理

Mybatis中我们的接口是使用代理进行跟数据库进行交互的。所以他的事务提交逻辑是嵌套在代理方法中的。 通过前面的调用流程学习,第04篇:Mybatis代理对象生成我们知道最终都是在MapperMethod对SqlSession的调用执行数据库操作的。 而SqlSession是有两个包装类的。

  • SqlSession 通过底层的封装提供具体的调用指令
  • SqlSessionManager 对SqlSession进行代理,自动对事务进行处理
  • SqlSessionTemplate 事务的处理完全外包给Spring来处理

下面我们分别来看下每个类具体都做了什么吧。

SqlSessionManager

SqlSessionManager 是对SqlSession的一个包装,它会自己来管理SqlSession。他的具体实现是通过对SqlSession 生成代理,代理拦截每个方法进行增强。


  private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
    this.sqlSessionFactory = sqlSessionFactory;
    this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[]
{SqlSession.class},
        new SqlSessionInterceptor())
;
  }

SqlSessionInterceptor

 private class SqlSessionInterceptor implements InvocationHandler {
    public SqlSessionInterceptor() {
        // Prevent Synthetic Access
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
      if (sqlSession != null) {
        try {
          return method.invoke(sqlSession, args);
        } catch (Throwable t) {
          throw ExceptionUtil.unwrapThrowable(t);
        }
      } else {
        try (SqlSession autoSqlSession = openSession()) {
          try {
            final Object result = method.invoke(autoSqlSession, args);
            autoSqlSession.commit();
            return result;
          } catch (Throwable t) {
            autoSqlSession.rollback();
            throw ExceptionUtil.unwrapThrowable(t);
          }
        }
      }
    }
  }
  1. 从ThreadLocal中获取SqlSession,如果有,说明是调用方要自己处理事务,那么就只进行执行数据库操作,不进行事务处理和连接的关闭。
  2. 如果没有,说明要自己来管理事务,那么就新生成SqlSession,帮我们调用SqlSession#commit来提交事务,失败进行回滚。

根据其中原理我们知道有两种使用办法,

  • 首先第一种自己管理SqlSession的方式
    InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
    // 实例化sqlSessionManager
    SqlSessionManager sqlSessionManager = SqlSessionManager.newInstance(inputStream);
    // 第一步: 开启管理SqlSession,创建一个SqlSession并存入到ThreadLocal中
    sqlSessionManager.startManagedSession();
    // 使用
    UserMapper mapper = sqlSessionManager.getMapper(UserMapper.class);
    mapper.save(new User("孙悟空"));
    // 第二步: 因为事务是我们自己开启的,所以要自己来操作提交事务,或者回滚
    sqlSessionManager.commit();
    // 第三步: 关闭连接
    sqlSessionManager.close();
  • 第二种,自动管理SqlSession
    InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
    // 实例化sqlSessionManager
    SqlSessionManager sqlSessionManager = SqlSessionManager.newInstance(inputStream);
    UserMapper mapper = sqlSessionManager.getMapper(UserMapper.class);
    mapper.save(new User("孙悟空"));
    // 只用关心关闭就好了,事务的信息,都帮我们完成了。
    sqlSessionManager.close();

SqlSessionTemplate

线程安全、Spring 管理、与 Spring 事务管理一起使用的SqlSession ,以确保实际使用的 SqlSession 是与当前 Spring 事务关联的那个。此外,它还管理会话生命周期,包括根据 Spring 事务配置根据需要关闭、提交或回滚会话。 模板需要一个 SqlSessionFactory 来创建 SqlSession,作为构造函数参数传递。也可以构造指示要使用的执行器类型,如果没有,将使用会话工厂中定义的默认执行器类型。 此模板将 MyBatis PersistenceExceptions 转换为未经检查的 DataAccessExceptions,默认情况下使用MyBatisExceptionTranslator 。

==SqlSessionTemplate== 和 ==SqlSessionManager==

  • 相同点: 都是通过对SqlSession进行代理对方法进行增强的
  • 不同点: 前者是将SqlSession外包给Spring进行管理的,后者是自己通过ThreadLocal进行管理的。

下面我们来具体看下是如何拦截增强的。

  1. 第一个点获取SqlSession不同。
    • 从Spring中的事务管理器中获取当前线程的事务信息
  2. 第二个点方法执行完成后都会自动关闭SqlSession或减少引用
    • 为解决嵌套事务的情况,每次执行完后会减少一次引用。当引用都减少为0才会真正进行关闭。
  3. 第三个点是否提交事务,有判定规则。
    • 只有Spring事务管理器中没有事务时候才会自己进行提交,否则都外包给Spring进行管理。

下面我们具体来看下代码的实现吧。

 private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
      try {
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator
              .translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }

getSqlSession

  • 从Spring提供的事务管理器(TransactionSynchronizationManager)中获取当前线程拥有的SqlSession
  • 如果没有就新建一个并注册到TransactionSynchronizationManager上。
 public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator)
 
{

    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
    notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
    // 从Spring提供的事务管理器(TransactionSynchronizationManager)中获取当前线程拥有的SqlSession
    // 逻辑很简单key=SqlSessionFactory value=SqlSessionHolder
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
      return session;
    }

    LOGGER.debug(() -> "Creating a new SqlSession");
    session = sessionFactory.openSession(executorType);
    // 如果没有就新建一个并注册到TransactionSynchronizationManager上。
    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

    return session;
  }

registerSessionHolder

  • 为了保险先判断下当前线程中是否已经存在同步器,如果存在还注册就提示: "SqlSession [" + session + "] was not registered for synchronization because synchronization is not active");
  • 如果当前线程没有,判断事务管理器是否是SpringManagedTransactionFactory,如果是就注册一个。
  • SqlSessionHolder#requested() 注意这一行,创建后给引用次数加1.
private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator, SqlSession session)
 
{
    SqlSessionHolder holder;
    if (TransactionSynchronizationManager.isSynchronizationActive()) {
      Environment environment = sessionFactory.getConfiguration().getEnvironment();

      if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
        LOGGER.debug(() -> "Registering transaction synchronization for SqlSession [" + session + "]");

        holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
        TransactionSynchronizationManager.bindResource(sessionFactory, holder);
        TransactionSynchronizationManager
            .registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
        holder.setSynchronizedWithTransaction(true);
        holder.requested();
      } else {
        if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) {
          LOGGER.debug(() -> "SqlSession [" + session
              + "] was not registered for synchronization because DataSource is not transactional");
        } else {
          throw new TransientDataAccessResourceException(
              "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
        }
      }
    } else {
      LOGGER.debug(() -> "SqlSession [" + session
          + "] was not registered for synchronization because synchronization is not active");
    }

closeSqlSession

  • 如果是Spring的事务管理,就减少引用
  • 如果不是Spring的事务管理,就直接关闭
  public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
    notNull(session, NO_SQL_SESSION_SPECIFIED);
    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);

    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    if ((holder != null) && (holder.getSqlSession() == session)) {
      LOGGER.debug(() -> "Releasing transactional SqlSession [" + session + "]");
      holder.released();
    } else {
      LOGGER.debug(() -> "Closing non transactional SqlSession [" + session + "]");
      session.close();
    }
  }

isSqlSessionTransactional

事务的判定逻辑:

  • 如果从事务管理器中获取,说明当前线程是有事务的
  • 当前线程中的事务SqlSession和这个方法中的SqlSession是同一个,说明是嵌套事务。

如果是Spring来管理事务,这就不会自动来提交事务。外包给Spring的事务拦截器自己去处理。

  public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory) {
    notNull(session, NO_SQL_SESSION_SPECIFIED);
    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);

    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

    return (holder != null) && (holder.getSqlSession() == session);
  }

好了,到这里Mybatis中事务的处理逻辑我们就到了解了。

SqlSession对底层进行封装提供具体的指令 SqlSessionManager和SqlSessionTemplate都是对SqlSession进行增强来自动或者委派Spring进行事务的处理的。

下面我们去看看Spring是如何来处理事务的吧。Spring事务的处理方式

感谢您的阅读,本文由 西魏陶渊明 版权所有。如若转载,请注明出处:西魏陶渊明(https://blog.springlearn.cn/)

本文由 mdnice 多平台发布


http://chatgpt.dhexx.cn/article/MTuK1TxL.shtml

相关文章

Mybatis中的事务提交

数据表不支持事务,mybatis会直接提交数据,即增删改不需要commit(MyISAM引擎不支持事务); 数据表支持事务,默认mybatis需要手动提交事务,也可以设置为自动提交,如下 (设…

Mybatis 的事务控制

事务:访问并可能更新数据库中各种数据项的一个程序执行单元(unit) 事务的四个特性:原子性、一致性、隔离性、持久性 不考虑隔离性的三个问题:丢失修改、不可重复读,读脏数据 解决办法:四种隔离级别文章目录 一、Mybati…

Mybatis深入之事务管理

Mybatis之事务管理 简介 Mybatis的事务管理分为两种JdbcTransaction,ManagedTransaction。其中JdbcTransaction仅仅是对数据库连接Connection的一个包装、内部管理数据库事务还是调用Connection的提交、回滚等事务操作方法。ManagedTransaction更直接、什么也没有…

事务-2 Spring与Mybatis事务实现原理

背景: 本文承接事务-1 事务隔离级别和Spring事务传播机制,是事务专题的第二篇;主题内容是Mybatis和Spring事务原理,结合源码和案例进行介绍。 本文主题内容为事务原理, 尤重Spring事务原理; 会结合源码讲解整体流程, 但不会拘限于…

什么是事务?Mysql事务怎么用?Mybatis怎么操作事务的?Spring呢?快进来看看

目录 一、计算机中的事务 1. 概念 2.事务的ACID特性 3.事务类型 1)手动事务 2)自动事务 4.为什么需要事务? 5.优点 二、MySQL事务 1.语法格式: 2.关闭自动提交,开启新事务 3.开启自动提交,关闭事务控制 三、Mybatis事务 Mybatis开关事务控…

SpringBoot Mybatis事物管理

本文主要讲述springboot提供的声明式的事务管理机制。 一、一些概念 声明式的事务管理是基于AOP的,在springboot中可以通过Transactional注解的方式获得支持,这种方式的优点是: 1)非侵入式,业务逻辑不受事务管理代码…

spring boot配置mybatis和事务管理

spring boot配置mybatis和事务管理 一、spring boot与mybatis的配置 1.首先,spring boot 配置mybatis需要的全部依赖如下&#xff1a; <!-- Spring Boot 启动父依赖 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>s…

《深入理解mybatis原理》 MyBatis事务管理机制

MyBatis作为Java语言的数据库框架&#xff0c;对数据库的事务管理是其非常重要的一个方面。本文将讲述MyBatis的事务管理的实现机制。首先介绍MyBatis的事务Transaction的接口设计以及其不同实现JdbcTransaction 和 ManagedTransaction&#xff1b;接着&#xff0c;从MyBatis的…

MyBatis事务管理

一、概述 事务管理对于企业应用来说是至关重要的&#xff0c;即使出现异常情况&#xff0c;它也可以保证数据的一致性。 Spring Framework对事务管理提供了一致的抽象&#xff0c;其特点如下&#xff1a; 为不同的事务API提供一致的编程模型&#xff0c;比如JTA(Java Transac…

深入浅出Mybatis系列(五)Mybatis事务篇

在学习Mysql事务开始,分为两步。一.先看下Mysql的事务级别都有什么,然后看Mysql的事务级别设置命令。及常见问题。二.JDK是如何处理数据库操作的呢? Mybatis是如何实现JDK定义的事务级别操作。 一.Mysql的事务级别及常见概念 MySQL事务隔离级别 事务隔离级别脏读不可重复读幻…

详解 MyBatis 事务管理,彻底颠覆你对事务的理解!

来源&#xff1a;https://my.oschina.net/zudajun/blog/666764 前言 说到数据库事务&#xff0c;人们脑海里自然不自然的就会浮现出事务的四大特性、四大隔离级别、七大传播特性。四大还好说&#xff0c;问题是七大传播特性是哪儿来的&#xff1f;是 Spring 在当前线程内&…

MyBatis的事务

Mybatis管理事务是分为两种方式: (1)使用JDBC的事务管理机制,就是利用java.sql.Connection对象完成对事务的提交 (2)使用MANAGED的事务管理机制&#xff0c;这种机制mybatis自身不会去实现事务管理&#xff0c;而是让程序的容器&#xff08;JBOSS,WebLogic&#xff09;来实现对…

MyBatis--事务

事务是基于关系型数据库的企业应用的重要组成部分&#xff0c;用来确保应用程序数据的完整性和一致性。 事务就是一个系列(一组、几个)操作的集合单元&#xff0c;这些操作要么全部完成&#xff0c;要么全部失败&#xff0c;如果某一个操作失败&#xff0c;就算是已经成功执行…

ResNet网络结构解析

ResNet是识别、检测中常用的backbone&#xff0c;看检测相关论文的时候重点都在方法创新上&#xff0c;并没有特别在意网络结构&#xff0c;但是到自己跑实验改网络的时候就涉及到结构细节问题&#xff0c;于是详细的看了下ResNet网络结构。下图是ResNet的网络结构简图&#xf…

resnet网络结构图

很重要&#xff0c;单独放

ResNet网络结构,BN以及迁移学习详解

网络中的亮点&#xff1a; 1.超深的网络结构&#xff08;超过1000层&#xff09; 2.提出residual(残差)模块 3.使用Batch Normalization加速训练&#xff08;丢弃dropout&#xff09; 左边是将卷积层和池化层进行一个简单的堆叠所搭建的网络结构 20层的训练错误率大概在1%…

ResNet网络结构详解(Tensorflow2.6.0实现网络结构)

文章目录 1.ResNetX网络结构表&#xff08;1&#xff09;论文地址&#xff1a;&#xff08;2&#xff09;ResNet18网络结构&#xff1a;&#xff08;3&#xff09;ResNet34网络结构&#xff1a; 2.卷积神经网络的发展(1).卷积神经网络的发展&#xff1a;&#xff08;2&#xff…

ResNet网络结构详解,网络搭建,迁移学习

前言&#xff1a; 参考内容来自up&#xff1a;6.1 ResNet网络结构&#xff0c;BN以及迁移学习详解_哔哩哔哩_bilibili up的代码和ppt&#xff1a;https://github.com/WZMIAOMIAO/deep-learning-for-image-processing 一、简介 ResNet 网络是在 2015年 由微软实验室提出&#xf…

ResNet网络结构详解与模型的搭建

ResNET(Deep Residual Learning for Image Recognition ) ResNet网络是在2015年由微软实验室提出&#xff0c;斩获当年ImageNet竞赛中分类任务第一名&#xff0c;目标检测第一名。获得COCO数据集中目标检测第一名&#xff0c;图像分割第一名。下图是ResNet34层模型的结构简图。…

Resnet网络结构图和对应参数表的简单理解

Resnet Resnet即就是残差网络&#xff0c;本文主要是对于resnet给出的网络结构图进行简单解释。 网络结构图 以上就是34层网络的网络结构图。 以上是18层、34层、50层、101层以及152层网络所对应的残差块。 我刚开始在网上看到这两张图片的时候&#xff0c;感觉一点都不懂&a…