mybatis源码阅读(五) ---执行器Executor

转载自  mybatis源码阅读(五) ---执行器Executor

1. Executor接口设计与类结构图

public interface Executor {ResultHandler NO_RESULT_HANDLER = null;// 执行update,delete,insert三种类型的sql语句int update(MappedStatement ms, Object parameter) throws SQLException;// 执行select类型的SQL语句,返回值分为结果对象列表和游标对象<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;// 批量执行SQL语句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();// 关闭Executor对象void close(boolean forceRollback);// 检测Executor对象是否关闭boolean isClosed();void setExecutorWrapper(Executor executor);}

简单执行器SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。(可以是Statement或PrepareStatement对象)

重用执行器ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。(可以是Statement或PrepareStatement对象)

批量执行器BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理的;BatchExecutor相当于维护了多个桶,每个桶里都装了很多属于自己的SQL,就像苹果蓝里装了很多苹果,番茄蓝里装了很多番茄,最后,再统一倒进仓库。(可以是Statement或PrepareStatement对象)

缓存执行器CachingExecutor:装饰设计模式典范,先从缓存中获取查询结果,存在就返回,不存在,再委托给Executor delegate去数据库取,delegate可以是上面任一的SimpleExecutor、ReuseExecutor、BatchExecutor。

无用执行器ClosedExecutor:毫无用处,读者可自行查看其源码,仅作为一种标识,和Serializable标记接口作用相当。

作用范围:以上这五个执行器的作用范围,都严格限制在SqlSession生命周期范围内。

2. 基类BaseExecutor源码解析

它是一个实现了Executor接口的抽象类,实现了接口中的大部分方法,其中就是使用了模板模式,它主要提供了缓存和事物管理的基本功能,不同的实现类,只要实现4个基本方法来完成数据库的相关操作,这4个抽象方法:doUpdate()、doQuery()、doFlushStatement()、doQueryCursor。

源码片段

protected Transaction transaction;// 实现事务的回滚和提交,关闭操作
protected Executor wrapper; // 其中封装的Executor对象// 延迟加载队列
protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
// 下面两个属性是一级缓存用到的对象
protected PerpetualCache localCache;
protected PerpetualCache localOutputParameterCache;protected Configuration configuration;// 嵌套查询层级
protected int queryStack;
private boolean closed;
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;

2.1 SimpleExecutor

@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);stmt = prepareStatement(handler, ms.getStatementLog());return handler.update(stmt);} finally {closeStatement(stmt);}
}@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);stmt = prepareStatement(handler, ms.getStatementLog());return handler.<E>query(stmt, resultHandler);} finally {closeStatement(stmt);//关闭statement对象}
}

源码很简单,从configuration对象中去材料,交给handler去处理,处理完后,statement对象马上关闭。

2.2 ReuseExecutor

执行器提供了Statement的重用功能,代码片段如下:

// 缓存使用过的Statement对象,key是SQL语句,value是SQL对应的Statement对象
private final Map<String, Statement> statementMap = new HashMap<String, Statement>();
/*** 准备获取Statement对象* @param handler* @param statementLog* @return* @throws SQLException*/
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Statement stmt;BoundSql boundSql = handler.getBoundSql();String sql = boundSql.getSql();if (hasStatementFor(sql)) {// 检测是否缓存了相同模式的SQL语句所对应的Statement对象stmt = getStatement(sql);// 从缓存中获取statement对象applyTransactionTimeout(stmt);// 修改超时时间} else {Connection connection = getConnection(statementLog);stmt = handler.prepare(connection, transaction.getTimeout());putStatement(sql, stmt);}handler.parameterize(stmt);return stmt;
}

那statement对象是什么时候关闭的呢?当事物提交回滚或者关闭时都需要关闭这些缓存的Statement对象,在BaseExecutor.commit(),rollback(),close()方法中都会掉用doFlushStatement()方法,所以在改方法中实现关闭Statement对象是非常合适。具体如下:

@Override
public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {// 遍历map集合并关闭其中的Statement对象for (Statement stmt : statementMap.values()) {closeStatement(stmt);}// 清空缓存statementMap.clear();// 返回空集合return Collections.emptyList();
}

