MyBaits系列-MyBatis源码之Executor(一)(5)

知识点
  • Myabtis有哪些Executor执行器,区别是什么?
  • Mybatis如何配置使用哪种Executor?
  • Mybatis如何实现批处理?
  • Mybatis的主键策略,批量写入,能返回数据库主键么?
Executor包的主要结构及其作用

image

Executor 接口及其实现类,使用场景及其调用

image

BaseExecutor

BaseExecutor在创建的时候new了一个本地缓存,每次执行query的时候先从缓存中获取,如果缓存中没有,则从数据库中获取并放到缓存中。代码如下:

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.");
    }
    //先清局部缓存,再查询.但仅查询堆栈为0,才清。为了处理递归调用
    if (queryStack == 0 &amp;&amp; ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<e> list;
    try {
      //加一,这样递归调用到上面的时候就不会再清局部缓存了
      queryStack++;
      //先根据cachekey从localCache去查
      list = resultHandler == null ? (List<e>) localCache.getObject(key) : null;
      if (list != null) {
        //若查到localCache缓存,处理localOutputParameterCache
        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
    	//如果是STATEMENT,清本地缓存
        clearLocalCache();
      }
    }
    return list;
  }

可以看到如果LocalCacheScope为STATEMENT,则每次调用后就清空缓存。那么,LocalCacheScope的值如何配置呢?
Configuration的settings节点配置:SESSION,STATEMENT

从数据库中获取数据:

  public <e> List<e> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.<e> handleResultSets(ps);
  }
SimpleExecutor:

每次获取一个新的statement

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    //调用StatementHandler.prepare
    stmt = handler.prepare(connection);
    //调用StatementHandler.parameterize
    handler.parameterize(stmt);
    return stmt;
  }
ReuseExecutor:

从hashmap中获取statement

  //ReuseExecutor中声明了可重用的一个map,用来缓存SQL语句对应的Statement
 private final Map<string, statement> statementMap = new HashMap<string, statement>();
  ...
 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    //得到绑定的SQL语句
    BoundSql boundSql = handler.getBoundSql();
    String sql = boundSql.getSql();
    //如果缓存中已经有了,直接得到Statement
    if (hasStatementFor(sql)) {
      stmt = getStatement(sql);
    } else {
      //如果缓存没有找到,则和SimpleExecutor处理完全一样,然后加入缓存
      Connection connection = getConnection(statementLog);
      stmt = handler.prepare(connection);
      putStatement(sql, stmt);
    }
    handler.parameterize(stmt);
    return stmt;
  }
 ...
 private Statement getStatement(String s) {
    return statementMap.get(s);
  }
BatchExecutor:可以批量更新。

将多次执行的statement存起来一次执行,核心代码如下:

  @Override
  public void batch(Statement statement) throws SQLException {
    String sql = boundSql.getSql();
    //调用Statement.addBatch
    statement.addBatch(sql);
  }
 

如何配置默认的Excutor?
Configuration的settings节点配置:defaultExecutorType,值为SIMPLE,REUSE,BATCH

CachingExecutor
keygen

keygen为主键生成策略,提供了一下几种策略:

  • Jdbc3KeyGenerator:JDBC3键值生成器,核心是使用JDBC3的Statement.getGeneratedKeys,执行后生成主键。核心代码如下:
//获取主键集合...
ResultSet resultSet = statement.getGeneratedKeys();
//回填到对象...
...
  • SelectKeyGenerator:根据sql生成主键,支持执行前生成主键和执行后生成主键。
//获取主键集合...
Executor keyExecutor = configuration.newExecutor(executor.getTransaction(), ExecutorType.SIMPLE);
      List<object> values = keyExecutor.query(keyStatement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
//回填到对象...
  • NoKeyGenerator:不生成主键

  • 注意:

    • XML配置:如果配置的useGeneratedKeys为true并且执行的insert语句,则默认使用Jdbc3KeyGenerator,否则为NoKeyGenerator
    • 注解方式:如果是insert或者update语句,首选判断@SelectKey注解,其次判断@Options注解,如果没有,则判断默认配置中的useGeneratedKeys,否则根据@Option注解执行。所以,如果配置了全局的useGeneratedKeys为true,则update的时候需要设置 ++@Option(useGeneratedKeys=false)++
if (SqlCommandType.INSERT.equals(sqlCommandType) ||              SqlCommandType.UPDATE.equals(sqlCommandType)) {
        // first check for SelectKey annotation - that overrides everything else
        SelectKey selectKey = method.getAnnotation(SelectKey.class);
        if (selectKey != null) {
          keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
          keyProperty = selectKey.keyProperty();
        } else if (options == null) {
          keyGenerator = configuration.isUseGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
        } else {
          keyGenerator = options.useGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
          keyProperty = options.keyProperty();
          keyColumn = options.keyColumn();
        }
} else {
    keyGenerator = new NoKeyGenerator();
}
//Test1
    @Insert("insert into table2 (name) values(#{name})")
    @SelectKey(statement="select id, name_fred from table2 where id = identity()", keyProperty="nameId,generatedName", keyColumn="ID,NAME_FRED", before=false, resultType=Map.class)
    int insertTable2WithSelectKeyWithKeyMap(Name name);
//Test2
    @Insert("insert into table2 (name) values(#{name})")
    @Options(useGeneratedKeys=true, keyProperty="nameId,generatedName", keyColumn="ID,NAME_FRED")
    int insertTable2WithGeneratedKey(Name name);
```</object></string,></string,></e></e></e></e></e></e></e>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值