Mybatis源码阅读(四):核心接口4.2——Executor(下)

*************************************优雅的分割线 **********************************

分享一波:程序员赚外快-必看的巅峰干货

如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程

请关注微信公众号:HB荷包
在这里插入图片描述
一个能让你学习技术和赚钱方法的公众号,持续更新
*************************************优雅的分割线 **********************************
SimpleExecutor

接上一章博客继续。

SImpleExecutor继承了BaseExecutor类,是最简单的Executor实现。Executor使用了模板方法模式,所以SimpleExecutor不必在关心一级缓存等操作,只需要实现基本的4个方法。

首先看doQuery

/*** 执行查询操作* @param ms* @param parameter* @param rowBounds* @param resultHandler* @param boundSql* @param <E>* @return* @throws SQLException*/
@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();// 创建RoutingStatementHandler,根据MappedStatement.statementType决定选择具体的StatementHandlerStatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);// 完成Statement的初始化。先创建对应的StatementHandler,再调用parameterize方法处理占位符stmt = prepareStatement(handler, ms.getStatementLog());// 调用StatementHandler.query方法执行SQLreturn handler.query(stmt, resultHandler);} finally {closeStatement(stmt);}
}/*** 创建Statement并处理占位符* @param handler* @param statementLog* @return* @throws SQLException*/
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Statement stmt;Connection connection = getConnection(statementLog);// 创建Statementstmt = handler.prepare(connection, transaction.getTimeout());// 处理占位符handler.parameterize(stmt);return stmt;
}

代码很简单,先获取配置对象,再创建RoutingStatementHandler对象,对Statement进行初始化,最后调用query方法完成查询操作。doQueryCursor、update方法与之类似,不进行介绍。SimpleExecutor不提供批量处理SQL的功能所以doFlushStatement方法直接返回空集合。
ReuseExecutor

传统的JDBC编程中,Statement对象重用是最常见的一种优化手段,这样可以减少SQL的预编译以及创建、销毁Statement对象的开销,从而提高性能。

ReuseExecutor提供了Statement重用的功能,通过statementMap字段缓存使用过的Statement对象,key是sql,value是statement。

该类中的doQuery、doQueryCursor、doUpdate与SimpleExecutor中的实现一样, 不同的是PrepareStatement方法。SimpleExecutor每次都会通过JDBC的Connection创建新的Statement,而ReuseExecutor则先尝试从statementMap中查找缓存的对象。

/*** 默认会从缓存中查找Statement* @param handler* @param statementLog* @return* @throws SQLException*/
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Statement stmt;BoundSql boundSql = handler.getBoundSql();//  获取SQLString sql = boundSql.getSql();if (hasStatementFor(sql)) {stmt = getStatement(sql);// 修改事务超时时间applyTransactionTimeout(stmt);} else {// 获取数据库连接Connection connection = getConnection(statementLog);// 创建新的Statement放到statementMapstmt = handler.prepare(connection, transaction.getTimeout());putStatement(sql, stmt);}handler.parameterize(stmt);return stmt;
}

当事务提交、回滚、连接关闭时,需要销毁这些缓存的Statement对象。在BaseExecutor中commit、rollback、close方法中,都会调用doFlushStatement方法,所以在doFlushStatement方法中关闭Statement比较合适。方法实现如下。

@Override
public List<BatchResult> doFlushStatements(boolean isRollback) {for (Statement stmt : statementMap.values()) {closeStatement(stmt);}statementMap.clear();return Collections.emptyList();
}

BatchExecutor

系统在执行一条sql语句的时候,会将SQQL语句以及相关参数通过网络发送到数据库。对于频繁操作数据库的系统,如果执行一条SQl就向数据库发送一次请求,在网络通信上会有很多性能折损。因此使用批量处理的方式进行优化,缓存多条SQL语句,在合适的时机将多条SQL打包发送给数据库执行,从而减少网络方面的开销,提高性能。

需要注意,批量执行SQL的时候,每次向数据库发送的SQL语句条数是有上限的,如果超出了这个上限,数据库会拒绝执行这些SQL并抛出异常。

BatchExecutor实现了批量处理SQL的功能,核心字段如下。

/*** 缓存多个Statement,每个Statement都缓存了多条SQL*/
private final List<Statement> statementList = new ArrayList<>();
/*** 记录批处理的结果*/
private final List<BatchResult> batchResultList = new ArrayList<>();
/*** 记录当前执行的SQL*/
private String currentSql;
/*** 记录当前执行的MappedStatement*/
private MappedStatement currentStatement;

JDBC只支持insert、update、delete的批处理,select不存在批处理一说,因此这里主要分析doUpdate方法。

doUpdate方法在添加一条SQL的时候,会先将currentSql字段记录的SQl以及currentStatement记录的MappedStatement对象与当前添加的SQL对比,如果相同则添加到同一个Statement对象中等待执行,不同则创建新的Statement对象并缓存到statementList集合中等待执行,代码如下。

