Mybatis源码学习第六天(核心流程分析)之Executor分析(补充)

时间:2022-07-26
本文章向大家介绍Mybatis源码学习第六天(核心流程分析)之Executor分析(补充),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

补充上一章没有讲解的三个Executor执行器;

还是贴一下之前的代码吧;我发现其实有些分析注释还是写在代码里面比较好,方便大家理解,之前是我的疏忽,不好意思

 1 @Override
 2   public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
 3     Statement stmt = null;
 4     try {
 5       Configuration configuration = ms.getConfiguration();
      // 第一个执行器 StatementHandler 创建 其实StatementHandler用的也是模板模式
 6       StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      // StatementHandler对象创建stmt,ParameterHandler对参数进行处理
 7       stmt = prepareStatement(handler, ms.getStatementLog());
      // 通过StatementHandler对象调用ResultSetHandler将结果集转化为指定对象返回
 8       return handler.<E>query(stmt, resultHandler);
 9     } finally {
10       closeStatement(stmt);
11     }
12   }

StatementHandler继承体系 BaseStatementHandler构建了骨架,下面的三个子类对他的特定方法做了实现;

还是说一下吧;

StatementHandler完成了Mybatis最核心的工作,也是Executor实现的基础,功能包括:创建Statement对象,为Sql语句绑定参数,执行增删改查等Sql语句,将结果映射集进行转化;

BaseStatementHandler:所有子类的抽象父类,定义了初始化Statement的操作顺序,由子类实现具体的实例化不同的statement(模板模式);

RoutingStatementHandler:Executor组件真正实例化的子类使用静态代理模式,根据上下文决定创建那个具体实体类;

SimpleStatementHandler:使用Statement对象访问数据库,无需参数化;

PrepareStatementHandler:使用预编译PrepareStatement对象访问数据库;

CallableStatementHandler:调用存储过程;

BaseStatementHandler定义的骨架

 1 @Override
 2   public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
 3     ErrorContext.instance().sql(boundSql.getSql());
 4     Statement statement = null;
 5     try {
      // 这个instantiateStatement这个方法就是交给子类去实现的 simple prepare callbale
 6       statement = instantiateStatement(connection);
 7       setStatementTimeout(statement, transactionTimeout);
 8       setFetchSize(statement);
 9       return statement;
10     } catch (SQLException e) {
11       closeStatement(statement);
12       throw e;
13     } catch (Exception e) {
14       closeStatement(statement);
15       throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
16     }
17   }

返回之前的SimpleExecutor,从这行代码进去看看

1 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
1 public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
     // 在这里突然有点发懵,之前说了那么多,咋到这变了? 别着急进去看看
2     StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
3     statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
4     return statementHandler;
5   }
 1 public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
 2     // 进来后发现 是使用的静态代理模式 根据上下文MappedStatement动态决定创建那个具体的子类 赋值给包装的 StatementHandler
 3     switch (ms.getStatementType()) {
 4       case STATEMENT:
 5         delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
 6         break;
 7       case PREPARED:
 8         delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
 9         break;
10       case CALLABLE:
11         delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
12         break;
13       default:
14         throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
15     }
16 
17   }

1 private final StatementHandler delegate;

1 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
2     Statement stmt;
    // 上次说到这里还重点说了一下 日志模块的优雅嵌入
3     Connection connection = getConnection(statementLog);
    // 开始构建stmt  这个方法就进入到了上面的BaseStatementHandler的骨架中 通过不同的StatementHandler,利用connection创建(prepare)statement
4     stmt = handler.prepare(connection, transaction.getTimeout());
    // 使用ParameterHandler进行占位符参数处理
5     handler.parameterize(stmt);
6     return stmt;
7   }

在SimpleStatementHandler中参数是没有必要处理的,因为statement中是没有 ? 占位符的,所以他 虽然实现了这个方法 但是却是空的

