天天看点

mybatis的解析和运行原理(四)前言:SqlSession下的四大对象总结:

前言:

之前的SqlSession的运行过程还未讲完,还差一个SqlSession下的最重要的四大对象,这一篇文章将会讲解四大对象之间的关系.

SqlSession下的四大对象

之前讲解了映射器其实就是一个动态代理的对象,进入到MapperMethod的execute方法,那么进入到execute的方法又是如何执行的呢?肯定是通过我们所写的Sql语句,但是我们关心的不是这些细节,而是框架的设计这些,现在我们来分析这些对象.

1.Executor 执行器,用于调度其他对象

2.StatementHandler 调用statement执行操作

3.ParameterHandler 处理Sql参数

4.ResultHandler 处理最后的结果集封装并返回

一.Executor

用于与数据库进行交互,为我们提供了查询,更新,事务等方法,这和其他框架并无不同,那么先让我们看看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;
}
           

他会根据你选择的三种执行器的哪一种去创建一个对象,创建对象后回去执行最后这句(Executor) interceptorChain.pluginAll(executor),其实这句就是mybatis的插件,它为我们构建了动态代理对象,然后在Executor执行之前先配置mybatis插件.

然后我们再从他的一个BaseExecutor中的updata再来进行分析

@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.update(stmt);
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    BoundSql boundSql = handler.getBoundSql();
    String sql = boundSql.getSql();
    if (hasStatementFor(sql)) {
        stmt = getStatement(sql);
        applyTransactionTimeout(stmt);
    } else {
        Connection connection = getConnection(statementLog);
        stmt = handler.prepare(connection, transaction.getTimeout());
        putStatement(sql, stmt);
    }
    handler.parameterize(stmt);
    return stmt;
}
           

上面内容很显然Mybatis根据Configuration来构建StatementHandler,然后使用prepareStatement对SQL语句进行编译,它先调用了StatementHandler的prepare()方法进行预编译,然后通过StatementHandler的parameterize()方法来设置参数,resultHandler组装结果并返回结果,看到上面这段代码,我们的注意力就要从Executor中出来了,那么再让我们去分析一下StatementHandler这个对象吧.

二.StatementHandler

StatementHandler专门用来处理数据库之间的会话类,同样的我们来先看看Mybatis如何创建这个类的把.

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);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }
           

这么几句话很显然创建的真实对象其实是RoutingStatementHandler只是实现了StatementHandler这个接口,那么它和Executor一样,都只是用代理对象进行了一步步的封装,他和Executor一样为我们提供了三种执行器分别时SimpleStatementHandler,PreparedStatementHandler,

CallableStatementHandler,他会根据上下文决定创建那个执行器

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());
    }

  }
           

这是RouingStatementHandler类下的构造方法,它提供了一个是配置delegate,他的作用是给实现类对象的使用提供一个统一,简易的适配器.再来看看他的主要三个方法之一的prepare,之后再按照顺序执行parameterize,query

public Statement prepare(Connection connection) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      statement = instantiateStatement(connection);
      setStatementTimeout(statement);
      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);
    }
  }
           

以上代码中的instantiateStatement方法是对SQL进行了预编译和基础配置然后调用parameterize方法去设置参数

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

继续查看query方法

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

现在就非常明确了,在得到DefaultSqlSession对象时初始化了Executor执行器。然后在调用Executor的doQuery方法查询的时候,会初始化StatementHandler对象,然后在初始化StatementHandler的过程中,会初始化ParameterHandler和ResultSetHandler对象,生成的StatementHandler对象会保存这两个对象的引用。直到这时,Executor的职责就差不多完成了,通过其他的三大组件来完成接下来的数据库查询工作并返回结果。

三.ParameterHandler

public interface ParameterHandler {
  Object getParameterObject();
  void setParameters(PreparedStatement ps)
      throws SQLException;
}
           

getParameterObject方法作用是返回参数对象,setParameter设置预编译SQL语句的参数.

那么我们来分析一下setParameter的实现吧.

@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 e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          } catch (SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }
}
           

从parameterObject中取参数,根据typeHandler进行参数处理.typeHandler在Mybatis初始化的时候,就已经注册在了Configuration中.

四.ResultHandler

接口代码我就不贴出来了,根据前面的描述它只是对最终的结果进行了封装处理,我就不在这里多废话了.

总结:

SqlSession是通过Executor创建StatementHandler来运行,二StatementHandler要进过下面三步:

1.prepared 预编译

2.parameterize 设置参数

3. query 执行SQL.

以上就是SqlSession的执行过程,明白了四大对象是如何协作的了.

继续阅读