该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址Mybatis-Spring 源码分析 GitHub 地址Spring-Boot-Starter 源码分析 GitHub 地址)进行阅读

MyBatis 版本:3.5.2

MyBatis-Spring 版本:2.0.3

MyBatis-Spring-Boot-Starter 版本:2.1.4

MyBatis的SQL执行过程

在前面一系列的文档中,我已经分析了MyBatis的基础支持层以及整个的初始化过程,此时MyBatis已经处于就绪状态了,等待使用者发号施令了

那么接下来我们来看看它执行SQL的过程,该过程比较复杂,因为涉及到二级缓存,将返回结果转换成Java对象和延迟加载等等处理过程,这里将一步一步地进行分析

MyBatis中SQL执行的整体过程如下图所示:

在SqlSession中,会将执行SQL的过程交由Executor执行器去执行,过程大致如下:

  1. 通过DefaultSqlSessionFactory创建一个与数据的 SqlSession 会话,其中会创建一个Executor执行器对象
  2. 然后Executor执行器通过StatementHandler创建对应的java.sql.Statement对象,并通过ParameterHandler设置参数,然后操作数据库
  3. 如果是更新数据的操作,则可能需要通过KeyGenerator设置自增键,返回受影响的行数
  4. 如果是查询操作,则将操作数据库返回的ResultSet结果集对象包装成ResultSetWrapper,然后通过DefaultResultSetHandler对结果集进行映射,返回Java对象

上面还涉及到一级缓存二级缓存延迟加载等其他处理过程

SQL执行过程(一)之Executor

在MyBatis的SQL执行过程中,Executor执行器担当着一个重要的角色,相关操作都需要通过它来执行,相当于一个调度器,把SQL语句交给它,它来调用各个组件执行操作

其中一级缓存和二级缓存都是在Executor执行器中完成的

Executor执行器接口的实现类如下图所示:

  • org.apache.ibatis.executor.BaseExecutor:实现Executor接口,提供骨架方法,支持一级缓存,指定几个抽象的方法交由不同的子类去实现

  • org.apache.ibatis.executor.SimpleExecutor:继承 BaseExecutor 抽象类,简单的 Executor 实现类(默认)

  • org.apache.ibatis.executor.ReuseExecutor:继承 BaseExecutor 抽象类,可重用的 Executor 实现类,相比SimpleExecutor,在Statement执行完操作后不会立即关闭,而是缓存起来,执行的SQL作为key,下次执行相同的SQL时优先从缓存中获取Statement对象

  • org.apache.ibatis.executor.BatchExecutor:继承 BaseExecutor 抽象类,支持批量执行的 Executor 实现类

  • org.apache.ibatis.executor.CachingExecutor:实现 Executor 接口,支持二级缓存的 Executor 的实现类,实际采用了装饰器模式,装饰对象为左边三个Executor类

Executor

org.apache.ibatis.executor.Executor:执行器接口,代码如下:

public interface Executor {
/**
* ResultHandler 空对象
*/
ResultHandler NO_RESULT_HANDLER = null;
/**
* 更新或者插入或者删除
* 由传入的 MappedStatement 的 SQL 所决定
*/
int update(MappedStatement ms, Object parameter) throws SQLException;
/**
* 查询,带 ResultHandler + CacheKey + BoundSql
*/
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
CacheKey cacheKey, BoundSql boundSql) throws SQLException;
/**
* 查询,带 ResultHandler
*/
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)
throws SQLException;
/**
* 查询,返回 Cursor 游标
*/
<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
/**
* 刷入批处理语句
*/
List<BatchResult> flushStatements() throws SQLException;
/**
* 提交事务
*/
void commit(boolean required) throws SQLException;
/**
* 回滚事务
*/
void rollback(boolean required) throws SQLException;
/**
* 创建 CacheKey 对象
*/
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
/**
* 判断是否缓存
*/
boolean isCached(MappedStatement ms, CacheKey key);
/**
* 清除本地缓存
*/
void clearLocalCache();
/**
* 延迟加载
*/
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
/**
* 获得事务
*/
Transaction getTransaction();
/**
* 关闭事务
*/
void close(boolean forceRollback);
/**
* 判断事务是否关闭
*/
boolean isClosed();
/**
* 设置包装的 Executor 对象
*/
void setExecutorWrapper(Executor executor);
}

