Executor 执行器【原来Mybatis执行一个sql有这么多类型】今天分享一下 Executor 。它在框架中是具体sql的执行器,sqlSession(门面模式)封装通用的api,把具体操作委派给 Executor 执行,Executor协同BoundSql,StatementHandler,ParameterHandler 和 ResultSetHandler 完成工作 。
它使用装饰器的方式组织 Executor 对象 。如 CachingExecutor 装饰了SimpleExecutor 提供二级缓存功能 。
可以通过插件机制扩展功能 。mybatisplus 就是通过插件机制扩展的功能 。
下面是更新流程,Executor 处于流程中间蓝色部分,缓存执行器,基础执行器,简单执行器三个 Executor 通过责任链的方式组织起来,各司其职,一同完成执行工作 。,可以感受到它的作用是承上启下 。
文章插图
执行器介绍
文章插图
Mybatis 一共提供了四种执行器的实现和一个模板类:
- 基础执行器 BaseExecutor:实现Executor接口的抽象类,实现了框架逻辑,具体的逻辑委派给子类实现 。一级缓存也是在这里实现的 。
- 缓存执行器 CachingExecutor:实现了二级缓存,是jvm级别的全局缓存 。
- 简单执行器 SimpleExecutor:继承自 BaseExecutor,具体执行逻辑的实现 。
- 重用执行器 ReuseExecutor:相同的 sql 只会预编译一次 。
- 批处理执行器 BatchExecutor:批处理执行器 使用 JDBC 的batch API 执行 SQL 的批量操作,如insert 或者 update 。select的逻辑和 SimpleExecutor 的实现一样 。
SimpleExecutor简单执行器顾名思义,处理的逻辑比较简单直接,来一个 sql 预编译一个,处理一个 。示例代码如下:
// 创建 SimpleExecutor SimpleExecutor simpleExecutor = new SimpleExecutor(sessionFactory.getConfiguration(),jdbcTransaction);// 获取 MAppedStatement final MappedStatement ms = sessionFactory.getConfiguration().getMappedStatement("example.mapper.UserMapper.getUserByID");final BoundSql boundSql = ms.getBoundSql(1);// 执行 2 次查询simpleExecutor.doQuery(ms, 1, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER, boundSql);simpleExecutor.doQuery(ms, 1, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER, boundSql);
执行结果:[DEBUG][main] o.a.i.l.j.BaseJdbcLogger.debug ==>Preparing: select * from `user` where id = ? [DEBUG][main] m.p.ThresholdInterceptor.intercept ThresholdInterceptor plugin... [DEBUG][main] o.a.i.l.j.BaseJdbcLogger.debug ==> Parameters: 1(Integer) [DEBUG][main] o.a.i.l.j.BaseJdbcLogger.debug <==Total: 1 [DEBUG][main] o.a.i.l.j.BaseJdbcLogger.debug ==>Preparing: select * from `user` where id = ? [DEBUG][main] m.p.ThresholdInterceptor.intercept ThresholdInterceptor plugin... [DEBUG][main] o.a.i.l.j.BaseJdbcLogger.debug ==> Parameters: 1(Integer) [DEBUG][main] o.a.i.l.j.BaseJdbcLogger.debug <==Total: 1
通过日志看到,虽然执行相同的 sql 但是每次都要执行预编译 。这是一个需要优化的点 。ReuseExecutorReuseExecutor 对相同 SQL 重复编译做了优化,相同的 sql 的 Statement 只创建一个 。
示例代码上面一样,只是把 SimpleExecutor 换成 ReuseExecutor。从执行我们看到,Preparing 只有一次,执行结果也是正确的:
[DEBUG][main] o.a.i.l.j.BaseJdbcLogger.debug ==>Preparing: select * from `user` where id = ? [DEBUG][main] m.p.ThresholdInterceptor.intercept ThresholdInterceptor plugin... [DEBUG][main] o.a.i.l.j.BaseJdbcLogger.debug ==> Parameters: 1(Integer) [DEBUG][main] o.a.i.l.j.BaseJdbcLogger.debug <==Total: 1 [DEBUG][main] m.p.ThresholdInterceptor.intercept ThresholdInterceptor plugin... [DEBUG][main] o.a.i.l.j.BaseJdbcLogger.debug ==> Parameters: 1(Integer) [DEBUG][main] o.a.i.l.j.BaseJdbcLogger.debug <==Total: 1
他是怎么做到的呢?翻开代码看看实现,其实逻辑也很简单,用 SQL 当作 key 保存对应的 Statement 来实现重用 。private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Statement stmt;BoundSql boundSql = handler.getBoundSql();String sql = boundSql.getSql();// 关键逻辑,通过 sql 判断是否已经创建了 Statement,如果有则重用 。if (hasStatementFor(sql)) {stmt = getStatement(sql);applyTransactionTimeout(stmt);} else {Connection connection = getConnection(statementLog);stmt = handler.prepare(connection, transaction.getTimeout());putStatement(sql, stmt);}handler.parameterize(stmt);return stmt;}private final Map<String, Statement> statementMap = new HashMap<>();private boolean hasStatementFor(String sql) {try {Statement statement = statementMap.get(sql);return statement != null && !statement.getConnection().isClosed();} catch (SQLException e) {return false;}}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 三国志的作者陈寿原来是 三国志的作者陈寿说过的一句跟书有
- 春招|原来上班不累,累的是不上班
- 王者荣耀阅读理解 原来是王者的前一句是什么?
- Laravel 用户授权原来内置了这么多方法
- Go如何保证gorountine执行完毕后继续执行
- 黑客大神的Weblogic 远程命令执行漏洞分析
- 使用 PDF Mix Tool 执行常见的 PDF 编辑任务
- 用白醋去皱纹原来这么有作用
- 发型|原来这5个生活习惯,会令头发不知不觉掉落!
- 如何使用Python执行js代码