1 @Override
2   public void parameterize(Statement statement) throws SQLException {
3     // N/A
4   }

在PreppareStatementHandler中做了一层封装,也就是调用ParameterHandler进行处理,

1 @Override
2   public void parameterize(Statement statement) throws SQLException {
3     parameterHandler.setParameters((PreparedStatement) statement);
4   }

看一下参数处理器的数据结构

  
1   private final TypeHandlerRegistry typeHandlerRegistry; // 类型处理器注册中心
2   private final MappedStatement mappedStatement; // 对应的Sql节点的信息
3   private final Object parameterObject; // 用户传入的参数
4   private final BoundSql boundSql; // Sql语句的信息,包含?占位符和参数名称
5   private final Configuration configuration; 
 1 @Override
 2   public void setParameters(PreparedStatement ps) {
 3     ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
      // 从boundSql中获取Sql语句的占位符和对应参数的信息
 4     List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
 5     if (parameterMappings != null) {
 6       for (int i = 0; i < parameterMappings.size(); i++) {
 7         ParameterMapping parameterMapping = parameterMappings.get(i);
 8         if (parameterMapping.getMode() != ParameterMode.OUT) { //对于存储过程中的参数不处理
 9           Object value;//绑定的实参
10           String propertyName = parameterMapping.getProperty(); // 参数的名字
11           if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params 获取对应的实参值
12             value = boundSql.getAdditionalParameter(propertyName);
13           } else if (parameterObject == null) {
14             value = null;
15           } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
16             value = parameterObject;
17           } else {
18             MetaObject metaObject = configuration.newMetaObject(parameterObject);
19             value = metaObject.getValue(propertyName);
20           }
21           TypeHandler typeHandler = parameterMapping.getTypeHandler();//从parameterMapping中获取TypeHandler对象
22           JdbcType jdbcType = parameterMapping.getJdbcType();//获取参数对应的JDBCType
23           if (value == null && jdbcType == null) {
24             jdbcType = configuration.getJdbcTypeForNull();
25           }
26           try {
          // 为Statement中的占位符绑定参数
27             typeHandler.setParameter(ps, i + 1, value, jdbcType);
28           } catch (TypeException e) {
29             throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
30           } catch (SQLException e) {
31             throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
32           }
33         }
34       }
35     }
36   }
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  // 获取Sql
  String sql = boundSql.getSql();
  // 执行
  statement.execute(sql);
  // ResultSetHandler处理结果
  return resultSetHandler.<E>handleResultSets(statement);
}
 1 @Override
 2   public List<Object> handleResultSets(Statement stmt) throws SQLException {
 3     ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
 4     // 用于保存结果集对象
 5     final List<Object> multipleResults = new ArrayList<>();
 6 
 7     int resultSetCount = 0;
 8     ResultSetWrapper rsw = getFirstResultSet(stmt);//可能返回多个结果集,先取第一个
 9 
10     List<ResultMap> resultMaps = mappedStatement.getResultMaps();//获取对应的ResultMap 其实就是获取映射规则
11     int resultMapCount = resultMaps.size();
12     validateResultMapsCount(rsw, resultMapCount);//验证都不能为空,否则抛异常
13     while (rsw != null && resultMapCount > resultSetCount) {
14       ResultMap resultMap = resultMaps.get(resultSetCount);//获取当前结果集对应的ResultMap
15       handleResultSet(rsw, resultMap, multipleResults, null);//根据映射规则对结果集进行转换,转化成目标对象放入multipleResultSet中
16       rsw = getNextResultSet(stmt);//获取下一个结果集
17       cleanUpAfterHandlingResultSet();//清空 nestedResultObjects对象
18       resultSetCount++;
19     }
20     // 获取多个结果集,多结果集一般出现在存储过程中,返回多个ResultSet
      // mappedStatement.resultSets属性列出多了结果集的名称,用逗号分隔
      // 多结果集的处理不是重点,暂时不分析
21     String[] resultSets = mappedStatement.getResultSets();
22     if (resultSets != null) {
23       while (rsw != null && resultSetCount < resultSets.length) {
24         ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
25         if (parentMapping != null) {
26           String nestedResultMapId = parentMapping.getNestedResultMapId();
27           ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
28           handleResultSet(rsw, resultMap, null, parentMapping);
29         }
30         rsw = getNextResultSet(stmt);
31         cleanUpAfterHandlingResultSet();
32         resultSetCount++;
33       }
34     }
35 
36     return collapseSingleResultList(multipleResults);
37   }
 1 private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
 2     try {
 3       if (parentMapping != null) {//处理多结果集的嵌套映射
 4         handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
 5       } else {
 6         if (resultHandler == null) { //如果resultHandler为空 实例化一个默认的
 7           DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
 8           handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);//对resultSet进行映射,映射结果暂存在resultHandler中
 9           multipleResults.add(defaultResultHandler.getResultList());//将暂存在resultHandler中的映射结果填充到multipleResults
10         } else {
11           handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
12         }
13       }
14     } finally {
15       // issue #228 (close resultsets)
16       closeResultSet(rsw.getResultSet());
17     }
18   }
1 public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
2     if (resultMap.hasNestedResultMaps()) {//处理有嵌套ResultMap的情况
3       ensureNoRowBounds();
4       checkResultHandler();
5       handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
6     } else {//处理没有嵌套的情况
7       handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
8     }
9   }
 1 private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
 2       throws SQLException {
 3     DefaultResultContext<Object> resultContext = new DefaultResultContext<>();//创建结果上下文,所谓的上下文就是专门在循环中缓存结果对象的
    
 4     skipRows(rsw.getResultSet(), rowBounds);// 根据分页信息 定位到指定的记录
 5     while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {//判断是够需要映射后续结果,实际还是分页处理,避免超过limit
 6       ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);//进一步完善ResultMap信息,主要是处理鉴别器的信息
 7       Object rowValue = getRowValue(rsw, discriminatedResultMap);//读取ResultSet中 的一行记录并进行映射,转化并返回目标对象
 8       storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());//保存映射结果对象
 9     }
