(八)Mybatis持久化框架原理之不同Executor对比和Spring事务关系

文章目录

  • [1. SqlSession的差异](#1. SqlSession的差异)
  • [2. Executor的差异](#2. Executor的差异)
    • [2.1 SimpleExecutor流程说明](#2.1 SimpleExecutor流程说明)
    • [2.2 ReuseExecutor流程说明](#2.2 ReuseExecutor流程说明)
    • [2.3 BatchExecutor流程说明](#2.3 BatchExecutor流程说明)
  • [3. Mybatis事务](#3. Mybatis事务)
  • [4. Spring事务](#4. Spring事务)
  • [5. 总结](#5. 总结)

本篇文章主要是由一次批量插入数据而引起的思考与探究,在这篇文章中将会分析不同的Executor和SqlSession的实现原理差异,同时也会分析Mybatis和Spring的事务处理差异并比较他们之间的优先级与关系。

1. SqlSession的差异

Mybatis依赖于SqlSession来完成sql的调用,Mybatis和Spring集成时,SqlSession常用的类型有两种:

  1. DefaultSqlSession:如果通过SqlSessionFactory的openSession方法获取SqlSession,其生成的类型就是DefaultSqlSession。使用该类时需要开发者执行完业务逻辑后自行commit、rollback或close;
  2. SqlSessionTemplate:如果使用的是@Mapper或@MapperScan等方式通过Spring自动扫描注册的方式注入Mapper类,Mapper类则会被SqlSessionTemplate代理。在代理切面中调完方法后会自动commit、rollback或close,无需开发者关心介入。但在切面中实际使用的SqlSession类型依然是DefaultSqlSession。

总结: DefaultSqlSession是Mybatis操作Mapper方法的唯一入口,而SqlSessionTemplate则是使用代理方式包装了一层,在代理方法中使用DefaultSqlSession完成了commit、rollback和close。

所以不难理解为什么SqlSessionTemplate的commit、rollback和close三个方法未实现,调用则报错,因为其只提供代理的模板方法,不提供真实逻辑操作。

DefaultSqlSession类有个autoCommit属性,很多人会误以为这个属性是真正控制事务自动提交的,实际上不是,这个属性只会控制Mybatis的事务管理器是否调用commit、rollback方法,和事务的自动提交没有关系。 SqlSession基本每次事务调用都会生成一个新的。

2. Executor的差异

Mapper的增删改调用的都是Executor的doUpdate方法,查则调用的doQuery方法。

Mybati的Executor可供选择的有三种:

  1. SIMPLE:默认的执行器,实现类SimpleExecutor,其实现非常简单,从Datssource中获取Connection,再用Connection获取Statement实现类,最后执行增删改查;
  2. REUSE:实现类ReuseExecutor,在SIMPLE的基础上加入了Statement对象的缓存,如果对应的sql有Statement对象缓存则直接使用,避免了Statement对象的频繁创建销毁;
  3. BATCH:实现类BatchExecutor,增删改时会把Statement对象缓存起来,并添加到批处理中,在进行查询或commit时将会批量执并清空Statement缓存。

Executor的生命周期和SqlSession基本等同,其中的Transaction对象则是实例化Executor时由SqlSession设置进来的。

2.1 SimpleExecutor流程说明

  • 调用时会从Connection中初始化Statement对象,而Connection会由Transaction对象维护;
  • 获取Statement之后直接调用执行方法,执行sql;
  • 最后关闭Statement对象。

2.2 ReuseExecutor流程说明

  • 用sql判断Statement是否已生成, 如果已生成则使用缓存的Statement对象,并重新设置超时时间;未生成则流程和SimpleExecutor一致;
  • 获取Statement之后直接调用执行方法,执行sql;
  • 调用commit/rollback后会调用doFlushStatements方法清空Statement缓存,相当于只在一次事务中Statement会反复使用,这个步骤没在图中标注。

2.3 BatchExecutor流程说明

  • 判断生成sql和MappedStatement对象和上次调用是否相同,如果相同则只会重新设置Statement对象的超时时间,并更新缓存;否则会重新实例化Statement
  • 调用时会从Connection中初始化Statement对象,而Connection会由Transaction对象维护;
  • 获取Statement后记录当前sql和MappedStatement对象,随后添加到缓存statementList中,并实例化BatchResult添加到缓存batchResultList;
  • 调用Statement的addBatch方法添加到批处理中;

注: BatchExecutor的增删改流程相当于只是在本地做了初始化操作并添加到了缓存中,没有实际调用数据库,因此如果是Mysql数据库,此时是拿不到自增主键的,因为还没有和数据库交互。

BatchExecutor的查询/commit流程图:

  • 从statementList和batchResultList分别获取Statement和BatchResult对象;
  • 执行Statement对象的executeBatch方法执行批量操作;
  • 执行KeyGenerator生成主键并回填;
  • 保存批量操作结果;
  • 最后关闭Statement对象并清空缓存。

查询、commit或rollback时都会调用Executor的doFlushStatements方法,如果是rollback调用,则会直接返回空。只有查询和commit才会执行图片中的流程。

3. Mybatis事务

Mybatis的事务对象Transaction会在SqlSession中实例化Executor时由TransactionFactory实例化,并注入到Executor中。在Executor中实际操作的事务就是Transaction对象,因此Mybatis的事务实际提交与否和Transaction的实现类有关。

TransactionFactory和Transaction对象的对应关系表:

Transaction类型 TransactionFactory类型 是否默认 设置autoCommit commit/rollback 备注说明
JdbcTransaction JdbcTransactionFactory 可设置 可操作 非默认类型,需要开发者手动指定,可通过方法直接操作Connection对象
ManagedTransaction ManagedTransactionFactory 官方默认 不可设置 不可操作 Mybatis未指定TransactionFactory时的默认类型,负责打开Connection连接,但不进行实际的commit/rollback操作
SpringManagedTransaction SpringManagedTransactionFactory Spring默认 不可设置 未在Spring事务可操作 对接Spring时指定的默认类型,和JdbcTransaction类似,但是不能设置Connection对象的autoCommit属性,同时会判断Connection是否处于Spring的事务管理中,如果是则不会进行commit/rollback操作

由上面的表格内容可反推:Mybatis操作Datasource的Connection是由Transaction来完成的,常用的有两种:

  1. 由ManagedTransactionFactory生成的ManagedTransaction来完成,不能使用ManagedTransaction来控制connection的commit/rollback,只能直接使用Connection;
  2. 由SpringManagedTransactionFactory生成的SpringManagedTransaction来完成,会获取Datasource的自动提交属性,如果autocommit属性为true,则commit/rollback无效,如果autocommit为false,则commit/rollback生效。在Spring的事务管理中commit/rollback也无效。

注意: Druid的defaultAutoCommit属性默认是true,因此Druid会自动提交。

4. Spring事务

Spring事务通常有两种实现方式:

  1. 使用TransactionTemplate显式编程处理;
  2. 使用@Transactional切面进行处理。

使用TransactionTemplate的好处是不会受到任何约束,只要注入了TransactionTemplate,就可以直接使用,而不像@Transactional一样必须要求触发切面逻辑。其实现原理就是帮我们把try-catch块的提交回滚逻辑固定了,开发者只需要关注事务内部的业务逻辑即可。

使用这两种方式都会通过PlatformTransactionManager获取Connection,此时会把autocommit设置为false,因此事务不会自动提交。在切面即将结束时才会调用Connection的commit方法提交事务。

Spring的具体事务操作类是交给PlatformTransactionManager的实现类,如果使用的是Datasource,实现类一般是DataSourceTransactionManager,其实际管理的是由Datasource生成的Connection类,commit/rollback都是使用Connection对应的方法,autoCommit也是Connection的属性。

5. 总结

  1. 事务提交回滚与Datasource生成的Connection对象是否调用commit/rollback或autoCommit属性有关,第三方框架管理事务通过Connection对象操作;
  2. Spring的事务管理优先级大于Mybatis的事务管理,实际事务commit/rollback与否以Spring为主;
  3. Mybatis对接Spring使用的是SpringManagedTransaction,内部使用ThreadLocal完成Spring是否管理事务判断;
  4. 只通过Mybatis操作数据库事务,commit与否的优先级数据源autoCommit>Mybatis;
  5. Spring容器的事务通常由DataSourceTransactionManager管理实现。
相关推荐
二宝1521 小时前
黑马商城day1-MyBatis-Plus
java·开发语言·mybatis
不能再留遗憾了3 小时前
【SpringCloud】Sentinel
spring·spring cloud·sentinel
whltaoin4 小时前
AI 超级智能体全栈项目阶段五:RAG 四大流程详解、最佳实践与调优(基于 Spring AI 实现)
java·人工智能·spring·rag·springai
心勤则明4 小时前
Spring AI 文档ETL实战:集成text-embedding-v4 与 Milvus
人工智能·spring·etl
艾菜籽4 小时前
Spring Web MVC入门补充1
java·后端·spring·mvc
艾菜籽7 小时前
Spring MVC入门补充2
java·spring·mvc
为java加瓦10 小时前
Spring 方法注入机制深度解析:Lookup与Replace Method原理与应用
java·数据库·spring
无名客010 小时前
SpringCloud中的网关(Gateway)的作用是什么?
spring·spring cloud·gateway
hrrrrb13 小时前
【Spring Security】Spring Security 概念
java·数据库·spring
小信丶13 小时前
Spring 中解决 “Could not autowire. There is more than one bean of type“ 错误
java·spring