mybatis-3.4.6 SqlSession执行过程

系列

  • mybatis-3.4.6 配置介绍
  • mybatis-3.4.6 顶层配置解析
  • mybatis-3.4.6 子配置解析
  • mybatis-3.4.6 mapper解析
  • mybatis-3.4.6 SQL执行流程
  • mybatis-3.4.6 SqlSession执行过程
  • mybatis-3.4.6 缓存介绍
  • mybatis-3.4.6 自增主键
  • mybatis-3.4.6 foreach 自增主键
  • mybatis-3.4.6 事务管理


开篇

  • 这个系列是基于mybatis-3.4.6版本的源码解析,这篇文章是梳理基于SqlSessionFactory的mybatis的整体执行流程,文章更侧重于整体流程的梳理。
  • 在mybatis-3.4.6 SQL执行流程的文章中已经分析了SQL执行的流程并定位最终有SqlSession来完成最后的SQL执行过程。
  • 在这篇文章中具体分析下SqlSession的执行过程。


JDBC执行Statement步骤

jdbc执行Statement的核心步骤,mybatis的流程大致如此。

// 驱动注册程序
Class.forName(com.mysql.jdbc.Driver);
// 获取连接对象
Connection conn = DriverManager.getConnection(url,user,password);
// 创建Statement对象
Statement stsm = conn.createStatement();
// 执行sql语句
stsm.executeUpdate(sql);
// 关闭连接
stsm.close();
conn.close();
  • 1、驱动注册程序。
  • 2、获取连接对象。
  • 3、创建Statement对象。
  • 4、执行sql语句。
  • 5、关闭连接。


SqlSession执行过程

mybatis-3.4.6 SqlSession执行过程_第1张图片
SqlSession执行过程
  • DefaultSqlSession执行CachingExecutor,CachingExecutor执行SimpleExecutor,最终执行流程都通过SimpleExecutor来执行。
  • SimpleExecutor核心在于获取connection,初始化statement对象,格式化查询SQL并通过PreparedStatementHandler执行statement。
  • PreparedStatementHandler是真正执行SQL查询和结果处理的核心类。
  • DefaultResultHandler负责处理查询结果的逻辑。
  • SimpleExecutor的prepareStatement内部创建connection和statement的核心逻辑


MapperMethod

public class MapperMethod {

  private final SqlCommand command;
  private final MethodSignature method;

  public MapperMethod(Class mapperInterface, Method method, Configuration config) {
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
  }

  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
          if (method.returnsOptional()
              && (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName()
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }


  private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {
    MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());
  
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));
    } else {
      sqlSession.select(command.getName(), param, method.extractResultHandler(args));
    }
  }

  private  Object executeForMany(SqlSession sqlSession, Object[] args) {
    List result;
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      result = sqlSession.selectList(command.getName(), param, rowBounds);
    } else {
      result = sqlSession.selectList(command.getName(), param);
    }
    // issue #510 Collections & arrays support
    if (!method.getReturnType().isAssignableFrom(result.getClass())) {
      if (method.getReturnType().isArray()) {
        return convertToArray(result);
      } else {
        return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
      }
    }
    return result;
  }
}
  • MapperMethod的SqlCommand创建打通了Dao和Mapper.xml的映射关系,Mapper.xml在Configuration的对象中按照dao的interface+method维度保存了MappedStatement,而SqlCommand的创建就是根据dao的interface+method查询Configuration获得MappedStatement进行后续的所有操作
  • 从MapperMethod的execute可以看出所有的SQL命令本质都是通过sqlSession来完成真正的执行。
  • 那么下一步核心就得研究下sqlSession的执行过程了。


SqlCommand

public class MapperMethod {

  public static class SqlCommand {

    private final String name;
    private final SqlCommandType type;

    public SqlCommand(Configuration configuration, Class mapperInterface, Method method) {
      final String methodName = method.getName();
      final Class declaringClass = method.getDeclaringClass();
      MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
          configuration);
      if (ms == null) {
        if (method.getAnnotation(Flush.class) != null) {
          name = null;
          type = SqlCommandType.FLUSH;
        } else {
        }
      } else {
        name = ms.getId();
        type = ms.getSqlCommandType();
        if (type == SqlCommandType.UNKNOWN) {
          throw new BindingException("Unknown execution method for: " + name);
        }
      }
    }

    private MappedStatement resolveMappedStatement(Class mapperInterface, String methodName,
        Class declaringClass, Configuration configuration) {
      // 根据interface + method去查找对应的MappedStatement对象
      String statementId = mapperInterface.getName() + "." + methodName;
      if (configuration.hasStatement(statementId)) {
        return configuration.getMappedStatement(statementId);
      } else if (mapperInterface.equals(declaringClass)) {
        return null;
      }
      for (Class superInterface : mapperInterface.getInterfaces()) {
        if (declaringClass.isAssignableFrom(superInterface)) {
          MappedStatement ms = resolveMappedStatement(superInterface, methodName,
              declaringClass, configuration);
          if (ms != null) {
            return ms;
          }
        }
      }
      return null;
    }
  }
}
  • resolveMappedStatement根据mapper.xml文件的namespace的namespace + 对应的执行的method去查找对应的MappedStatement对象。


DefaultSqlSession

public class DefaultSqlSession implements SqlSession {

  private final Configuration configuration;
  private final Executor executor;

  private final boolean autoCommit;
  private boolean dirty;
  private List> cursorList;

  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }

  public  List selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      // 查询配置xml解析后的MappedStatement对象
      // statement为interface+method维度,如cn.edu.example.mapper.UserMapper.getById
      MappedStatement ms = configuration.getMappedStatement(statement);
      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();
    }
  }
}
  • DefaultSqlSession从configuration根据interface+method维度获取MappedStatement。
  • MappedStatement的注册本身包含interface+method和method两个维度的key
  • DefaultSqlSession内部通过executor去执行SQL查询。


