【参考文章】:Mybatis-Executor解析

1. Executor的继承结构

   

2. Executor(顶层接口)

  定义了执行器的一些基本操作;

  1. public interface Executor {
  2.  
  3. ResultHandler NO_RESULT_HANDLER = null;
  4. // 更新
  5. int update(MappedStatement ms, Object parameter) throws SQLException;
  6. // 查询,先查缓存,再查数据库
  7. <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
  8. // 查询
  9. <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
  10.  
  11. <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
  12.  
  13. List<BatchResult> flushStatements() throws SQLException;
  14. // 事务提交
  15. void commit(boolean required) throws SQLException;
  16. // 事务回滚
  17. void rollback(boolean required) throws SQLException;
  18. // 创建缓存的键对象
  19. CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
  20. // 缓存中是否有这个查询的结果
  21. boolean isCached(MappedStatement ms, CacheKey key);
  22. // 清空缓存
  23. void clearLocalCache();
  24.  
  25. void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
  26.  
  27. Transaction getTransaction();
  28.  
  29. void close(boolean forceRollback);
  30.  
  31. boolean isClosed();
  32.  
  33. void setExecutorWrapper(Executor executor);
  34.  
  35. }

3. BaseExecutor

  BaseExecutor是一个抽象类,采用模板方法的设计模式。

  它实现了Executor接口,实现了执行器的基本功能。

  具体使用哪一个Executor则是可以在 mybatis 的 config.xml 中进行配置的。默认为SimpleExecutor;

  配置如下:

  1. <settings>
  2. <!--SIMPLE、REUSE、BATCH-->
  3. <setting name="defaultExecutorType" value="SIMPLE"/>
  4. </settings>

3.1 构造方法

  子类的构造方法会调用 BaseExecutor 的构造方法。

  默认都支持一级缓存;

  1. public abstract class BaseExecutor implements Executor {
  2.  
  3. protected BaseExecutor(Configuration configuration, Transaction transaction) {
  4. this.transaction = transaction;
  5. this.deferredLoads = new ConcurrentLinkedQueue<DeferredLoad>();
  6. // 一级缓存
  7. this.localCache = new PerpetualCache("LocalCache");
  8. this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
  9. this.closed = false;
  10. this.configuration = configuration;
  11. this.wrapper = this;
  12. }
    }

3.2 update

  insert,update,delete操作都会调用此方法;

  调用此方法时会清空一级缓存;

  1. @Override
  2. public int update(MappedStatement ms, Object parameter) throws SQLException {
  3. ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
  4. if (closed) {
  5. throw new ExecutorException("Executor was closed.");
  6. }
  7. // 数据变更操作会清空一级缓存
  8. clearLocalCache();
  9. return doUpdate(ms, parameter);
  10. }

3.3 query

  查询操作会先在缓存中查询,缓存命中失败后再去数据中查询

  1. @Override
  2. public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  3. BoundSql boundSql = ms.getBoundSql(parameter);
  4. // 创建一级缓存的键对象
  5. CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
  6. // 调用下面的 query 方法
  7. return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
  8. }
  9.  
  10. @SuppressWarnings("unchecked")
  11. @Override
  12. public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  13. ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
  14. if (closed) {
  15. throw new ExecutorException("Executor was closed.");
  16. }
  17. if (queryStack == 0 && ms.isFlushCacheRequired()) {
  18. clearLocalCache();
  19. }
  20. List<E> list;
  21. try {
  22. queryStack++;
  23. // 先在缓存中查询,缓存命中失败再去数据库查询
  24. list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
  25. if (list != null) {
  26. handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
  27. } else {
  28. list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
  29. }
  30. } finally {
  31. queryStack--;
  32. }
  33. if (queryStack == 0) {
  34. for (DeferredLoad deferredLoad : deferredLoads) {
  35. deferredLoad.load();
  36. }
  37. // issue #601
  38. deferredLoads.clear();
  39. if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
  40. // issue #482
  41. clearLocalCache();
  42. }
  43. }
  44. return list;
  45. }

