欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

MyBatis学习笔记(二) Executor

程序员文章站 2022-06-25 15:50:08
一、概述 当我们打开一个SqlSession的时候,我们就完成了操作数据库的第一步,那MyBatis是如何执行Sql的呢?其实MyBatis的增删改查都是通过Executor执行的,Executor和SqlSession绑定在一起,由Configuration类的newExecutor方法创建。 二 ......

一、概述

当我们打开一个sqlsession的时候,我们就完成了操作数据库的第一步,那mybatis是如何执行sql的呢?其实mybatis的增删改查都是通过executor执行的,executor和sqlsession绑定在一起,由configuration类的newexecutor方法创建。

 

二、executor类图

MyBatis学习笔记(二) Executor

首先,顶层接口是executor,有两个实现类,分别是baseexecutor和cachingexecutor,cachingexecutor用于二级缓存,而baseexecutor则用于一级缓存及基础的操作,baseexecutor是一个抽象类,又有三个实现,分别是simpleexecutor,batchexecutor,reuseexecutor,而具体使用哪一个executor则是可以在mybatis-config.xml中进行配置的,配置方式如下:

<settings>
    <!--simple、reuse、batch-->
    <setting name="defaultexecutortype" value="reuse"/>
</settings>

如果没有配置executor,默认情况下是simpleexecutor。

 

三、各个executor介绍

1.baseexecutor

相当于一个基础,实现了executor的方法,但是只是做一些准备工作,比如查询的cachekey定义等,以及公共方法的定义,比如close、commit、rollback方法等,而具体的执行则是定义了抽象方法doupdate、doquery,这些将由baseexecutor的子类实现,如下

baseexecutor这里采用了模板方法模式

  protected abstract int doupdate(mappedstatement ms, object parameter)
      throws sqlexception;

  protected abstract list<batchresult> doflushstatements(boolean isrollback)
      throws sqlexception;

  protected abstract <e> list<e> doquery(mappedstatement ms, object parameter, rowbounds rowbounds, resulthandler resulthandler, boundsql boundsql)
      throws sqlexception;

  protected abstract <e> cursor<e> doquerycursor(mappedstatement ms, object parameter, rowbounds rowbounds, boundsql boundsql)
      throws sqlexception;

 

1.1 simpleexecutor

最简单的执行器,根据对应的sql直接执行,每执行一次update或select,就开启一个statement对象,用完立刻关闭statement对象。(可以是statement或preparestatement对象)

 

1.2 batchexecutor

执行update(没有select,jdbc批处理不支持select),将所有sql都添加到批处理中(addbatch()),等待统一执行(executebatch()),它缓存了多个statement对象,每个statement对象都是addbatch()完毕后,等待逐一执行executebatch()批处理的;batchexecutor相当于维护了多个桶,每个桶里都装了很多属于自己的sql,就像苹果蓝里装了很多苹果,番茄蓝里装了很多番茄,最后,再统一倒进仓库。(可以是statement或preparestatement对象)

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

  @override
  public int doupdate(mappedstatement ms, object parameterobject) throws sqlexception {
    final configuration configuration = ms.getconfiguration();
    final statementhandler handler = configuration.newstatementhandler(this, ms, parameterobject, rowbounds.default, null, null);
    final boundsql boundsql = handler.getboundsql();
    final string sql = boundsql.getsql();
    final statement stmt;
    if (sql.equals(currentsql) && ms.equals(currentstatement)) {
      int last = statementlist.size() - 1;
      stmt = statementlist.get(last);
      applytransactiontimeout(stmt);
     handler.parameterize(stmt);//fix issues 322
      batchresult batchresult = batchresultlist.get(last);
      batchresult.addparameterobject(parameterobject);
    } else {
      connection connection = getconnection(ms.getstatementlog());
      stmt = handler.prepare(connection, transaction.gettimeout());
      handler.parameterize(stmt);    //fix issues 322
      currentsql = sql;
      currentstatement = ms;
      statementlist.add(stmt);
      batchresultlist.add(new batchresult(ms, sql, parameterobject));
    }
  // handler.parameterize(stmt);
    handler.batch(stmt);
    return batch_update_return_value;
  }

 

