mybatis-executor解析

news/2024/5/19 1:46:22 标签: mysql, java, mybatis, sql, jdbc

概述

在这里插入图片描述

执行器包主要包含了 Executor、ParameterHandler、ResultSetHandler、StatementHandler。

这些都是sql执行中非常重要的一环,本篇从Executor开始。

Executor:执行器,主要职责是在sql执行过程中添加缓存和事务的功能。与jdbc相关的操作会继续委托给StatementHandler。

Executor

java">public interface Executor {

  ResultHandler NO_RESULT_HANDLER = null;

  // 更新
  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;

  // 查询游标
  <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 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();

  // 设置执行器包装类
  void setExecutorWrapper(Executor executor);

}

将Executor提供的方法分类

  • sql操作
  • 缓存
  • 事务

可得出Executor的基本职责。


Executor的实现类:

在这里插入图片描述

  • BaseExecutor:基础执行器,实现Executor方法,添加通用缓存、事务处理,模板方法模式。
  • CachingExecutor:缓存执行器,BaseExecutor内部包含的是一级缓存,CachingExecutor是二级缓存。
  • ReuseExecutor:可重用执行器,会将Statement缓存。
  • SimpleExecutor:基础执行器,值也包含默认数据库执行。
  • BatchExecutor:批处理执行器,可进行批量执行sql(仅限Update)。

BaseExecutor

java">public abstract class BaseExecutor implements Executor {

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

  /**
   * 事务
   */
  protected Transaction transaction;
  /**
   * 包装器
   */
  protected Executor wrapper;

  /**
   * 延迟加载
   */
  protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
  /**
   * 一级缓存
   */
  protected PerpetualCache localCache;
  /**
   * 输出参数缓存
   */
  protected PerpetualCache localOutputParameterCache;
  /**
   * 配置
   */
  protected Configuration configuration;

  /**
   * 查询的深度
   */
  protected int queryStack;
  /**
   * 是否关闭
   */
  private boolean closed;
}

update

java">@Override
public int update(MappedStatement ms, Object parameter) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
  // 关闭状态校验
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  // 清除缓存
  clearLocalCache();
  return doUpdate(ms, parameter);
}

query

java">@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  /*
   * 获取真正的sql,将入参传入,占位符替换
   */
  BoundSql boundSql = ms.getBoundSql(parameter);

  /*
   * 创建一级缓存key
   */
  CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);


  return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
java">@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  if (queryStack == 0 && ms.isFlushCacheRequired()) {
    clearLocalCache();
  }
  List<E> list;
  try {
    queryStack++;

    /*
     * 先尝试从缓存拿
     */
    list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
    if (list != null) {
      // 存储过程时,缓存outPut类型参数
      handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
    } else {
      /*
       * 实时查库
       */
      list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }
  } finally {
    queryStack--;
  }
  
  // 延迟加载处理
  if (queryStack == 0) {
    for (DeferredLoad deferredLoad : deferredLoads) {
      deferredLoad.load();
    }
    // issue #601
    deferredLoads.clear();
    // 缓存是Statement级别,则清楚
    if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
      // issue #482
      clearLocalCache();
    }
  }
  // 返回实体
  return list;
}

commit

java">@Override
public void commit(boolean required) throws SQLException {
  if (closed) {
    throw new ExecutorException("Cannot commit, transaction is already closed");
  }
  // 清除缓存
  clearLocalCache();
  // 刷新statement
  flushStatements();
  if (required) {
    transaction.commit();
  }
}

rollback

java">@Override
public void rollback(boolean required) throws SQLException {
  if (!closed) {
    try {
      clearLocalCache();
      flushStatements(true);
    } finally {
      if (required) {
        transaction.rollback();
      }
    }
  }
}

SimpleExecutor

简单执行器,最常用的执行器。

java">@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
  Statement stmt = null;
  try {
    Configuration configuration = ms.getConfiguration();
    // 创建 statementHandler
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
    // 创建statement实例
    stmt = prepareStatement(handler, ms.getStatementLog());
    // 执行
    return handler.update(stmt);
  } finally {
    closeStatement(stmt);
  }
}

这里步骤很简单,创建StatementHandler和Statement实例,执行sql交给StatementHandler。

java">@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();

    /*
     * 创建一个statement处理器 用于操作jdbc statement
     */
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);

    /*
     * 根据statementType创建对应的statement实例
     * simple
     * prepare
     * callable
     */
    stmt = prepareStatement(handler, ms.getStatementLog());

    /*
     * 执行sql并转换返回值
     * 这里handler类型是RoutingStatementHandler
     * 根据org.apache.ibatis.mapping.MappedStatement.statementType
     * 路由到真正的StatementHandler
     */
    return handler.query(stmt, resultHandler);
  } finally {
    closeStatement(stmt);
  }
}

query和update逻辑相同。

创建Statement实例

java">private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  // 获取数据源连接,这里会对Connection创建代理类,添加了日志打印的功能
  Connection connection = getConnection(statementLog);
  //获取一个Statement实例
  stmt = handler.prepare(connection, transaction.getTimeout());

  //往statement里设置参数
  handler.parameterize(stmt);
  return stmt;
}