10   }

在这里说一下为什么不用Mybatis的rowBounds进行分页,因为他是把数据全部加载过来后,通过移动游标进行逻辑分页的效率差,数据量大的时候性能慢;分页就是为了网络传输性能快,数据量小;

 1 private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
 2     if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
 3       if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) {
 4         rs.absolute(rowBounds.getOffset());
 5       }
 6     } else {// 如果不能准确定位 就需要一下一下的调用next
 7       for (int i = 0; i < rowBounds.getOffset(); i++) {
 8         rs.next();
 9       }
10     }
11   }
 1 private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
 2     final ResultLoaderMap lazyLoader = new ResultLoaderMap();
      // 根据ResultMap的type属性实例化目标对象
 3     Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
 4     if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
      // 对目标对象封装得到metaObject,为后续的赋值操作做准备
 5       final MetaObject metaObject = configuration.newMetaObject(rowValue);
 6       boolean foundValues = this.useConstructorMappings;//取得是否采用构造参数初始化属性值
 7       if (shouldApplyAutomaticMappings(resultMap, false)) {//是否使用自动映射
        // 一般情况下 autoMappingBehavior的默认值为PARTIAL,对为明确指定映射规则的字段进行自动映射
 8         foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
 9       }
       //映射ResultMap中明确指定需要映射的列
10       foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
11       foundValues = lazyLoader.size() > 0 || foundValues;
        // 如果没有一个映射成功的属性,则根据<returnInstanceForEmpty>的配置返回null或者结果对象
12       rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
13     }
14     return rowValue;
15   }

作者:彼岸舞

时间:2020323

内容关于:Mybatis

本文部分来源于网络,只做技术分享,一概不负任何责任