MyBatis源码的学习(15)---sqlSession.selectList方法

sqlSession一共俩个实现类,我们这里分析默认的DefaultSqlSession类

public  List selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      //ms对象代表我们的xml中的一条sql。例如:
      MappedStatement ms = configuration.getMappedStatement(statement);
      //如果是二级缓存执行器,就是多了维护二级缓存的操作。我们这里查看最简单的执行器
     //SimpleExecutor
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
// 三种普通的执行器,都是继承了BaseExecutor类
@Override
  public  List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //生成一个完整的sql,会处理动态标签,'${}'赋值,"#{}"替换为占位符'?'
    BoundSql boundSql = ms.getBoundSql(parameter);
    //用于缓存使用,生成唯一的缓存的key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
  }

//CachingExecutor执行器,里面的被包装执行器,最终调用这个方法。因为在CachingExecutor类的
//query方法中,已经生成了BoundSql ,CacheKey 。所以直接调用这个方法
 @Override
  public  List 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.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List list;
    try { //一级缓存(默认是session级别的,和我们的sqlSession对象绑定)的使用,从一级缓存中查询,不存在数据,则查询数据库
      queryStack++;
      list = resultHandler == null ? (List) localCache.getObject(key) : null;
      if (list != null) {
        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
        clearLocalCache();
      }
    }
    return list;
  }

private  List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {//这里将查询的逻辑交给了具体的子类,使用的模板方法的设计模式。同时我们的执行器这块
//也是一个典型的包装设计模式。二级缓存执行器包装下面的三种基本执行器
//我们查看SimpleExecutor执行器的doDuery方法
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

下面我们看类SimpleExecutor的方法doQuery

public  List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      //如果当前执行器有它的包装类,那么wrapper只能是CachingExecutor,否则就是它自己
//这里默认是返回PreparedStatementHandler类型的statement对象
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      //进行了连接的建立,statenment对象的创建,参数的赋值操作。(默认PreparedStatementHandler
//其他的statement步骤差不多)
      stmt = prepareStatement(handler, ms.getStatementLog());
      //进行数据库的操作,并对结果进行处理为java类型 比如:preparedStatement.execute();
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

MyBatis源码的学习(15)---sqlSession.selectList方法_第1张图片

我们的StatementHandler的类结构和我们的执行器的几乎一模一样。很明显这儿也是用到了装饰设计模式,同时也用到了模板方法。

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

MyBatis源码的学习(15)---sqlSession.selectList方法_第2张图片

为何我们常说的四大对象,是可以进行插件拦截。就是因为我们创建好的对象,最终都会经过代理,生成代理对象。

 resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);

 parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
   
 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);

 executor = (Executor) interceptorChain.pluginAll(executor);

查看生成ms对象代码,发现我们的StatementType默认是预编译的。

在MyBatis实现了statementHandler的有四个类:

RoutingStatementHandler,这是一个封装类,它是其他三个的包装类,它不提供具体的实现,只是根据StatementType,创建不同的类型StatementHandler。

SimpleStatementHandler,这个类对应于JDBC的Statement对象,用于没有预编译参数的SQL的运行。

PreparedStatementHandler 这个用于预编译参数SQL的运行,默认的就是预编译。

CallableStatementHandler 存储过程的调度。

      stmt = prepareStatement(handler, ms.getStatementLog());
 //相当于以前我们的jdbc
// connection = JDBCTools.getConnection();
// preparedStatement = connection.prepareStatement(sql);
//preparedStatement.setString(1, "ATGUIGU"); 赋值操作

 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws     SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    //这里不同的handler,返回不同的statement,我们这儿使用默认的,预编译的handler,生成预编译statement对象
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }

//查看代码,由于使用的是PooledDataSource,在获取连接对象的时候,会进行一次代理
protected Connection getConnection(Log statementLog) throws SQLException {
    Connection connection = transaction.getConnection();
//这儿的connection 是一个代理对象
    if (statementLog.isDebugEnabled()) {
      //如果是debug,则再次进行一次代理,好插入记录日志的功能
      return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
      return connection;
    }
  }

public static Connection newInstance(Connection conn, Log statementLog, int queryStack) {
    InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack);
    ClassLoader cl = Connection.class.getClassLoader();
    //使用jdk的动态代理
    return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);
  }

@Override
  public Object invoke(Object proxy, Method method, Object[] params)
      throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, params);
      }
      if ("prepareStatement".equals(method.getName())) {
        //如果是执行的 prepareStatement()方法
        if (isDebugEnabled()) {
          debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
        }
        PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
       //对PrepareStatement进行代理
// preparedStatement = connection.prepareStatement(sql); 返回的是一个代理对象
        stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
        return stmt;
      } else if ("prepareCall".equals(method.getName())) {
        if (isDebugEnabled()) {
          debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
        }
        PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
        stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
        return stmt;
      } else if ("createStatement".equals(method.getName())) {
        Statement stmt = (Statement) method.invoke(connection, params);
        stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);
        return stmt;
      } else {
        return method.invoke(connection, params);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }

MyBatis源码的学习(15)---sqlSession.selectList方法_第3张图片

 我们的connection对象,确实被代理了俩次,所以为了性能考虑,我们线上不要用debug模式。

 //这一行代码,相当于我们jdbc的
//preparedStatement = connection.prepareStatement(sql);
  
 stmt = handler.prepare(connection, transaction.getTimeout()); 

@Override
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
      String[] keyColumnNames = mappedStatement.getKeyColumns();
      if (keyColumnNames == null) {
        return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
      } else {
        return connection.prepareStatement(sql, keyColumnNames);
      }
    } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
//默认是执行这个逻辑,如果是dubug模式,这里返回的statement对象是一个代理对象。可以查看上面
//新建connnection对象的逻辑,所以生产环境不建议用debug模式
      return connection.prepareStatement(sql);
    } else {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
  }

接下来就是参数赋值的操作:

    handler.parameterize(stmt);
    //对应jdbc的  preparedStatement.setString(1, "ATGUIGU"); 赋值操作

在这个方法中,我们主要做的工作就是,遍历我们的参数,然后将参数转换为jdbc类型的参数,然后进行赋值操作。在这里会涉及到类型推断,默认类型。所以,为了性能,我们最好在xml中,指明jdbcType和JavaType。这样直接找到对应的类型处理器,提高效率。

然后,我们返回的就是一个已经完成connection建立,statement对象创建,参数赋值好的statement对象。接下来就是我们的doQuery方法中的:

  return handler.query(stmt, resultHandler);

进行查询数据库的操作。

你可能感兴趣的:(Mybatis源码学习)