• MyBatis学习之SqlSession下的四大对象详解
  • 发布于 2个月前
  • 200 热度
    0 评论
Mapper 通过类名和方法名就可以匹配到配置的SQL。Mapper 执行的过程是通过 Executor,StatementHandler,ParameterHandler 和 ResultHandler 来完成数据库操作和结果返回的。

四大对象
Executor 代表执行器,由它来调度 StatementHandler,ParameterHandler 和 ResultHandler 等来执行对应的 SQL。
StatementHandler 数据库会话器,作用是使用数据库的 Statement(PreparedStatement)执行操作,它是四大对象的核心,起到承上启下的作用。
ParameterHandler 用于 SQL 对参数的处理。
ResultHandler 是进行最后数据集(ResultSet)的封装返回处理的。
这四大对象的都是在 Configuration 类中创建的,从这可以看出 Configuration 类是 MyBatis 的核心。
public class Configuration {
    
    //堆代码 duidaima.com
    // 参数处理器
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject,
      BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement,
        parameterObject, boundSql);
    return (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
  }

  // 接口处理器
  public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds,
      ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler,
        resultHandler, boundSql, rowBounds);
    return (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
  }

  // 数据库会话器
  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement,
      Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject,
        rowBounds, resultHandler, boundSql);
    return (StatementHandler) interceptorChain.pluginAll(statementHandler);
  }

  // 执行器
  public Executor newExecutor(Transaction transaction) {
    return newExecutor(transaction, defaultExecutorType);
  }

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : 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);
    }
    return (Executor) interceptorChain.pluginAll(executor);
  }
执行器
执行器(Executor)是一个真正执行 Java 和 数据库交互的东西,起到至关重要的作用。在 MyBatis 中存在三种执行器,默认在 MyBatis 的 Configuration 类中创建执行器对象,配置文件 setting 元素的属性是 defaultExecutorType。
SIMPLE,简易执行器,默认的执行器。
REUSE,是一种执行器重用预处理语句。
BATCH,执行器重要语句和批量更新,它是针对批量专用的执行器。
创建执行器 Executor 源码逻辑:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  executorType = executorType == null ? defaultExecutorType : 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);
  }
  return (Executor) interceptorChain.pluginAll(executor);
}
配置文件 setting 属性配置:
<settings>
    <setting name="defaultExecutorType" value="SIMPLE"/>
</settings>
MyBatis 会根据配置的类型去确定需要创建三种执行器中的哪一种,在创建对象后,会执行下面这样一行代码。
interceptorChain.pluginAll(executor);
这就是 MyBatis 的插件,这里它将为我们构建一层层的动态代理对象。在调度真实的 Executor 方法之前执行配置插件的代码可以修改。以SIMPLE执行器为例,看看执行器方法内部。
  @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();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler,
          boundSql);
        // 返回PreparedStatement
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

  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;
  }
}
MyBatis 根据 Configuration 来构建 StatementHandler,然后使用 PreparedStatement 方法对 SQL 编译并对参数进行初始化。实现过程是它调用了 StatementHandler 的 prepare() 方法进行预编译和基础设置,然后通过 StatementHandler 的 parameterize() 来设置参数并执行,ResultHandler 再组装查询结果返回给调用方来完成一次查询。

数据库会话器
数据库会话器(StatementHandler)是专门处理数据库会话的。下面看看 MyBatis 如何创建 StatementHandler 的。在 Configuration 类里创建 StatementHandler,很显然,创建的真实对象是 RoutingStatementHandler 对象,它实现了接口 StatementHandler,和 Executor 一样,用代理对象做一层层的封装。。
public class Configuration {
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement,
      Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject,
        rowBounds, resultHandler, boundSql);
    return (StatementHandler) interceptorChain.pluginAll(statementHandler);
  }
}
在初始化 RoutingStatementHandler 对象的时候它会根据上下文环境决定创建哪个 StatementHandler 对象。RoutingStatementHandler 不是我们真实的服务对象,它是通过适配模式找到应的的 StatementHandler 来执行的。StatementHandler 和 Executor 一样分为三种:SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler。
public class RoutingStatementHandler implements StatementHandler {