2.3 BatchExecutor

BatchExecutor实现了批处理多条SQL语句的功能,需要注意的是在批处理执行SQL语句时,每次向数据库发送的SQL语句条数是有上限,超过上限会抛出异常,所以批量发送SQL语句的时机是很重要的。

其中的核心字段含义如下:

//缓存多个Statement对象,每个Statement对象中都缓存了多条SQL语句private final List<Statement> statementList = new ArrayList<Statement>();//记录批处理的结果 BatchResult中通过updateCounts字段(int[])记录每个Statement执行批处理的结果private final List<BatchResult> batchResultList = new ArrayList<BatchResult>();// 记录当前执行的SQL语句private String currentSql;// 记录当前的MappedStatement对象private MappedStatement currentStatement;

方法实现如下:

@Override
public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {final Configuration configuration = ms.getConfiguration();final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);final BoundSql boundSql = handler.getBoundSql();// 本次执行的sqlfinal String sql = boundSql.getSql();final Statement stmt;// 如果当前执行的SQL与上一次执行的SQL相同且对应的MappedStatement对象相同if (sql.equals(currentSql) && ms.equals(currentStatement)) {int last = statementList.size() - 1;// 已经存在Statement,取出最后一个Statement,有序stmt = statementList.get(last);applyTransactionTimeout(stmt);handler.parameterize(stmt);//fix Issues 322BatchResult batchResult = batchResultList.get(last);batchResult.addParameterObject(parameterObject);} else {// 尚不存在,新建StatementConnection connection = getConnection(ms.getStatementLog());stmt = handler.prepare(connection, transaction.getTimeout());handler.parameterize(stmt);    //fix Issues 322currentSql = sql;currentStatement = ms;// 放到statementList缓存statementList.add(stmt);batchResultList.add(new BatchResult(ms, sql, parameterObject));}
// handler.parameterize(stmt);// 将sql以addBatch()的方式,添加到Statement中(该步骤由StatementHandler内部完成)handler.batch(stmt);return BATCH_UPDATE_RETURN_VALUE;
}

需要注意的是sql.equals(currentSql)和statementList.get(last),充分说明了其有序逻辑:AABB,将生成2个Statement对象;AABBAA,将生成3个Statement对象,而不是2个。因为,只要sql有变化,将导致生成新的Statement对象。

缓存了这么多Statement批处理对象,何时执行它们?在doFlushStatements()方法中完成执行stmt.executeBatch(),随即关闭这些Statement对象。

2.4 CachingExecutor

CachingExecutor是一个Executor接口的装饰器,它为Executor对象增加了二级缓存的相关功能。

//委托的执行器对象,可以是SimpleExecutor、ReuseExecutor、BatchExecutor任一一个
private final Executor delegate;
//管理使用的二级缓存对像
private final TransactionalCacheManager tcm = new TransactionalCacheManager();

query方法执行的查询操作步骤:

(1)获取BoundSql对象,创建查询语句对应的CacheKey对象,

(2)检测是否开启了二级缓存,如果没有,则指教调用delegate对象的query()方法查询,如果开启了,则继续后面的步骤

(3)检测查询是否包含输出类型的参数,如果是,则报错

(4)调用TransactionalCacheManager.getObject()方法查询二级缓存,如果二级缓存中查找到相应的结果,则直接返回结果。

(5)如果二级缓存没有相应的结果对象,在调用delegate对象的query()方法查询。最后将得到的结果放入

TransactionalCache.entriesToAddOnCommit集合中保存。