@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();final String sql = boundSql.getSql();final Statement stmt;// 如果sql和当前sql相同,这里的sql还有问号占位符,并且MappedStatement和当前Statement相同,就添加到同一个Statement对象中等待执行if (sql.equals(currentSql) && ms.equals(currentStatement)) {// 获取最后一个Statement对象int last = statementList.size() - 1;stmt = statementList.get(last);applyTransactionTimeout(stmt);// 绑定实参,处理?占位符handler.parameterize(stmt);//fix Issues 322// 查找对应的BatchResult,记录用户传入的实参BatchResult batchResult = batchResultList.get(last);batchResult.addParameterObject(parameterObject);} else {// 否则创建新的Statement缓存到statementList中等待执行Connection connection = getConnection(ms.getStatementLog());// 创建Statementstmt = handler.prepare(connection, transaction.getTimeout());handler.parameterize(stmt);    //fix Issues 322currentSql = sql;currentStatement = ms;// 添加到statementListstatementList.add(stmt);// 添加新的BatchResultbatchResultList.add(new BatchResult(ms, sql, parameterObject));}handler.batch(stmt);return BATCH_UPDATE_RETURN_VALUE;
}

JDBC的Statement可以添加不同模式的SQL,每添加一个新模式的SQl就会触发一次编译操作。而PrepareStatement中只能添加统一模式的SQL语句,只触发一次编译操作,但是可以通过绑定多组不同的参数实现批处理。而BatchExecutor做的就是这件事,将连续添加的、相同模式的SQL语句添加到同一个Statement对象中,从而有效地减少编译次数。

在添加完待执行的SQL之后,doFlushStatement方法会处理这些SQL语句。

