Mybatis执行器综述

简介: Mybatis执行器综述

Mybatis执行器综述

这次我们先翻源码,看看executor包下一定会有好多executor类,果然如此

接下来我们看看他们之前有没有什么潜在的关系

public abstract class BaseExecutor implements Executor {
   ......    
}
public class BatchExecutor extends BaseExecutor {
   ......   
}
public class CachingExecutor implements Executor {
   ......  
}
public class ReuseExecutor extends BaseExecutor {
   ......    
}
public class SimpleExecutor extends BaseExecutor {
   ......   
}
public interface Executor {
   ......
}

关系图:

下面我们来分别介绍下

1 Executor接口

Executor是Mybatis中的顶级接口,主要包括对数据库常见方法的定义,源码如下:

public interface Executor {
  ResultHandler NO_RESULT_HANDLER = null;
  //更新操作
  int update(MappedStatement ms, Object parameter) throws SQLException;
  //先查缓存,没有的话查询数据库
  <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;
  //获取结果集
  List<BatchResult> flushStatements() throws SQLException;
  //提交事务
  void commit(boolean required) throws SQLException;
  //事务回滚
  void rollback(boolean required) throws SQLException;
  //创建缓存,返回key
  CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
  //判断缓存的key是否存在
  boolean isCached(MappedStatement ms, CacheKey key);
  //清除本地缓存
  void clearLocalCache();
  //deferLoad
  void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
  //获取事务
  Transaction getTransaction();
  //关闭链接
  void close(boolean forceRollback);
  //检查关闭链接
  boolean isClosed();
  //setExecutorWrapper
  void setExecutorWrapper(Executor executor);
}

2 BaseExecutor执行器

BaseExecutor是一个抽象类,采用模板方法的设计模式。它实现了Executor接口,实现了执行器的基本功能。

2.1 主要功能
  • 通过会话中调用commit、rollback对事务进行管理。
  • 实现了Executor中的Query与update方法。
  • Query方法中处理一级缓存逻辑,即根据SQL及参数判断缓存中是否存在数据,有就走缓存。
  • 在doUpdate() 中主要是用于清空缓存。
2.2 核心源码
//查询方法
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  BoundSql boundSql = ms.getBoundSql(parameter);
  //创建一级缓存
  CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
  return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@SuppressWarnings("unchecked")
@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) {
      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();
    if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
      // issue #482
      clearLocalCache();
    }
  }
  return list;
}
//创建一级缓存的具体实现
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    CacheKey cacheKey = new CacheKey();
    cacheKey.update(ms.getId());
    cacheKey.update(rowBounds.getOffset());
    cacheKey.update(rowBounds.getLimit());
    cacheKey.update(boundSql.getSql());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
    // mimic DefaultParameterHandler logic
    for (ParameterMapping parameterMapping : parameterMappings) {
      if (parameterMapping.getMode() != ParameterMode.OUT) {
        Object value;
        String propertyName = parameterMapping.getProperty();
        if (boundSql.hasAdditionalParameter(propertyName)) {
          value = boundSql.getAdditionalParameter(propertyName);
        } else if (parameterObject == null) {
          value = null;
        } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
          value = parameterObject;
        } else {
          MetaObject metaObject = configuration.newMetaObject(parameterObject);
          value = metaObject.getValue(propertyName);
        }
        cacheKey.update(value);
      }
    }
    if (configuration.getEnvironment() != null) {
      // issue #176
      cacheKey.update(configuration.getEnvironment().getId());
    }
    return cacheKey;
  }

3 CachingExecutor执行器

3.1 主要作用
  • 用于处理二级缓存
  • 缓存事务
3.2 核心源码
@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) {
        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);
}

4 BatchExecutor执行器

BatchExecutor(批处理执行器) 顾名思议,它就是用来作批处理的。但会将所有SQL请求集中起来,最后调用Executor.flushStatements() 方法时一次性将所有请求发送至数据库

4.1 作用
  • 用来作批处理