  // 适配
  private final StatementHandler delegate;

  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds,
      ResultHandler resultHandler, BoundSql boundSql) {

    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }

  }

  @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    return delegate.prepare(connection, transactionTimeout);
  }

  @Override
  public void parameterize(Statement statement) throws SQLException {
    delegate.parameterize(statement);
  }

  @Override
  public void batch(Statement statement) throws SQLException {
    delegate.batch(statement);
  }

  @Override
  public int update(Statement statement) throws SQLException {
    return delegate.update(statement);
  }

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    return delegate.query(statement, resultHandler);
  }

  @Override
  public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
    return delegate.queryCursor(statement);
  }

  @Override
  public BoundSql getBoundSql() {
    return delegate.getBoundSql();
  }

  @Override
  public ParameterHandler getParameterHandler() {
    return delegate.getParameterHandler();
  }
}
数据会话器定义了一个对象的适配器 delegate,它是一个 StatementHandler 接口对象,构造方法根据配置来适配对应的 StatementHandler 对象。它的作用是给实现类对象的使用提供一个统一,简易的使用适配器。此为对象的适配模式,可以让我们使用现有的类和方法对象提供服务,也可以根据实际需求对象屏蔽一些方法,甚至加入新的服务。

以最常用的 PreparedStatementHandler 为例,看看 MyBatis 早怎么执行查询的。看看它的主要三个方法 prepare、parameterize、query 。
public abstract class BaseStatementHandler implements StatementHandler {

  @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      statement = instantiateStatement(connection);
      setStatementTimeout(statement, transactionTimeout);
      setFetchSize(statement);
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }
    
}
prepare: instantiateStatement 方法是对 SQL 进行预编译。还做了一些基础配置,如设置超时、获最的最大行数等。然后 Executor 调用 parameterize 方法去设置参数。
public class PreparedStatementHandler extends BaseStatementHandler {

  @Override
  public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }
    
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.handleResultSets(ps);
  }
    
}
在执行 query 方法前,参数和SQL已经被 prepare 预编译了,参数已经在 parameterize 方法设置了,接下来只是执行 SQL,然后返回结果就可以了。结果可以看到使用了 ResultHandler 进行封装和返回。

一条查询SQL执行过程:
1.Executor 先调用 StatementHandler 的 prepare 方法预编译,同时设置一些基本运行的参数。
2.然后调用 parameterize 方法设置参数,完成预编译。

3.跟着就是执行查询,update() 也是这样,最后如果需要结果,就用 ResultSetHandler 封装结果返回给调用者。


参数处理器
MyBatis 通过参数处理器 ParameterHandler 对预编译语句进行参数设置,作用很明显,就是完成对预编译参数的设置。
public interface ParameterHandler {
  Object getParameterObject();
  void setParameters(PreparedStatement ps) throws SQLException;
}
ParameterHandler 是个接口,提供了两个方法给重写,其中 getParameterObject() 方法的作用是返回参数对象,**setParameters()**作用是设置预编译 SQL 语句的参数。MyBatis 为 ParameterHandler 提供了一个默认实现类 DefaultParameterHandler,看看其中 setParameters() 的实现。
public class DefaultParameterHandler implements ParameterHandler {

  @Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          String propertyName = parameterMapping.getProperty();
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            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);
          }
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException | SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }

}
从代码中可以看到它是从 parameterObject 对象中取参数,然后使用 typeHandler 进行参数处理,如果有显式设置,则会根据签名注册的 typeHandler 对参数进行处理。而 typeHandler 也是在 MyBatis 初始化的时候,注册在 Configuration 里面的,需要的时候可以直接拿来用。这样就完成了参数设置。

结果处理器
结果处理器 ResultSetHandler 是对结果集进行封装返回。
ResultSetHandler接口:
public interface ResultSetHandler {
  <E> List<E> handleResultSets(Statement stmt) throws SQLException;
  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
  void handleOutputParameters(CallableStatement cs) throws SQLException;

}
handleResultSets:包装结果集。
handleCursorResultSets:数据库内处理游标结果集。
handleOutputParameters:处理存储过程输出参数。
MyBatis 同样提供了 ResultSetHandler 默认的实现类 DefaultResultSetHandler,默认情况下都是通过这个类进行处理。这个实现比较复杂,它涉及使用了 JAVASSIST 或者 CGLIB 作为延迟加载,然后通过 typeHandler 和 ObjectFactory 进行组装结果返回,而我们使用它时几乎不会需要改变它。
用户评论