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来完成的。

相关推荐
络75 小时前
Spring14——案例:利用AOP环绕通知计算业务层接口执行效率
java·后端·spring·mybatis·aop
企业码道刘某16 小时前
EasyExcel 大数据量导入导出解决方案进阶
java·后端·mybatis
计算机学姐17 小时前
基于微信小程序的调查问卷管理系统
java·vue.js·spring boot·mysql·微信小程序·小程序·mybatis
小徐敲java1 天前
通用mybatis-plus查询封装(QueryGenerator)
mybatis
OEC小胖胖1 天前
Spring Boot + MyBatis 项目中常用注解详解(万字长篇解读)
java·spring boot·后端·spring·mybatis·web
计算机学姐1 天前
基于SpringBoot+Vue的在线投票系统
java·vue.js·spring boot·后端·学习·intellij-idea·mybatis
落落落sss1 天前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
罗曼蒂克在消亡1 天前
2.3MyBatis——插件机制
java·mybatis·源码学习
cyt涛2 天前
MyBatis 学习总结
数据库·sql·学习·mysql·mybatis·jdbc·lombok
OLDERHARD2 天前
Java - MyBatis(上)
java·oracle·mybatis