执行器接口定义了操作数据库的相关方法:

  • 数据库的读和写操作
  • 事务相关
  • 缓存相关
  • 设置延迟加载
  • 设置包装的 Executor 对象

BaseExecutor

org.apache.ibatis.executor.BaseExecutor:实现Executor接口,提供骨架方法,指定几个抽象的方法交由不同的子类去实现,例如:

protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;

protected abstract List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException;

protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, BoundSql boundSql) throws SQLException; protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds,
BoundSql boundSql) throws SQLException;

上面这四个方法交由不同的子类去实现,分别是:更新数据库、刷入批处理语句、查询数据库和查询数据返回游标

构造方法

public abstract class BaseExecutor implements Executor {

	private static final Log log = LogFactory.getLog(BaseExecutor.class);

	/**
* 事务对象
*/
protected Transaction transaction;
/**
* 包装的 Executor 对象
*/
protected Executor wrapper;
/**
* DeferredLoad(延迟加载)队列
*/
protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
/**
* 本地缓存,即一级缓存,内部就是一个 HashMap 对象
*/
protected PerpetualCache localCache;
/**
* 本地输出类型参数的缓存,和存储过程有关
*/
protected PerpetualCache localOutputParameterCache;
/**
* 全局配置
*/
protected Configuration configuration;
/**
* 记录当前会话正在查询的数量
*/
protected int queryStack;
/**
* 是否关闭
*/
private boolean closed; protected BaseExecutor(Configuration configuration, Transaction transaction) {
this.transaction = transaction;
this.deferredLoads = new ConcurrentLinkedQueue<>();
this.localCache = new PerpetualCache("LocalCache");
this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
this.closed = false;
this.configuration = configuration;
this.wrapper = this;
}
}

其中上面的属性可根据注释进行查看

这里提一下localCache属性,本地缓存,用于一级缓存,MyBatis的一级缓存是什么呢?

每当我们使用 MyBatis 开启一次和数据库的会话,MyBatis 都会创建出一个 SqlSession 对象,表示与数据库的一次会话,而每个 SqlSession 都会创建一个 Executor 对象

在对数据库的一次会话中,我们有可能会反复地执行完全相同的查询语句,每一次查询都会访问一次数据库,如果在极短的时间内做了完全相同的查询,那么它们的结果极有可能完全相同,由于查询一次数据库的代价很大,如果不采取一些措施的话,可能造成很大的资源浪费

为了解决这一问题,减少资源的浪费,MyBatis 会在每一次 SqlSession 会话对象中建立一个简单的缓存,将每次查询到的结果缓存起来,当下次查询的时候,如果之前已有完全一样的查询,则会先尝试从这个简单的缓存中获取结果返回给用户,不需要再进行一次数据库查询了

最新文章

  1. oracle创建用户
  2. echo &#39;.SUFFIXES: .cpp&#39; &gt;&gt; ${OUTPUT_FILE}
  3. 传统认知PK网络认知 刚子扯谈烤串认知
  4. 帝国cms 灵动标签
  5. [原] perforce 获取本地最近更新的Changelist
  6. Java5 并发学习
  7. Struct2 向Action中传递参数(中文乱码问题)
  8. HTML5初步——新的表单元素和属性
  9. javascript 中获取对象的长度(map对象的长度)--js关联数组的长度
  10. java四大会话技术
  11. sql语句的一些案列!
  12. 聊一聊快速排序(Js)
  13. Intellij IDEA 环境 tomcat 启动设置
  14. Node.js中文乱码解决方法
  15. const constptr 和引用的盲点(未解决)
  16. WebSphere禁用SSLv3和RC4算法教程
  17. 【Ansible 文档】【译文】配置文件
  18. [USACO16JAN]子共七Subsequences Summing to Sevens
  19. HTTP协议GET和POST的区别
  20. DPDK(mtcp)vs RDMA/ROCE

热门文章

  1. C# / VB.NET 在PPT中创建、编辑PPT SmartArt图形
  2. 【服务总线 Azure Service Bus】ServiceBus 队列中死信(DLQ - Dead Letter Queue)问题
  3. 字节跳动2020Java面经,你离高薪就只差一片面试题了
  4. 【bug录】安装项目编译环境bug录
  5. R语言学习网站(分享)
  6. git 移除远程仓库关联
  7. Git系列:常用命令
  8. Django项目-个人网站之投票模块
  9. Bucardo使用文档-lottu
  10. php的三元运算符