3.4 createCacheKey

  一级缓存通过 HashMap 实现,它的键对象根据SQL的ID,参数,SQL本身,分页参数以及JDBC的参数信息构成。

  1. @Override
  2. // 创建CacheKey对象
  3. public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
  4. if (closed) {
  5. throw new ExecutorException("Executor was closed.");
  6. }
  7. CacheKey cacheKey = new CacheKey();
  8. // MappedStatement的id
  9. cacheKey.update(ms.getId());
  10. // 分页参数的offset
  11. cacheKey.update(rowBounds.getOffset());
  12. // 分页参数的limit
  13. cacheKey.update(rowBounds.getLimit());
  14. // SQL语句本身
  15. cacheKey.update(boundSql.getSql());
  16. // 传递给jdbc的参数
  17. List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  18. TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
  19. // mimic DefaultParameterHandler logic
  20. for (ParameterMapping parameterMapping : parameterMappings) {
  21. if (parameterMapping.getMode() != ParameterMode.OUT) {
  22. Object value;
  23. String propertyName = parameterMapping.getProperty();
  24. if (boundSql.hasAdditionalParameter(propertyName)) {
  25. value = boundSql.getAdditionalParameter(propertyName);
  26. } else if (parameterObject == null) {
  27. value = null;
  28. } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
  29. value = parameterObject;
  30. } else {
  31. MetaObject metaObject = configuration.newMetaObject(parameterObject);
  32. value = metaObject.getValue(propertyName);
  33. }
  34. cacheKey.update(value);
  35. }
  36. }
  37. if (configuration.getEnvironment() != null) {
  38. // issue #176
  39. cacheKey.update(configuration.getEnvironment().getId());
  40. }
  41. return cacheKey;
  42. }

3.5定义的抽象方法

  1. // 定义的四个抽象方法,在去掉 do 前缀的相应方法中被调用
  2. protected abstract int doUpdate(MappedStatement ms, Object parameter)
  3. throws SQLException;
  4.  
  5. protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
  6. throws SQLException;
  7.  
  8. protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
  9. throws SQLException;
  10.  
  11. protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
  12. throws SQLException;

4. SimpleExecutor

  最简单的执行器,根据对应的sql直接执行即可,不会做一些额外的操作;

  拼接完SQL之后,直接交给 StatementHandler  去执行。

  1. /**
  2. * Copyright 2009-2016 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.ibatis.executor;
  17.  
  18. import org.apache.ibatis.cursor.Cursor;
  19. import org.apache.ibatis.executor.statement.StatementHandler;
  20. import org.apache.ibatis.logging.Log;
  21. import org.apache.ibatis.mapping.BoundSql;
  22. import org.apache.ibatis.mapping.MappedStatement;
  23. import org.apache.ibatis.session.Configuration;
  24. import org.apache.ibatis.session.ResultHandler;
  25. import org.apache.ibatis.session.RowBounds;
  26. import org.apache.ibatis.transaction.Transaction;
  27.  
  28. import java.sql.Connection;
  29. import java.sql.SQLException;
  30. import java.sql.Statement;
  31. import java.util.Collections;
  32. import java.util.List;
  33.  
  34. /**
  35. * @author Clinton Begin
  36. */
  37. public class SimpleExecutor extends BaseExecutor {
  38.  
  39. public SimpleExecutor(Configuration configuration, Transaction transaction) {
  40. super(configuration, transaction);
  41. }
  42.  
  43. @Override
  44. public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
  45. Statement stmt = null;
  46. try {
  47. Configuration configuration = ms.getConfiguration();
  48. StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
  49. stmt = prepareStatement(handler, ms.getStatementLog());
  50. return handler.update(stmt);
  51. } finally {
  52. closeStatement(stmt);
  53. }
  54. }
  55.  
  56. @Override
  57. public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  58. Statement stmt = null;
  59. try {
  60. Configuration configuration = ms.getConfiguration();
  61. StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
  62. stmt = prepareStatement(handler, ms.getStatementLog());
  63. return handler.<E>query(stmt, resultHandler);
  64. } finally {
  65. closeStatement(stmt);
  66. }
  67. }
  68.  
  69. @Override
  70. protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
  71. Configuration configuration = ms.getConfiguration();
  72. StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
  73. Statement stmt = prepareStatement(handler, ms.getStatementLog());
  74. return handler.<E>queryCursor(stmt);
  75. }
  76.  
  77. @Override
  78. public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
  79. return Collections.emptyList();
  80. }
  81.  
  82. private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  83. Statement stmt;
  84. Connection connection = getConnection(statementLog);
  85. stmt = handler.prepare(connection, transaction.getTimeout());
  86. handler.parameterize(stmt);
  87. return stmt;
  88. }
  89.  
  90. }