@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {// (1)BoundSql boundSql = ms.getBoundSql(parameterObject);CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {Cache cache = ms.getCache();if (cache != null) {// (2)flushCacheIfRequired(ms);if (ms.isUseCache() && resultHandler == null) {// (3)ensureNoOutParams(ms, boundSql);// (4)@SuppressWarnings("unchecked")List<E> list = (List<E>) tcm.getObject(cache, key);if (list == null) {// (5)list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);tcm.putObject(cache, key, list); // issue #578 and #116}return list;}}// 没有启动二级缓存,只调用底层Executor查询return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/322741.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[52ABP实战系列] .NET CORE实战入门第三章更新了

早安 各位道友好&#xff0c;.NET CORE入门视频的第三章也算录制完毕了。欢迎大家上传课网进行学习。 更新速度 大家也知道最近的社会新闻比较多。频繁发生404、关键字打不出来&#xff0c;我个人也在关注这些事件。导致精力分散&#xff0c;没有做到稳定更新&#xff0c;现在呢…

如何安装nuxt

因为vue是单页面应用&#xff0c;所以不被Seo&#xff0c;如百度和Google抓取到&#xff0c;在Vue中如果想要爬虫爬到就必须使用nuxt 那么如何安装使用呢&#xff1f; yarn create nuxt-app <project-name> cd <project-name> yarn build yarn start必须先build&a…

mybatis源码阅读(六) ---StatementHandler了解一下

转载自 mybatis源码阅读(六) ---StatementHandler了解一下 StatementHandler类结构图与接口设计 BaseStatementHandler&#xff1a;一个抽象类&#xff0c;只是实现了一些不涉及具体操作的方法 RoutingStatementHandler&#xff1a;类似路由器&#xff0c;根据配置文件来路由…

基于OIDC(OpenID Connect)的SSO(纯JS客户端)

在上一篇基于OIDC的SSO的中涉及到了4个Web站点&#xff1a; oidc-server.dev&#xff1a;利用oidc实现的统一认证和授权中心&#xff0c;SSO站点。 oidc-client-hybrid.dev&#xff1a;oidc的一个客户端&#xff0c;采用hybrid模式。 oidc-client-implicit.dev&#xff1a;od…

IIS中的 Asp.Net Core 和 dotnet watch

在基于传统的.NET Framework的Asp.Net Mvc的时候&#xff0c;本地开发环境中可以在IIS中建立一个站点&#xff0c;可以直接把站点的目录指向asp.net mvc的项目的根目录。然后build一下就可以在浏览器里面刷新到最新的修改了&#xff0c;也可以附加到w3wp的进程进行调试。但是在…

foreach方法使用

用法 foreach方法主要是针对数组而言的&#xff0c;对数组中的每个元素可以执行一次方法 var array [a, b, c, e]; array.forEach((a)> {console.log(a); });属性 foreach方法主要有三个参数&#xff0c;分别是数组内容、数组索引、整个数组 var array [a, b, c, e]; arra…

.NET Core 控制台程序读 appsettings.json 、注依赖、配日志、设 IOptions

.NET Core 控制台程序没有 ASP.NET Core 的 IWebHostBuilder 与 Startup.cs &#xff0c;那要读 appsettings.json、注依赖、配日志、设 IOptions 该怎么办呢&#xff1f;因为这些操作与 ASP.NET Core 无依赖&#xff0c;所以可以自己动手&#xff0c;轻松搞定。 1、读 appsett…

Object.keys方法拿到对象的key值

项目中的高级搜索选项用到了Object.keys方法&#xff0c; 那么它是用来干嘛的呢&#xff1a;删除某个子对象里的数据 var anObj { aaa: kejin,bbb: shenxian,ccc: yuanshan };let params {...anObj,ddd: luanwu } console.log(params) console.log(Object.keys(anObj)); // …

jsdiff 比较文本内容差异

翻译自 jsdiff JavaScript文本内容差异实现。 基于"An O(ND) Difference Algorithm and its Variations" (Myers, 1986) 中提出的算法 。 安装 npm install diff --save API Diff.diffChars(oldStr, newStr[, options]) -区分两个文本块&#xff0c;逐字符比较。…

Net Core下多种ORM框架特性及性能对比

在.NET Framework下有许多ORM框架&#xff0c;最著名的无外乎是Entity Framework&#xff0c;它拥有悠久的历史以及便捷的语法&#xff0c;在占有率上一路领先。但随着Dapper的出现&#xff0c;它的地位受到了威胁&#xff0c;本文对比了在.NET Core下 两种框架的表现以及与原生…

从ThoughtWorks 2017技术雷达看微软技术

ThoughtWorks在每年都会出品两期技术雷达&#xff0c;这是一份关于技术趋势的报告&#xff0c;它比起一些我们能在市面上见到的其他各种技术行情和预测报告&#xff0c;更加具体&#xff0c;更具可操作性&#xff0c;因为它不仅涉及到新技术大趋势&#xff0c;比如云平台和大数…

Spark入门(一)单主standalone安装

一、集群安装条件前置 实验spark安装在【Hadoop入门&#xff08;二&#xff09;集群安装】机器上&#xff0c; 已完成安装jdk,hadoop和ssh、网络等配置环境等。 spark所依赖的虚拟机和操作系统配置 环境&#xff1a;ubuntu14 spark-2.4.4-bin-hadoop2.6jdk1.8ssh 虚拟机&a…

AutoMapper在asp.netcore中的使用

automapper 是.net 项目中针对模型之间转换映射的一个很好用的工具&#xff0c;不仅提高了开发的效率还使代码更加简洁&#xff0c;当然也是开源的&#xff0c;https://github.com/AutoMapper&#xff0c;这不多做介绍&#xff0c;详细看&#xff0c;官网下面是介绍它在 .net c…

Hadoop生态Zookeeper安装

一、安装条件前置 实验zookeeper安装在【Hadoop入门&#xff08;二&#xff09;集群安装】机器上&#xff0c;已完成安装jdk,hadoop和ssh配置环境等。 zookeeper所依赖的虚拟机和操作系统配置 环境&#xff1a;ubuntu14 apache-zookeeper-3.5.6-bin.tar jdk1.8ssh 虚拟机…

Hangfire在ASP.NET CORE中的简单实现

hangfire是执行后台任务的利器&#xff0c;具体请看官网介绍&#xff1a;https://www.hangfire.io/ 新建一个asp.net core mvc 项目 引入nuget包 Hangfire.AspNetCore hangfire的任务需要数据库持久化&#xff0c;我们在Startup类中修改ConfigureServices 然后在Configure方法中…

Spark入门(二)多主standalone安装

一、集群安装条件前置 实验spark安装在【Hadoop生态Zookeeper安装】机器上&#xff0c; 已完成安装zookeeper、jdk、hadoop和ssh、网络等配置环境等。 spark所依赖的虚拟机和操作系统配置 环境&#xff1a;ubuntu14 spark-2.4.4-bin-hadoop2.6 apache-zookeeper-3.5.6 jd…

Ocelot——初识基于.Net Core的API网关

前言 前不久看到一篇《.NET Core 在腾讯财付通的企业级应用开发实践》&#xff0c;给现在研究.Net Core及想往微服务方向发展的人来了一剂强心针。于是我也就立刻去下Ocelot的源码及去阅读官方文档。 Ocelot的Github地址&#xff1a;https://github.com/TomPallister/Ocelot 官…

forEach和for in

如果是对数组本身进行操作&#xff0c;则必然要用到for循环&#xff0c;如果是依赖数组做其他东西的操作&#xff0c;则可以用 forEach 最经典的两种操作 1、批量获取数组中的单个元素 let _selectSns [];this.multipleSelection.forEach(item > {_selectSns.push(item.sn…

Spark入门(三)Idea构建spark项目

一、依赖包配置 scala与spark的相关依赖包&#xff0c;spark包后尾下划线的版本数字要跟scala的版本第一二位要一致&#xff0c;即2.11 pom.xml <?xml version"1.0" encoding"UTF-8"?><project xmlns"http://maven.apache.org/POM/4.0.…

VS Tools for AI全攻略

大家都知道微软在Connect();17大会上发布了VS Tools for AI&#xff0c;旨在提升Visual Studio和VSCode对日益增长的深度学习需求的体验。看了一圈&#xff0c;网上似乎没有一个完整的中文教程来教你怎么完整配置深度学习方案。 与此同时我也接了一个活&#xff0c;是俱乐部与M…