(八)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管理实现。
相关推荐
cmdch201734 分钟前
Mybatis加密解密查询操作(sql前),where要传入加密后的字段时遇到的问题
数据库·sql·mybatis
撒呼呼1 小时前
# 起步专用 - 哔哩哔哩全模块超还原设计!(内含接口文档、数据库设计)
数据库·spring boot·spring·mvc·springboot
天使day2 小时前
SpringMVC
java·spring·java-ee
秋恬意3 小时前
什么是MyBatis
mybatis
壹佰大多3 小时前
【spring-cloud-gateway总结】
java·spring·gateway
CodeChampion3 小时前
60.基于SSM的个人网站的设计与实现(项目 + 论文)
java·vue.js·mysql·spring·elementui·node.js·mybatis
秋意钟3 小时前
Spring框架处理时间类型格式
java·后端·spring
科马7 小时前
【Redis】缓存
数据库·redis·spring·缓存
cloud___fly7 小时前
Spring AOP入门
java·后端·spring
zxguan10 小时前
Springboot 学习 之 logback-spring.xml 日志压缩 .tmp 临时文件问题
spring boot·学习·spring