Executor

public interface Executor {

  ResultHandler NO_RESULT_HANDLER = null;

  int update(MappedStatement ms, Object parameter) throws SQLException;

   List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;

   List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

   Cursor queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;

  List flushStatements() throws SQLException;

  void commit(boolean required) throws SQLException;

  void rollback(boolean required) throws SQLException;

  CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);

  boolean isCached(MappedStatement ms, CacheKey key);

  void clearLocalCache();

  void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class targetType);

  Transaction getTransaction();

  void close(boolean forceRollback);

  boolean isClosed();

  void setExecutorWrapper(Executor executor);

}
mybatis-3.4.6 SqlSession执行过程_第2张图片
Executor
  • Executor的核心类关系如上图所示,核心接口也如上所示。


CachingExecutor

public class CachingExecutor implements Executor {
  // delegate为SimpleExecutor对象
  private final Executor delegate;
  private final TransactionalCacheManager tcm = new TransactionalCacheManager();

  public CachingExecutor(Executor delegate) {
    this.delegate = delegate;
    delegate.setExecutorWrapper(this);
  }

  @Override
  public  List 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  List 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, boundSql);
        @SuppressWarnings("unchecked")
        List list = (List) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    // 执行的逻辑
    return delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
}
  • DefaultSqlSession通过CachingExecutor去执行,在CachingExecutor内部通过SimpleExecutor执行逻辑。
  • CachingExecutor的delegate为SimpleExecutor对象,真正是通过SimpleExecutor去执行query。


SimpleExecutor

public abstract class BaseExecutor implements Executor {

  public  List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {

    List list;
    try {
      queryStack++;
      list = resultHandler == null ? (List) localCache.getObject(key) : null;
      // 核心逻辑先取缓存,没有再从DB进行获取
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        // 从mysql进行查询
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }

    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 {
      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的query调用父类的BaseExecutor的queryFromDatabase,进而调用SimpleExecutor自身的doQuery来实现查询。
public class SimpleExecutor extends BaseExecutor {

  @Override
  public  List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      // 创建RoutingStatementHandler对象
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      // 核心逻辑是创建mysql的stmt对象,内部包含建立connection和创建stmt对象
      stmt = prepareStatement(handler, ms.getStatementLog());
      // 执行query操作
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 获取mysql连接对象
    Connection connection = getConnection(statementLog);
    // 初始化mysql的statement对象
    stmt = handler.prepare(connection, transaction.getTimeout());
    // 初始化mysql的statement的查询参数
    handler.parameterize(stmt);
    return stmt;
  }
}


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);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }
  • SimpleExecutor的configuration.newStatementHandler返回RoutingStatementHandler对象。
  • prepareStatement的负责最核心的connection的建立以及statement的生成,所有的SQL执行是通过statement来完成操作的。
  • prepareStatement核心对statement的查询语句进行了格式化,填充请求参数。


RoutingStatementHandler

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

  public  List query(Statement statement, ResultHandler resultHandler) throws SQLException {
    // 本质上是PreparedStatementHandler对象
    return delegate.query(statement, resultHandler);
  }
}
  • RoutingStatementHandler针对StatementHandler进行二次封装,内部是通过StatementHandler来实现query查询。
  • 这里暂时通过PreparedStatementHandler来进行举例。


PreparedStatementHandler

public class PreparedStatementHandler extends BaseStatementHandler {

  public  List query(Statement statement, ResultHandler resultHandler) throws SQLException {
    // mysql的PreparedStatement对象并执行
    PreparedStatement ps = (PreparedStatement) statement;
    // 执行PreparedStatement
    ps.execute();
    // 通过handleResultSets处理查询结果
    return resultSetHandler. handleResultSets(ps);
  }

  public List handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List multipleResults = new ArrayList();

    int resultSetCount = 0;
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }
}
 
 
  • PreparedStatementHandler的内部执行流程是执行statement并通过handleResultSets来处理结果。


获取connection的过程

public class SimpleExecutor extends BaseExecutor {

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

public abstract class BaseExecutor implements Executor {

  protected Connection getConnection(Log statementLog) throws SQLException {
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
      return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
      return connection;
    }
  }
}
public class JdbcTransaction implements Transaction {

  public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
    dataSource = ds;
    level = desiredLevel;
    autoCommmit = desiredAutoCommit;
  }

  @Override
  public Connection getConnection() throws SQLException {
    if (connection == null) {
      openConnection();
    }
    return connection;
  }

  protected void openConnection() throws SQLException {
     // 通过JDBC的 getConnection来获取connection连接对象
    connection = dataSource.getConnection();
    if (level != null) {
      connection.setTransactionIsolation(level.getLevel());
    }
    setDesiredAutoCommit(autoCommmit);
  }
}
  • connection的openConnection本质是通过dataSource.getConnection()来实现连接的获取。


获取statetment的过程

public abstract class BaseStatementHandler implements StatementHandler {

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


public class PreparedStatementHandler extends BaseStatementHandler {

  @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() != null) {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    } else {

      return connection.prepareStatement(sql);
    }
  }
}
  • statetment的获取本质是通过connection.prepareStatement去实现,中间无非增加了一些proxy的设置。


参考文章

  • 源码参考
  • mybatis官网介绍
  • 深入理解mybatis原理
  • Mybatis3.4.x技术内幕
  • mybatis 3.x源码深度解析与最佳实践

你可能感兴趣的:(mybatis-3.4.6 SqlSession执行过程)