ReuseExecutor

可复用Statement实例执行器。SimpleExecutor中Statement是一次性的,用完马上关闭。

java">private final Map<String, Statement> statementMap = new HashMap<>();

存储Statement缓存,key是 动态sql。其他与SimpleExecutor完全相同。

BatchExecutor

批量执行器,可批量执行sql。仅限update(jdbc仅支持批量update)

实在用的少,略过。

CachingExecutor

缓存执行器,是mybatis二级缓存机制的实现类。

java">public class CachingExecutor implements Executor {

  private final Executor delegate;
  /**
   * 缓存跨SqlSession,所以需要事务控制
   */
  private final TransactionalCacheManager tcm = new TransactionalCacheManager();
}

这里也是典型的装饰模式,执行器的具体实现都委托给Delegate执行。自己在之上添加缓存功能。

这里主要看下query方法,看下缓存的处理

java">@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) {
    // 是否需要刷新缓存
    flushCacheIfRequired(ms);
    // 是否使用缓存
    if (ms.isUseCache() && resultHandler == null) {
      ensureNoOutParams(ms, boundSql);
      // 读取二级缓存值(受事务管理)
      @SuppressWarnings("unchecked")
      List<E> list = (List<E>) tcm.getObject(cache, key);
      if (list == null) {
        // 二级缓存为null,委托实例查询
        list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        // 设置值到二级缓存
        tcm.putObject(cache, key, list); // issue #578 and #116
      }
      return list;
    }
  }
  
  // 没有缓存容器,说明没有二级缓存
  return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

从代码中可以看出,mybatis的缓存机制顺序

  1. 二级缓存
  2. 一级缓存
  3. 数据库

二级缓存在如今分布式环境下,也使用的非常少。

总结

executor是介于SqlSession和StatementHandler之间的,用于专门处理缓存和事务的。

不对外直接开放,也不与jdbc直接交互。在我们应用开发中,也可以细化dao层,添加缓存事务的这一层。


http://www.niftyadmin.cn/n/1182359.html

相关文章

php英语单词,php常用英语单词,快速学习php编程英语(3)

global object 全局对象global 全局的grant 授权granularity 粒度group 组、群handle 句柄handler 处理器hard disk 硬盘hardware 硬件heap 堆hierarchy 层次结构、继承体系hook 钩子hyperlink 超链接icon 图标identifier 标识符image 图象implement 实现implementation 实现、…

Canvas简单画板

实现功能&#xff1a;canvas画板。 可拖动&#xff0c;可缩放 用JQuery实现 事件&#xff0c; Jquery实现的 缩放和拖放 &#xff0c;所以需要引入相关的JS文件。 下面是代码部分&#xff1a; <!DOCTYPE html> <html xmlns"http://www.w3.org/1999/xhtml"…

mybatis-StatementHandler解析

概述 StatementHandler&#xff0c;Statement处理器&#xff0c;主要是和jdbc中的Statement交互。 public interface StatementHandler {/*** 创建Statement* param connection* param transactionTimeout* return* throws SQLException*/Statement prepare(Connection conne…

数据库xml输出

SELECT xmlelement("root",xmlagg(xmlforest(xmlforest(字段1&#xff0c;字段2&#xff0c;字段3&#xff0c;字段4 ...)TBL))).getclobval() from table where 条件转载于:https://www.cnblogs.com/scbaoma2008/archive/2012/11/02/2751266.html

CentOS查看CPU、内存、网络流量和磁盘 I/O【详细】

2019独角兽企业重金招聘Python工程师标准>>> 安装 yum install -y sysstat sar -d 1 1 rrqm/s: 每秒进行 merge 的读操作数目。即 delta(rmerge)/s wrqm/s: 每秒进行 merge 的写操作数目。即 delta(wmerge)/s r/s: 每秒完成的读 I/O 设备次数。即 delta(rio)/s w/…

PHP最简单的分页!

最近学习php分页&#xff0c;结合他人的思想总结了分页的一点经验&#xff0c;在这里分享一下&#xff0c;第一次写博客&#xff0c;大家交流交流&#xff01; 分页的思想可以根据mysql里的select * from mytable limit 0,10得到&#xff0c;这里0表示第一页开始&#xff0c;10…

mybatis-ResultHandler解析

概述 结果值的处理相当于参数的处理会复杂一些&#xff0c;负责结果值转换的类是 ResultSetHandler public interface ResultSetHandler {/*** 处理数据集并返回* param stmt* param <E>* return* throws SQLException*/<E> List<E> handleResultSets(Stat…

[报告]codeforces 175d Plane of Tanks: Duel

Abstract codeforces 175d Plane of Tanks: Duel dp 概率 乱搞 Source http://codeforces.com/problemset/problem/175/D Description 战车少女互殴 两辆战车对战&#xff0c;战车有如下属性&#xff1a;hp&#xff0c;射击时间间隔&#xff0c;击中目标概率&#xff0c;伤害值…