补充上一章没有讲解的三个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 }
作者:彼岸舞
时间:2020 323
内容关于:Mybatis
本文部分来源于网络,只做技术分享,一概不负任何责任