5. BatchExecutor

  通过批量操作来优化性能。通常需要注意的是批量更新操作,由于内部有缓存的实现,使用完成后记得调用flushStatements来清除缓存。

  1. public class BatchExecutor extends BaseExecutor {
  2.  
  3. public static final int BATCH_UPDATE_RETURN_VALUE = Integer.MIN_VALUE + 1002;
  4. private final List<Statement> statementList = new ArrayList<Statement>();
  5. private final List<BatchResult> batchResultList = new ArrayList<BatchResult>();
  6. // 上一次的SQL语句
  7. private String currentSql;
  8. // 上一次的MappedStatement 对象
  9. private MappedStatement currentStatement;
  10.  
  11. // 因为调用父类的构造方法,所以 BatchExecutor 自己的私有属性 currentSql和currentStatement 开始都为null
  12. public BatchExecutor(Configuration configuration, Transaction transaction) {
  13. super(configuration, transaction);
  14. }
  15.  
  16. @Override
  17. public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
  18. final Configuration configuration = ms.getConfiguration();
  19. final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
  20. final BoundSql boundSql = handler.getBoundSql();
  21. final String sql = boundSql.getSql();
  22. final Statement stmt;
  23. // 第一次肯定是false,进入else分支,currentSql和currentStatement被初始化,后面进入false分支则进行更新
  24. if (sql.equals(currentSql) && ms.equals(currentStatement)) {
  25. // 取上一次的 Statement 对象
  26. int last = statementList.size() - 1;
  27. stmt = statementList.get(last);
  28. applyTransactionTimeout(stmt);
  29. handler.parameterize(stmt);//fix Issues 322
  30. BatchResult batchResult = batchResultList.get(last);
  31. batchResult.addParameterObject(parameterObject);
  32. } else {
  33. Connection connection = getConnection(ms.getStatementLog());
  34. stmt = handler.prepare(connection, transaction.getTimeout());
  35. handler.parameterize(stmt); //fix Issues 322
  36. // currentSql和currentStatement 更新为此次的对象
  37. currentSql = sql;
  38. currentStatement = ms;
  39. statementList.add(stmt);
  40. batchResultList.add(new BatchResult(ms, sql, parameterObject));
  41. }
  42. // handler.parameterize(stmt);
  43. handler.batch(stmt);
  44. return BATCH_UPDATE_RETURN_VALUE;
  45. }
  46.  
  47. }

