Mybatis核心类(二)-SqlSession、Executor

SqlSession、Executor的介绍

1.SqlSession

  1. SqlSession是MyBatis的一个重要接口,它对外(使用者)提供了数据库操作和事务管理的功能。

  2. SqlSession的默认实现类是DefaultSqlSession,内部维护了一个Executor对象,用于真正执行SQL语句和管理事务。

  3. 正常情况一个SqlSession对应一次数据库会话,数据库会话操作结束时可以执行关闭方法,销毁SqlSession对象。

2.Executor

1.负责对内(mybatis内部)执行SQL语句和管理事务。

2.Executor接口有多个实现类,如SimpleExecutorReuseExecutorBatchExecutorCachingExecutor,分别对应sql不同的执行方式和缓存策略。

3.DefaultSqlSession根据配置文件中的defaultExecutorType属性来选择使用哪种Executor

4.由SqlSession进行管理,一个SqlSession对象对应一个自己的Executor对象。数据库会话结束时可以执行关闭方法,销毁Executor对象。

3.SqlSession和Executor的联系跟区别

  • SqlSession是对外(使用者)提供的接口,Executor是内部代码的操作类。大部分情况下用户无法感知到Executor。

如下使用mybatis查询sql时,SqlSession的api即可全部满足要求。插件等情况除外。

  • SqlSession是一个数据库会话对象,它封装了数据库连接和Executor对象,每个SqlSession都有一个自己的Executor对象。

  • SqlSession提供的一系列的方法都是通过调用Executor对象的相应方法来实现的,所以Executor中的方法大多在SqlSession中能找到相同的。

源码解析

以下按xml配置文件读取形式解析源码。

1.SqlSession跟Executor对象的创建流程

main方法代码如下,SqlSession对象是通过SqlSessionFactory#openSession创建的。

追踪debug到DefaultSqlSessionFactory#openSessionFromDataSource

1.通过事务工厂TransactionFactory#newTransaction创建事务。

2.调用Configuration#newExecutor,传入新创建的事务tx,需要创建的Executor类型execType,创建Executor对象。

3.调用DefaultSqlSession构造方法创建SqlSession对象。

先看DefaultSqlSession的构造方法,只是单纯的属性赋值,没有其他操作,所以SqlSession的对象创建过程到此结束了。

核心主要在Executor对象的创建,我们继续看Configuration#newExecutor

1. 默认的Executor类型为简单类型:SIMPLE,我们最常用的也是该类型。

2. new了一个SimpleExecutor对象

3. 如果开启了二级缓存,通过装饰器模式对Executor对象进行包装。这部分逻辑将会在其他文章进行说明。

4. Executor对象创建完成,调用插件的拦截方法,提供给用户对Executor对象进行操作的切入点。

继续看SimpleExecutor构造方法。调用了BaseExecutor的构造方法,这里没有做额外操作,只是属性的赋值。至此SqlSession跟Executor对象的创建便结束。

2.SqlSession方法的使用及解析

SqlSession方法较多,主要分为以下几种类型

  1. 数据库数据操作 。如selectList,update,delete等。
  2. 事务管理 。如commit,rollback等。
  3. 数据库连接管理 。如close
  4. 内部类管理 。如Mapper获取getMapper,缓存清理 clearCache等。

1.数据库数据操作

selectList为例进行讲解。

使用方式

1.第一个参数statement,填写需要执行sql的类路径名+方法名称

2.第二个参数 parameter,填写sql参数值。

其他重载的selectList方法执行方式类似,不再放出示例。

对应的Mapper

步骤解析

通过debug定位到了DefaultSqlSession#selectList

1.获取查询的mapper方法对应的ms对象,MappedStatement对象在mybatis启动时对sql进行了解析与封装,后续文章会单独进行说明。

2.使用executor#query执行真正的查询逻辑

为了简便步骤,把二级缓存开关配置关闭了

ini 复制代码
<setting name="cacheEnabled" value="false"/>

定位到BaseExecutor#query,判断了一级缓存中是否存在对应key存在则直接读取缓存数据返回,不存在再查数据库,调用queryFromDatabase

debug到SimpleExecutor#doQuery,看到查询方法使用StatementHandler#query去执行。

1.通过Statement#execute执行sql,Statement为jdbc提供的接口,包装了sql语句、sql参数、连接信息等。

2.通过ResultSetHandler#handleResultSets转换sql结果。

以上便是SqlSession#selectList,及其他数据库操作类型方法在mybatis中执行流程,总结如下

  1. SqlSession获取sql对应的处理器对象MappedStatement,转交给Ececutor
  2. Executor处理一些sql外的步骤,如缓存获取等,sql的执行是调用jdbc接口去实现的,sql的内容存储在Statement中,这个对象也是jdbc定义的。
  3. 执行完sql后,Executor会使用ResultSetHandler对结果进行转换,如转换为具体对象。

2.事务管理

commit为例进行讲解。

使用方式

执行完更改语句后,调用commit方法,由于当前数据库事务隔离级别是可重复读(REPEATABLE READ),所以在同一个事务中,多次查询结果是一样的,这里通过多个Session的方式验证未commit的数据

可重复读级别下,update之后开启的事务,只能获取到已提交事务的数据改动的,未提交事务的数据改动是查询不到的,这里代码验证了commit成功提交了事务.

执行结果

步骤解析

看看调用SqlSession#commit后会执行什么操作,通过debug定位到了DefaultSqlSession#commit

跟前面一样,还是通过Executor#commit去执行真正的逻辑。