@Override
public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {try {// 记录批处理的结果List<BatchResult> results = new ArrayList<>();// 如果明确指定了要回滚事务,则直接返回空集合,忽略statementList中记录的sqlif (isRollback) {return Collections.emptyList();}// 遍历StatementListfor (int i = 0, n = statementList.size(); i < n; i++) {Statement stmt = statementList.get(i);applyTransactionTimeout(stmt);BatchResult batchResult = batchResultList.get(i);try {// 调用批量执行方法,返回int数组,每个元素都表示每条sql影响的记录条数batchResult.setUpdateCounts(stmt.executeBatch());MappedStatement ms = batchResult.getMappedStatement();List<Object> parameterObjects = batchResult.getParameterObjects();// 获取配置的KeyGeneratorKeyGenerator keyGenerator = ms.getKeyGenerator();if (Jdbc3KeyGenerator.class.equals(keyGenerator.getClass())) {Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator;// 获取数据库生成的主键,并配置到parameterObjectsjdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects);} else if (!NoKeyGenerator.class.equals(keyGenerator.getClass())) { //issue #141// 其他类型的KeyGenerator,调用其processAfterfor (Object parameter : parameterObjects) {keyGenerator.processAfter(this, ms, stmt, parameter);}}// Close statement to close cursor #1109closeStatement(stmt);} catch (BatchUpdateException e) {StringBuilder message = new StringBuilder();message.append(batchResult.getMappedStatement().getId()).append(" (batch index #").append(i + 1).append(")").append(" failed.");if (i > 0) {message.append(" ").append(i).append(" prior sub executor(s) completed successfully, but will be rolled back.");}throw new BatchExecutorException(message.toString(), e, results, batchResult);}// 将BatchResult添加到resultsresults.add(batchResult);}return results;} finally {for (Statement stmt : statementList) {closeStatement(stmt);}currentSql = null;statementList.clear();batchResultList.clear();}
}

结语

Executor接口的内容有点多,因此就分成了两篇博客进行介绍,而最后的CachingExecutor是为Mybatis实现二级缓存功能,其中使用了装饰器模式。Mybatis的二级缓存功能在实际开发中很少会使用,因此这里就不进行介绍,感兴趣的朋友可以自己摸索。
*************************************优雅的分割线 **********************************

分享一波:程序员赚外快-必看的巅峰干货

如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程

请关注微信公众号:HB荷包
在这里插入图片描述
一个能让你学习技术和赚钱方法的公众号,持续更新
*************************************优雅的分割线 **********************************

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

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

相关文章

python解椭圆方程的例题_椭圆标准方程典型例题及练习题

椭圆标准方程典型例题例1已知P 点在以坐标轴为对称轴的椭圆上&#xff0c;点P 到两焦点的距离分别为354和352&#xff0c;过P 点作焦点所在轴的垂线&#xff0c;它恰好过椭圆的一个焦点&#xff0c;求椭圆方程&#xff0e; 解&#xff1a;设两焦点为1F 、2F &#xff0c;且3541…

leetcode393. UTF-8 Validation

题目要求 A character in UTF8 can be from 1 to 4 bytes long, subjected to the following rules:For 1-byte character, the first bit is a 0, followed by its unicode code. For n-bytes character, the first n-bits are all ones, the n1 bit is 0, followed by n-1 by…

Mybatis源码阅读(五 ):接口层——SqlSession

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

插入公式_一个小工具,彻底帮你搞定在Markdown中插入公式的问题

在编辑Markdown文档时&#xff0c;插入公式是一个挺麻烦的活儿。需要掌握LaTex语法。我自己看完语法后&#xff0c;直接放弃&#xff0c;这绝对是反人类的语法。&#xff08;好吧&#xff0c;是我不会用...&#xff09;但是&#xff0c;我相信你看了这篇文章后&#xff0c;绝对…

JavaScript数据结构与算法——字典

1.字典数据结构 在字典中&#xff0c;存储的是【键&#xff0c;值】对&#xff0c;其中键名是用来查询特定元素的。字典和集合很相似&#xff0c;集合以【值&#xff0c;值】的形式存储&#xff0c;字典则是用【键&#xff0c;值】对的形式存储。字典也称作映射。 2.创建字典 f…

Mybatis源码阅读(一):Mybatis初始化1.2 —— 解析别名、插件、对象工厂、反射工具箱、环境

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

中西方对时间的差异_中西方时间观念差异 英文

The concept of time(时间观念)①Inchina&#xff0c;words and phrases about time are very general. Forexample&#xff0c;ifyoudatewithsomeone,mostofChineseusedtoanswer: in the afternoon /at night/after a while and so on.Butinwestern,peoplehaveaverystrongconc…

Google 修改 Chrome API,防止隐身模式检测

开发四年只会写业务代码&#xff0c;分布式高并发都不会还做程序员&#xff1f; 在使用 Chrome 浏览网页时&#xff0c;某些网站会使用某种方法来确定访问者是否处于隐身模式&#xff0c;这是一种隐私泄漏行为。Google 目前正在考虑修改 Chrome 的相关 API&#xff0c;来杜绝…

Mybatis源码阅读(一):Mybatis初始化1.1 解析properties、settings

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

亚马逊推荐python_使用python查找amazon类别

我想得到amazon的类别&#xff0c;我计划废弃不用API。我已经取消了http://www.amazon.com&#xff0c;我已经在Shop By Department下拉列表中抓取了所有的类别和子类别&#xff0c;我创建了一个web服务来完成这项工作&#xff0c;代码就在这里route(/hello)def hello():textli…

JavaScript异步基础

唯一比不知道代码为什么崩溃更可怕的事情是&#xff0c;不知道为什么一开始它是工作的&#xff01;在 ECMA 规范的最近几次版本里不断有新成员加入&#xff0c;尤其在处理异步的问题上&#xff0c;更是不断推陈出新。然而&#xff0c;我们在享受便利的同时&#xff0c;也应该了…

Flutter、ReactNative、uniapp对比

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

JavaScript数组方法

一、基本类型和引用类型 数值、字符串、布尔值、undefined、null可以直接写出来&#xff0c;比较简单的数据称为基本类型&#xff0c;在比较的时候&#xff0c;是直接按值比较。对象、函数、数组复杂的数据是引用类型&#xff0c;在比较的时候&#xff0c;是按照地址比较。cons…

nodejs mysql模块_NodeJs使用Mysql模块实现事务处理

依赖模块&#xff1a;1. mysql&#xff1a;https://github.com/felixge/node-mysqlnpm install mysql --save2. async&#xff1a;https://github.com/caolan/asyncnpm install async --save(ps: async模块可换成其它Promise模块如bluebird、q等)因为Node.js的mysql模块本身对于…

计数排序vs基数排序vs桶排序

从计数排序说起 计数排序是一种非基于元素比较的排序算法&#xff0c;而是将待排序数组元素转化为计数数组的索引值&#xff0c;从而间接使待排序数组具有顺序性。 计数排序的实现一般有两种形式&#xff1a;基于辅助数组和基于桶排序。 基于辅助数组 整个过程包含三个数组&…

多线程中ThreadLocal的使用

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

mysql 查看所有表的引擎_MySQL查看数据库、表的占用空间大小以及某个库中所有表的引擎类型...

本文章来给大家介绍一些常用的MySQL查看数据库、表的占用空间大小sql命令吧&#xff0c;希望此教程 对各位同学会有所帮助。查看各库的大小代码如下复制代码SELECT SUM(DATA_LENGTH)SUM(INDEX_LENGTH) FROM information_schema.tables WHERE TABLE_SCHEMAdatabase_name;结果是以…

Fusion组件库是如何支持多语言能力的

随着国际化发展&#xff0c;多语言的需求越来越常见&#xff0c;单一的语言已经远不能满足需求了。作为一个组件库&#xff0c;支持多语言也是基本能力。 多语言功能的本质其实是文本的替换&#xff0c;一个词汇“OK”&#xff0c;在英文语境下是“OK”&#xff0c;日语语境下是…

mysql 存储过程 replace_mysql replace存储过程

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里云数据库专家保驾护航&#xff0c;为用户…

注解版poi操作工具

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…