1.3 reuseexecutor

可重用的执行器,重用的对象是statement,也就是说该执行器会缓存同一个sql的statement,省去statement的重新创建,优化性能。内部的实现是通过一个hashmap来维护statement对象的。由于当前map只在该session中有效,所以使用完成后记得调用flushstatements来清除map。
private final map<string, statement> statementmap = new hashmap<string, statement>();

 

2.cachingexecutor

先从缓存中获取查询结果,存在就返回,不存在,再委托给executor delegate去数据库取,delegate可以是上面任一的simpleexecutor、reuseexecutor、batchexecutor。

这几个executor的生命周期都是局限于sqlsession范围内。

  public cachingexecutor(executor delegate) {
    this.delegate = delegate;
    delegate.setexecutorwrapper(this);
  }

......

  @override
  public int update(mappedstatement ms, object parameterobject) throws sqlexception {
    flushcacheifrequired(ms);
    return delegate.update(ms, parameterobject);
  }

  @override
  public <e> list<e> query(mappedstatement ms, object parameterobject, rowbounds rowbounds, resulthandler resulthandler) throws sqlexception {
    boundsql boundsql = ms.getboundsql(parameterobject);
    cachekey key = createcachekey(ms, parameterobject, rowbounds, boundsql);
    return query(ms, parameterobject, rowbounds, resulthandler, key, boundsql);
  }

  @override
  public <e> cursor<e> querycursor(mappedstatement ms, object parameter, rowbounds rowbounds) throws sqlexception {
    flushcacheifrequired(ms);
    return delegate.querycursor(ms, parameter, rowbounds);
  }

  @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, parameterobject, boundsql);
        @suppresswarnings("unchecked")
        list<e> list = (list<e>) tcm.getobject(cache, key);
        if (list == null) {
          list = delegate.<e> query(ms, parameterobject, rowbounds, resulthandler, key, boundsql);
          tcm.putobject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.<e> query(ms, parameterobject, rowbounds, resulthandler, key, boundsql);
  }

......

 

四、executor初始化

1.先调用startmanagedsession

  public void startmanagedsession() {
    this.localsqlsession.set(opensession());
  }

2.调用startmanagedsession方法来使sqlsessionmanager内部维护的localsqlsession变量生效,这一步操作中会涉及到对executor的获取,代码如下:

 @override
  public sqlsession opensession() {
    return sqlsessionfactory.opensession();
  }


 @override
  public sqlsession opensession() {
    return opensessionfromdatasource(configuration.getdefaultexecutortype(), null, false);
  }

  private sqlsession opensessionfromdatasource(executortype exectype, transactionisolationlevel level, boolean autocommit) {
    transaction tx = null;
    try {
      final environment environment = configuration.getenvironment();
      final transactionfactory transactionfactory = gettransactionfactoryfromenvironment(environment);
      tx = transactionfactory.newtransaction(environment.getdatasource(), level, autocommit);
      //这里根据不同的executortype获取不同的executor 
      final executor executor = configuration.newexecutor(tx, exectype);
      return new defaultsqlsession(configuration, executor, autocommit);
    } catch (exception e) {
      closetransaction(tx); // may have fetched a connection so lets call close()
      throw exceptionfactory.wrapexception("error opening session.  cause: " + e, e);
    } finally {
      errorcontext.instance().reset();
    }
  }

3.最后就是文章开头所说的newexecutor方法创建返回不同的executor 

  public executor newexecutor(transaction transaction, executortype executortype) {
    executortype = executortype == null ? defaultexecutortype : executortype;
    executortype = executortype == null ? executortype.simple : executortype;
    executor executor;
    if (executortype.batch == executortype) {
      executor = new batchexecutor(this, transaction);
    } else if (executortype.reuse == executortype) {
      executor = new reuseexecutor(this, transaction);
    } else {
      executor = new simpleexecutor(this, transaction);
    }
    if (cacheenabled) {
      executor = new cachingexecutor(executor);
    }
    executor = (executor) interceptorchain.pluginall(executor);
    return executor;
  }

 

参考文章:

https://www.jianshu.com/p/53cc886067b1

 

https://blog.csdn.net/cleargreen/article/details/80614362