4.2 源码
@Override
public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
  try {
    List<BatchResult> results = new ArrayList<>();
    if (isRollback) {
      return Collections.emptyList();
    }
    for (int i = 0, n = statementList.size(); i < n; i++) {
      Statement stmt = statementList.get(i);
      applyTransactionTimeout(stmt);
      BatchResult batchResult = batchResultList.get(i);
      try {
        batchResult.setUpdateCounts(stmt.executeBatch());
        MappedStatement ms = batchResult.getMappedStatement();
        List<Object> parameterObjects = batchResult.getParameterObjects();
        KeyGenerator keyGenerator = ms.getKeyGenerator();
        if (Jdbc3KeyGenerator.class.equals(keyGenerator.getClass())) {
          Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator;
          jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects);
        } else if (!NoKeyGenerator.class.equals(keyGenerator.getClass())) { //issue #141
          for (Object parameter : parameterObjects) {
            keyGenerator.processAfter(this, ms, stmt, parameter);
          }
        }
        // Close statement to close cursor #1109
        closeStatement(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);
      }
      results.add(batchResult);
    }
    return results;
  } finally {
    for (Statement stmt : statementList) {
      closeStatement(stmt);
    }
    currentSql = null;
    statementList.clear();
    batchResultList.clear();
  }
}

5 ReuseExecutor执行器

ReuseExecutor (可重用执行器)区别在于他会将在会话期间内的Statement进行缓存,并使用SQL语句作为Key。所以当执行下一请求的时候,不在重复构建Statement,而是从缓存中取出并设置参数,然后执行

5.1 作用
  • 将在会话期间内的Statement进行缓存
5.2 源码
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;
    }
}
private Statement getStatement(String s) {
   return statementMap.get(s);
}
private void putStatement(String sql, Statement stmt) {
   statementMap.put(sql, stmt);
}

6 SimpleExecutor执行器

SimpleExecutor(简单执行器)是默认执行器,它的行为是每处理一次会话当中的SQl请求都会通过对应的StatementHandler 构建一个新个Statement,这就会导致即使是相同SQL语句也无法重用Statement,所以就有了(ReuseExecutor)可重用执行器

6.1 作用
  • 每处理一次会话当中的SQl请求都会通过对应的StatementHandler 构建一个新个Statement
6.2 源码
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  Connection connection = getConnection(statementLog);
  stmt = handler.prepare(connection, transaction.getTimeout());
  handler.parameterize(stmt);
  return stmt;
}


相关文章
|
SQL 缓存 Java
MyBatis-动态代理、执行器与缓存
概述 通过一条修改语句,我们来了解一下Mybatis的执行过程: 一般MyBatis在执行一条语句的时候会依次使用以下四个模块: 分别说下各个组件的作用 接口代理: 其目的是简化对MyBatis使用,底层使用动态代理实现。 Sql会话: 提供增删改查API,其本身不作任何业务逻辑的处理,所有处理都交给执行器。这是一个典型的门面模式设计。 执行器: 核心作用是处理SQL请求、事物管理、维护缓存以及批处理等 。执行器的角色更像是一个管理员,接收SQL请求,然后根据缓存、批处理等逻辑来决定如何执行这个SQL请求。并交给JDBC处理器执行具体SQL。 JDBC处理器:他的作用就是用于通过JDB
64 0
|
6月前
|
Java 关系型数据库 数据库连接
Hasor【环境搭建 02】Dataway接口配置服务使用DataQL聚合查询引擎(Mybatis执行器实现分页查询举例说明+问题分析)
Hasor【环境搭建 02】Dataway接口配置服务使用DataQL聚合查询引擎(Mybatis执行器实现分页查询举例说明+问题分析)
166 0
|
缓存 安全 Java
MyBatis源码-解读Executor的三个实现类之BatchExecutor(批处理执行器)
MyBatis源码-解读Executor的三个实现类之BatchExecutor(批处理执行器)
305 0
|
SQL 缓存 Java
MyBatis源码-解读Executor的三个实现类之ReuseExecutor(重用执行器)
MyBatis源码-解读Executor的三个实现类之ReuseExecutor(重用执行器)
151 0
|
SQL Java 数据库连接
MyBatis源码-解读Executor的三个实现类之SimpleExecutor(简单执行器)
MyBatis源码-解读Executor的三个实现类之SimpleExecutor(简单执行器)
133 0
|
SQL 缓存 Java
Java 最常见的面试题:mybatis 有哪些执行器(Executor)?
Java 最常见的面试题:mybatis 有哪些执行器(Executor)?
112 0
|
SQL 设计模式 缓存
Mybatis执行器综述
Mybatis执行器综述
|
缓存 Java 数据库连接
mybatis源码之执行器解析
mytatis执行器主要职责是翻译jdbc操作,是mybatis非常重要的功能 执行器类图如下 -从上图中可以看出所有执行器都实现了Executor接口,定义了一些通用的操作,Executor的接口定义如下 /** * Copyright 2009-2015 the original author or authors.
|
22天前
|
Java 数据库连接 Maven
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和MyBatis Generator,使用逆向工程来自动生成Java代码,包括实体类、Mapper文件和Example文件,以提高开发效率。
68 2
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
|
22天前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
39 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块