6. ReuseExecutor

  可重用的执行器,重用的对象是Statement,也就是说该执行器会缓存同一个sql的Statement,省去Statement的重新创建,优化性能。
  内部的实现是通过一个HashMap来维护Statement对象的。由于当前Map只在该session中有效,所以使用完成后记得调用flushStatements来清除Map。
 
  调用实现的四个抽象方法时会调用 prepareStatement() 
  1. public class ReuseExecutor extends BaseExecutor {
  2.  
  3. private final Map<String, Statement> statementMap = new HashMap<String, Statement>();
  4.  
  5. // 调用父类构造器
  6. public ReuseExecutor(Configuration configuration, Transaction transaction) {
  7. super(configuration, transaction);
  8. }
  9.  
  10. private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  11. Statement stmt;
  12. BoundSql boundSql = handler.getBoundSql();
  13. String sql = boundSql.getSql();
  14. if (hasStatementFor(sql)) {
  15. // 如果缓存了该SQL,则返回其Statement对象
  16. stmt = getStatement(sql);
  17. applyTransactionTimeout(stmt);
  18. } else {
  19. // 如果没有缓存该SQL,则创建SQL的Statement,并加入缓存
  20. Connection connection = getConnection(statementLog);
  21. stmt = handler.prepare(connection, transaction.getTimeout());
  22. putStatement(sql, stmt);
  23. }
  24. handler.parameterize(stmt);
  25. return stmt;
  26. }
  27.  
  28. // 是否缓存了这个 sql
  29. private boolean hasStatementFor(String sql) {
  30. try {
  31. return statementMap.keySet().contains(sql) && !statementMap.get(sql).getConnection().isClosed();
  32. } catch (SQLException e) {
  33. return false;
  34. }
  35. }
  36.  
  37. // 返回指定sql的 Statement
  38. private Statement getStatement(String s) {
  39. return statementMap.get(s);
  40. }
  41.  
  42. // 添加SQL和Statement
  43. private void putStatement(String sql, Statement stmt) {
  44. statementMap.put(sql, stmt);
  45. }
  46.  
  47. }

7. CachingExecutor

  启用于二级缓存时的执行器;

  采用静态代理;代理一个 Executor 对象。
 
  执行 update 方法前判断是否清空二级缓存;
  执行 query 方法前先在二级缓存中查询,命中失败再通过被代理类查询。
  

  1. public class CachingExecutor implements Executor {
  2. // 持有的 Executor,最终的操作都由该对象实现
  3. private final Executor delegate;
  4. private final TransactionalCacheManager tcm = new TransactionalCacheManager();
  5.  
  6. public CachingExecutor(Executor delegate) {
  7. this.delegate = delegate;
  8. delegate.setExecutorWrapper(this);
  9. }
  10.  
  11. public int update(MappedStatement ms, Object parameterObject) throws SQLException {
  12. this.flushCacheIfRequired(ms);
  13. return this.delegate.update(ms, parameterObject);
  14. }
  15.  
  16. public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  17. Cache cache = ms.getCache();
  18. if (cache != null) {
  19. this.flushCacheIfRequired(ms);
  20. if (ms.isUseCache() && resultHandler == null) {
  21. this.ensureNoOutParams(ms, boundSql);
  22. List<E> list = (List)this.tcm.getObject(cache, key);
  23. if (list == null) {
  24. list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  25. this.tcm.putObject(cache, key, list);
  26. }
  27.  
  28. return list;
  29. }
  30. }
  31.  
  32. return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  33. }
  34. // 是否清空二级缓存
  35. private void flushCacheIfRequired(MappedStatement ms) {
  36. Cache cache = ms.getCache();
  37. if (cache != null && ms.isFlushCacheRequired()) {
  38. this.tcm.clear(cache);
  39. }
  40.  
  41. }
  42. }