Executor#commit步骤如下

  1. 清空一级缓存,一级缓存存放在Executor下的PerpetualCache内。
  2. 清空statements对象,这个是Executor类型为批量等清空才会用到,平时我们不用。
  3. 调用事务管理器,我们常用JDBC进行事务管理,让JDBC执行commit方法。

以上便是SqlSession#commit,及其他事务相关方法在mybatis中执行流程,总结如下

  1. SqlSession调用Ececutor的同名方法
  2. Executor处理一些事务外的步骤,如缓存清空等,事务的管理是调用jdbc接口去实现的。

3.数据库连接管理

close为例进行讲解。

使用方式

需要执行的sql都执行完成后,就可以关闭会话归还资源了,如数据库连接资源等,执行close方法即可。

步骤解析

老规矩,通过debug定位到了DefaultSqlSession#close 也是通过Executor#close去执行真正的逻辑。

Executor#close步骤如下

  1. 清空一级缓存,如果开启强制回滚还会将当前事务先回滚
  2. 调用事务管理器,我们常用JDBC进行事务管理,让JDBC事务管理器执行close方法关闭事务及连接。

先分析rollback源码

  1. 清空一级缓存。
  2. 清空statement,我们平时用的Executor类型这个方法可以忽略,批量类型的才有作用。
  3. 如果开启强制回滚还会将当前事务先回滚。

再分析transaction#close源码,这里以JDBC类型的事务管理器为例。核心方法在于Connection#close,这里要注意,如果连接是非连接池类型,则close会直接关闭数据库连接,如果连接时连接池类型,则close会向连接池返还连接。

以下是连接池类型的Connection对象,可以看到close会向数据库连接池返还连接

以上便是数据库连接管理方法SqlSession#close在mybatis中执行流程,总结如下

  1. SqlSession调用Ececutor的同名close方法
  2. Executor处理一些事务外的步骤,如缓存清空等,事务的管理是调用jdbc接口去实现的。
  3. jdbc事务会被关闭,同时数据库连接根据连接是否池类型进行返还或者关闭。

4.内部类管理

getMapper为例进行讲解。

使用方式

getMapper能根据Class类型获取到被mybatis加工后的代理Mapper对象,这个代理对象跟我们平时Spring中获取到的Mapper bean作用是相同的。调用代理对象的对应方法能执行到mapper xml文件中的sql

步骤解析

这次不是通过Executor去执行真正的逻辑,而是通过Configuration

先分析rollback源码

  1. 清空一级缓存。
  2. 清空statement,我们平时用的Executor类型这个方法可以忽略,批量类型的才有作用。
  3. 如果开启强制回滚还会将当前事务先回滚。

继续debug,执行了MapperRegistry#getMapper

  1. 根据Mapper类从knownMappers中获取合适的MapperProxyFactory(mapper对象工厂),knownMappers是mybatis启动时便匹配完成的。
  2. 执行匹配到的MapperProxyFactorynewInstance方法

核心方法在此,这里能看懂代理的逻辑,也就是为什么执行mapper代理对象的方法,会执行mapper xml文件同名方法的sql。

这里是通过jdk动态代理生成的代理对象,Proxy#newProxyInstance是mybatis为了方便稍微做下封装。

既然是jdk动态代理,我们就看代理的InvocationHandler#invoke方法,这里实现InvocationHandler的是MapperProxy

MapperProxy#invoke源码

  1. 如果代理对象执行的方法是源自Object的,如toString,hashCode等,则直接执行。 2.如果代理对象执行的方法不是源自Object的,则调用MapperMethodInvoker#invoke

根据MapperMethodInvoker#invoke一路debug到MapperMethod#execute

该方法总结如下:根据sql类型的不同,调用SqlSession的对应操作方法,如insert调用insert,update调用update,select调用不同的select,我这里是调用了selectOne。

SqlSession#selectOne的逻辑可以参考1.数据库数据操作 中SqlSession#select逻辑的解析。到此源码解析已经完成,这也解释了为什么执行代理的Mapper对象,能执行到同名mapper xml文件中的同名方法的sql

最后总结

SqLSession是mybatis对外提供操作的类,几乎所有mybatis相关操作,如 数据库数据操作、事务管理、数据库连接管理、内部类管理 都可以通过调用SqLSession中的方法完成。

Executor是mybatis对内操作的类,SqLSession数据库数据操增删查改、事务管理 的主要步骤,基本都是调用Executor来完成的。

相关推荐
风景的人生16 小时前
mybatis映射时候的注意点
java·mybatis
玄〤16 小时前
MyBatis-Plus 核心功能详解:条件构造器、Service 封装与批量优化实践(黑马springcloud微服务课程)(day2)
spring cloud·微服务·mybatis
loading小马21 小时前
Mybatis-Plus超级实用的多种功能用法
java·spring boot·后端·maven·mybatis
高山上有一只小老虎1 天前
mybatisplus分页查询版本 3.5.8 以下和版本 3.5.9及以上的区别
java·spring boot·mybatis
人道领域1 天前
javaWeb从入门到进阶(MyBatis拓展)
java·tomcat·mybatis
J2虾虾1 天前
SpringBoot和mybatis Plus不兼容报错的问题
java·spring boot·mybatis
pp起床2 天前
【苍穹外卖】Day03 菜品管理
java·数据库·mybatis
九皇叔叔2 天前
【01】SpringBoot3 MybatisPlus 工程创建
java·mybatis·springboot3·mybatis plus
BD_Marathon2 天前
MyBatis逆向工程之清晰简洁版
mybatis
九皇叔叔2 天前
【02】SpringBoot3 MybatisPlus 加入日志功能
java·mysql·mybatis·日志·mybatisplus