mybatis源码解析3——Executor

经历了上一篇mybaits的基础架构部分的configuration的源码解析,也深感自身功力不足,也可能是configuration是复杂基础组件,一套下来感觉有点恶心吃不消,后面总算是有模有样的缕清了关系。言归正传,依旧继续上一个例子,这篇来解析下Executor。

	public int update(int id) {
		SqlSession session = SQLSessionFactory.getSessoinFactory().openSession();
		int i = session.update(namespace+"updateOne",id);
		session.commit();
		session.close();
		return i;
	}

SqlSession我们已经解析过,Executor是它包装的工作单元,如上面的close、commit、update等方法,实际上是由Executor来执行的。Executor同样是一个接口,同样可以理解为不同的Executor实现不同的策略。


Executor的两个实现子类:


update方法实际被Sqlsession用于update、delete,query被用作selectone、selectlist等,以update方法为例,看下BaseExecutor中的实现:

  @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);//委托方法
  }

我们没有直接看到update操作的逻辑,在update方法中,委托给了doUpdate方法执行,而诸如doUpdate的这些方法则是抽象方法:

  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;

这是一种模板方法模式,baseExecutor实现共性的操作,而具体的操作则由子类完成。而Executor接口的另一个子类CachingExecutor又如何实现update的:

  @Override
  public int update(MappedStatement ms, Object parameterObject) throws SQLException {
    flushCacheIfRequired(ms);
    return delegate.update(ms, parameterObject);//交给委托对象执行
  }

交给一个委托对象delegate执行update,这个委托对象就是Executor对象,而CachingExecutor本身是一个Executor的装饰器:

  private final Executor delegate;
  private final TransactionalCacheManager tcm = new TransactionalCacheManager();

  public CachingExecutor(Executor delegate) {
    this.delegate = delegate;
    delegate.setExecutorWrapper(this);//回调委托对象设置装饰器
  }

CachingExecutor不是baseExecutor的子类而是兄弟类,因此他肯定是对baseExecutor的子类进行包装,什么时候使用何种executor?返回它的创建者(configuration)就知道了:

 public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;//如果没有传入类型,则使用默认类型
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;//如果默认类型为空,则使用simpleExecutor
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);//批处理executor
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);//重用executor
    } else {
      executor = new SimpleExecutor(this, transaction);//简单executor
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);//缓存executor
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

加上baseExecutor的子类,一共有4种executor,下面我们一个个分析他们即可。根据上面的注释,默认情况下,会优先使用默认类型,而默认类型就是XMLConfigBuilder从配置文件中sql节点的配置属性解析映射而来的。如果没有设置,那么默认都是使用simple类型:configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));

还有一种情况,就是传入的executor类型会是什么?结合之前的流程,我们知道一个Sqlsession中会持有一个executor,但是Sqlsession并不负责executor的创建,因此应该从应用程序中update的上一个步骤来入手:SqlSession session = SQLSessionFactory.getSessoinFactory().openSession();前几个例子和这里因为我写了一个工具类来获取sessionFactory,所以实际上我们还是得先研究sessionFactory:

sessionFactory是一个接口,这个接口限定openSession返回的session必需是从连接或数据源创建的:

/**
 * Creates an {@link SqlSession} out of a connection or a DataSource
 * //创建一个必需是从连接或数据源创建的SqlSession
 * @author Clinton Begin
 */
public interface SqlSessionFactory {

  SqlSession openSession();//实际用到的只有这两个方法
  SqlSession openSession(boolean autoCommit);
  SqlSession openSession(Connection connection);//这个接口方法及下面的方法均是用户管理方法
  SqlSession openSession(TransactionIsolationLevel level);

  SqlSession openSession(ExecutorType execType);
  SqlSession openSession(ExecutorType execType, boolean autoCommit);
  SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
  SqlSession openSession(ExecutorType execType, Connection connection);

  Configuration getConfiguration();

}
它的两个实现子类分别是:

现在聚焦DefaultSqlSessionFactory,了解他是如何创建SqlSession的。上面说到,Sqlsession不负责创建executor,那么executor也是由factory负责创建并传入给session的。创建session都是通过下面两个核心方法产生,一个通过数据源产生,另一个直接由连接对象产生,其实大同小异,数据源只是对连接做了一个封装而已。关键是根据传入的executor类型参数来决定创建哪一种executor,而他们最终都是创建DefaultSqlSession并返回:

  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);
      final Executor executor = configuration.newExecutor(tx, execType);//传入类型创建executor
      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();
    }
  }

  private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
    try {
      boolean autoCommit;
      try {
        autoCommit = connection.getAutoCommit();
      } catch (SQLException e) {
        // Failover to true, as most poor drivers
        // or databases won't support transactions
        autoCommit = true;
      }      
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      final Transaction tx = transactionFactory.newTransaction(connection);
      final Executor executor = configuration.newExecutor(tx, execType);//传入类型创建executor
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

从下面的方法调用说明,Executor的类型跟随configuration默认配置:

  @Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

  @Override
  public SqlSession openSession(boolean autoCommit) {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
  }

如何修改默认设置?参照官方说明,在setting中配置如下条目设置:


最后,限于篇幅,上面提到的4种executor另做分解。总结一下:执行器Executor是一个接口,有四种类型的子类执行器,可通过配置文件的setting进行设定,默认使用simple类型,应用程序中可以通过manager创建所需类型的执行器的SqlSession。

本篇的类关系图总结如下:



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Mybatis是一个轻量级的Java持久层开源框架,它封装了JDBC操作数据库的底层细节,提供了一个简单易用的数据库访问方式。 Mybatis源码分为核心模块和附加模块两部分,核心模块主要包括配置解析、SQL解析、SQL执行等功能,附加模块包括连接池、缓存、事务管理等功能。 在Mybatis源码中,配置解析是其中的关键部分。通过解析mybatis-config.xml配置文件,可以获取到数据库连接信息、映射器配置、插件配置等。在配置解析过程中,Mybatis会对配置文件进行校验,确保配置的正确性。 SQL解析Mybatis的另一个重要功能。Mybatis通过解析Mapper接口中的注解或XML配置文件中的SQL语句,将SQL语句解析为ParameterMapping、BoundSql等对象,并将其封装成一个MappedStatement对象,供后续的SQL执行使用。 SQL执行是Mybatis的核心功能之一。在SQL执行阶段,Mybatis会根据MappedStatement中的信息,获取数据库连接,并执行对应的SQL语句。在执行过程中,Mybatis会通过TypeHandler对参数进行类型转换,并使用ResultSetHandler将查询结果封装成Java对象。 除了核心模块,Mybatis源码还包括了连接池、缓存、事务管理等附加模块的实现。连接池模块负责管理数据库连接的获取和释放,缓存模块负责缓存查询结果以提高性能,而事务管理模块则负责管理数据库的事务处理。 总之,Mybatis源码解析涉及多个关键模块的实现,包括配置解析、SQL解析、SQL执行、连接池、缓存、事务管理等。通过了解这些模块的实现原理,我们可以更好地理解和使用Mybatis框架。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值