mybatis四大接口之 Executor的更多相关文章

  1. mybatis四大接口之 StatementHandler

    1. 继承结构 StatementHandler:顶层接口 BaseStatementHandler : 实现顶层接口的抽象类,实现了部分接口,并定义了一个抽象方法 SimpleStatementHa ...

  2. mybatis四大接口之 ResultSetHandler

    1. 继承结构 2. ResultSetHandler public interface ResultSetHandler { // 将Statement执行后产生的结果集(可能有多个结果集)映射为结 ...

  3. mybatis四大接口之 ParameterHandler

    1.  继承结构 只有一个默认的实现类 2. ParameterHandler 获取参数对象: 设置参数对象: public interface ParameterHandler { Object g ...

  4. MyBatis 源码分析——SqlSession接口和Executor类

    mybatis框架在操作数据的时候,离不开SqlSession接口实例类的作用.可以说SqlSession接口实例是开发过程中打交道最多的一个类.即是DefaultSqlSession类.如果笔者记得 ...

  5. MyBatis学习笔记(二) Executor

    一.概述 当我们打开一个SqlSession的时候,我们就完成了操作数据库的第一步,那MyBatis是如何执行Sql的呢?其实MyBatis的增删改查都是通过Executor执行的,Executor和 ...

  6. SqlSession接口和Executor

    mybatis框架在操作数据的时候,离不开SqlSession接口实例类的作用.可以说SqlSession接口实例是开发过程中打交道最多的一个类.即是DefaultSqlSession类.如果笔者记得 ...

  7. Java集合框架之四大接口、常用实现类

    Java集合框架 <Java集合框架的四大接口> Collection:存储无序的.不唯一的数据:其下有List和Set两大接口. List:存储有序的.不唯一的数据: Set:存储无序的 ...

  8. Mybatis中接口和对应的mapper文件命名为什么需要一样?

    背景: 自己对于Mybatis现阶段只处于会用的阶段,有些问题,自己还是想深入的了解一下.就拿Mybatis的接口文件和mapper文件命名需要一致来开始. 解决: 当我们将接口和mapper文件放在 ...

  9. Mybatis Mapper接口是如何找到实现类的-源码分析

    KeyWords: Mybatis 原理,源码,Mybatis Mapper 接口实现类,代理模式,动态代理,Java动态代理,Proxy.newProxyInstance,Mapper 映射,Map ...

随机推荐

  1. delphi 中如何访问另一个类中到私有方法?(转载)

    原始连接 http://rvelthuis.blogspot.tw/2018/01/accessing-private-methods-of-another.html Accessing privat ...

  2. sql相同项求和

    select (SELECT O2.ORG_NAME           FROM OUTSOURCE_ORG O2          where o2.org_id = oo.parent_id) ...

  3. QT5中无法包含Qtgui头文件的问题。

    今天新学QT是,从一个ppt中边看边抄边学.前几页还能理解,但到了用纯源码写空白QT工程时,便遇到了一个问题.头文件里包含 #include  <QtGui> 但是编译时总是出现下面图一中 ...

  4. 【MVC】bootstrap-paginator 分页

    [MVC]bootstrap-paginator 分页http://www.cnblogs.com/stoneniqiu/p/4000041.htmlhttp://www.cnblogs.com/Le ...

  5. CYS-Sqlite数据导入工具

    界面: 曹永思 下载地址:asp.net 2.0版 Sqlite数据导入工具.zip 欢迎转载,转载请注明出处,希望帮到更多人.

  6. 【慕课网实战】Spark Streaming实时流处理项目实战笔记一之铭文升级版

    第一章:课程介绍 铭文一级: VMware Fusion Mac上搭建:为了给大家演示如何使用我们的OOTB环境 Hadoop环境:虚拟机,我是远程登录 Mac 那么就不需要使用我们的OOTB环境 V ...

  7. 07-css的继承性和层叠性

    css有两大特性:继承性和层叠性 继承性 面向对象语言都会存在继承的概念,在面向对象语言中,继承的特点:继承了父类的属性和方法.那么我们现在主要研究css,css就是在设置属性的.不会牵扯到方法的层面 ...

  8. Android自定义视图一:扩展现有的视图,添加新的XML属性

    这个系列是老外写的,干货!翻译出来一起学习.如有不妥,不吝赐教! Android自定义视图一:扩展现有的视图,添加新的XML属性 Android自定义视图二:如何绘制内容 Android自定义视图三: ...

  9. 20169207《Linux内核原理与分析》第八周作业

    本章的作业依旧包括两部分,1.阅读学习教材「Linux内核设计与实现 (Linux Kernel Development)」第教材第11,12章. 2.学习MOOC「Linux内核分析」第六讲「进程的 ...

  10. (不用循环也可以记录数组里的数)Color the ball --hdu--1556

    题目: N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球a开始到气球b依次给每个气球涂一次 ...