第八篇:SqlSession.selectList调用过程

之所以要说这个,是因为其他方法都跟这个差不多,而且这个才涉及到参数的填充过程等,这个过程你看懂了,其他的方法就难不倒你了,先看下基本的代码编写

样例

//工厂工具类
public class FactoryUtils {
    public static SqlSessionFactory sqlSessionFactory() {
        return sqlSessionFactory(null);
    }

    public static SqlSessionFactory sqlSessionFactory(Properties properties) {
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new SqlSessionFactoryBuilder().build(inputStream, properties);
    }
}


//sql执行测试类

public class SqlSessionTest {

    public static void main(String[] args) throws Exception{

        //获取SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = FactoryUtils.sqlSessionFactory();

        //打开SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //执行sql语句
        List<User> userList = sqlSession.selectList("com.zxc.study.test.mapper.UserMapper.selectUser", 1);
        //操作返回结构
        System.out.println(userList);
    }
}

源码入口之sqlSession.selectList

org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object, org.apache.ibatis.session.RowBounds)


RowBounds 默认为 RowBounds.DEFAUL
ResultHandler 默认为 NULL


  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      //从配置中获取MappedStatement对象,其实就是一个map,key就是statement组装的
      MappedStatement ms = configuration.getMappedStatement(statement);
      //使用executor对象执行查询逻辑
      //wrapCollection包装参数,有可能传进来的是集合
      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();
    }
  }



第一步就是从一个map中获取 MappedStatement 对象,没什么好看的
第二步才是核心的操作过程

至于 wrapCollection只是判断了一下参数是Collection或者是Array进行了一层包装

Executor(CachingExecutor).query

首先会先到 org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler)中


  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //获取绑定sql对象
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    //主要是为了缓存设计的对象
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    //执行查询数据库操作
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }



这里面设置了两个逻辑

1. 获取绑定sql

 public BoundSql getBoundSql(Object parameterObject) {
    //从sqlSource获取绑定的sql对象
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    //占用传入的参数映射对象,也就是有多少个?就有多少个映射对象
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    //如果没有就新建一个BoundSql对象
    if (parameterMappings == null || parameterMappings.isEmpty()) {
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

    // check for nested result maps in parameter mappings (issue #30)
    //result map相关处理逻辑,目前没有研究
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
      String rmId = pm.getResultMapId();
      if (rmId != null) {
        ResultMap rm = configuration.getResultMap(rmId);
        if (rm != null) {
          hasNestedResultMaps |= rm.hasNestedResultMaps();
        }
      }
    }

    return boundSql;
  }


又涉及了从sqlSource获取绑定sql,比较复杂的是DynamicSqlSource,涉及了<if>标签等的解析逻辑


  @Override
  public BoundSql getBoundSql(Object parameterObject) {
    //上下文信息,主要封装了参数和sqlBuilder构建对象
    DynamicContext context = new DynamicContext(configuration, parameterObject);
    //处理sqlNode对象,把sql拼接到sqlBuilder里面去
    rootSqlNode.apply(context);

    //声明解析器解析#{}的占位符
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
    //里面利用了ParameterMappingTokenHandler对#{}进行解析
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    //再获取boundSql,这是解析好的可以发往数据库的sql了。#{}被替换为?了
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
      boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
    }
    return boundSql;
  }


2. 获取CacheKey,这个就不解析了,就是缓存使用的

这两步做完以后就进入到了



org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)



  @Override
  public <E> List<E> 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<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    //调用真正的查询,也就是会先进到BaseExecutor的query方法,在下一段分析
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

Executor(BaseExecutor).query

org.apache.ibatis.executor.BaseExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)



  @Override
  public <E> List<E> 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<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) 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 <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      //执行查询
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      //先移除缓存
      localCache.removeObject(key);
    }
    //查后的数据放到缓存中
    localCache.putObject(key, list);
    //又放到了一个专门的缓存中,StatementType.CALLABLE的情况下
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }



最后就是看doQuery方法了,其他的子类都是覆盖这个进行实现即可,这也就是BaseExecutor存在的模板模式的意义,他封装了一些通用的逻辑,而子类只需要实现即可,不需要再重复的关注这些操作逻辑了

Executor(SimpleExecutor).query

根据前面最终的逻辑就会进入到SimpleExecutor进行执行


org.apache.ibatis.executor.SimpleExecutor#doQuery




  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      //获取配置类
      Configuration configuration = ms.getConfiguration();
      //从配置类中创建StatementHandler对象,默认为PreparedStatementHandler处理器
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      //前置处理
      stmt = prepareStatement(handler, ms.getStatementLog());
      //执行查询
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }



是不是好像已经看到了最熟悉的代码了,先获取statement对象,再进行prepare,最后再执行查询,下面就一个个点进去看

获取StatementHandler

又是一个接口,总的来说跟Executor的设计差不多,主要有以下的实现

BaseStatementHandler:基础的实现,提供了一些常用通用的方法

PreparedStatementHandler: 最常用的,继承了BaseStatementHandler

CallableStatementHandler:  callable类型,继承了BaseStatementHandler

SimpleStatementHandler: 简单处理器,原生Statement,继承了BaseStatementHandler

RoutingStatementHandler:路由处理器,其实就是把判断代码也封装成一个类管理了,同时保存了以下几个真正的一个实现,调用的时候就是直接调真正的,有兴趣的看一下这个类的实现就明白了,这里就不贴出来了........

以下是创建的流程和逻辑

org.apache.ibatis.session.Configuration#newStatementHandler


  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    //先创建一个RoutingStatementHandler,其实就是封装了一个具体实现
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    //这里是mybatis插件实现的位置,又进行了插件的拦截代理生成
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return 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:
        //默认其实就是这种实现,PreparedStatement类型的
        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());
    }

  }


里面会使用delegate 去实现接口的所有方法



第二步是代理拦截的生成,之前已经说过了,这里就不再细说了


所以创建这个对象仍然还是比较简单的


prepareStatement处理

org.apache.ibatis.executor.SimpleExecutor#prepareStatement


  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    //获取数据库链接,每个sqlSession获取的是同一条,所以也是sqlSession不安全的原因所在
    Connection connection = getConnection(statementLog);
    //获取statement对象
    stmt = handler.prepare(connection, transaction.getTimeout());
    //执行参数设置
    handler.parameterize(stmt);
    //返回statement
    return stmt;
  }


这里仍然有几个步骤


1. 获取连接,这个没啥好说的


2. 设置基本参数

  @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      //初始化
      statement = instantiateStatement(connection);
      //设置超时时间
      setStatementTimeout(statement, transactionTimeout);
      //设置fetchSize
      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);
    }
  }


3. 也是最重要的,设置参数


org.apache.ibatis.executor.statement.PreparedStatementHandler#parameterize


最终会到达这个方法

org.apache.ibatis.scripting.defaults.DefaultParameterHandler#setParameters

在这里你会看到核心的TypeHanler的使用设置


  @Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    //获取sql占位符对象,有多少个?就有多少个ParameterMapping对象
    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);
          }
          //获取ParameterMapping对象包含的类型转换器
          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);
          }
        }
      }
    }
  }

类型转换器便发生了这里,至于value的取值,目前有些地方我还没搞懂,以后搞懂了再写新的文章进行发布,不过整体逻辑也大概是看下来了

StatementHandler#query

这是最后的一个逻辑,也就是对返回结果的处理,包括怎么封装为对象返回的都在这个里面了,默认使用的是PreparedStatementHandler,也就是我们上面说到的地方

org.apache.ibatis.executor.statement.PreparedStatementHandler#query


  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    //转换为PreparedStatement对象
    PreparedStatement ps = (PreparedStatement) statement;
    //执行查询,这是jdbc原生的api,学过的应该都知道
    ps.execute();
    //使用ResultSetHandler进行处理结果集
    return resultSetHandler.<E> handleResultSets(ps);
  }



前面两步还是比较好理解的,最后一部是用于处理结果集的


ResultSetHandler又是一个新的接口,mybatis封装了很多接口,为的是以后方便别人的扩展,所以我们最后只要看ResultSetHandler的DefaultResultSetHandler实现即可,目前只有这个实现,估计大部分人就比较好理解了,再起一个标题块专门来看这个处理集,这个处理方法也是相对比较复杂的,不会说全部,只会说我知道的
handleResultSets方法


而这个默认的是在BaseStatementHandler初始化进行实例化的,可以看到


  protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    
    //其他的忽略掉了

        
    //参数处理器和结果处理器
    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
  }

resultSetHandler.<E> handleResultSets(ps)

org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleResultSets



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

    final List<Object> multipleResults = new ArrayList<Object>();

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

    //获取ResultMap对象,我们定义的对象被mybatis封装为这个了
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    //校验最少要有一个ResultMap
    validateResultMapsCount(rsw, resultMapCount);
    //循环处理
    while (rsw != null && resultMapCount > resultSetCount) {
      //循环获取,里面包含了 private Class<?> type; 这里的type就是User对象
      ResultMap resultMap = resultMaps.get(resultSetCount);
      //处理结果集,核心方法
      handleResultSet(rsw, resultMap, multipleResults, null);
      //获取下一个结果集
      rsw = getNextResultSet(stmt);
      //清除一些数据
      cleanUpAfterHandlingResultSet();
      //累加
      resultSetCount++;
    }

    //这一块应该是嵌套的resultMap处理,目前没研究..,很久没这么写过了
    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++;
      }
    }

    //进行包装返回,如果是1个直接返回那个,多个还是返回列表
    return collapseSingleResultList(multipleResults);
  }





上面的逻辑并不复杂,还没有到具体的解析代码,接下来看到处理的代码里面去,handleResultSet方法



  private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
    try {
      if (parentMapping != null) {
        handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
      } else {
        //parentMapping为空并且resultHandler为空的逻辑处理,我们现在走的是这里
        if (resultHandler == null) {
          //声明默认处理器
          DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
          //处理数据同时把数据放到ResultList中
          handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
          //把处理完的数据拿出来
          multipleResults.add(defaultResultHandler.getResultList());
        } else {
          //parentMapping为空并且resultHandler不为空,直接进入到这里来处理,其他的为其他逻辑
          handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
        }
      }
    } finally {
      // issue #228 (close resultsets)
      closeResultSet(rsw.getResultSet());
    }
  }




  public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    //是否有内嵌套的map
    if (resultMap.hasNestedResultMaps()) {
      ensureNoRowBounds();
      checkResultHandler();
      handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
      //目前我们是没有的,所以上面的都不需要看
      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
  }




private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
    DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
    //处理偏移量,我们的逻辑也没用到这个,不过mybatis支持这个.可以用来跳过前多少行数据的功能
    //看到这里也让我明白了RowBounds的使用,就是用来跳过前面多少行的
    skipRows(rsw.getResultSet(), rowBounds);
    //调用Result.next开始获取数据进行处理了
    while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
      //获取一行数据
      Object rowValue = getRowValue(rsw, discriminatedResultMap);
      //保存处理好的数据
      storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
    }
  }

  private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
    if (parentMapping != null) {
      linkToParents(rs, parentMapping, rowValue);
    } else {
      //
      callResultHandler(resultHandler, resultContext, rowValue);
    }
  }

  @SuppressWarnings("unchecked" /* because ResultHandler<?> is always ResultHandler<Object>*/)
  private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
    //把获取的数据放到下一个去
    resultContext.nextResultObject(rowValue);
    //回调处理把数据放到调用进来的List<Object>中去,这也是一种设计思想,回调模式!
    ((ResultHandler<Object>) resultHandler).handleResult(resultContext);
  }





那么现在的逻辑也是越来越清晰了,解析数据就发生在getRowValue方法中,我们再去里面看看发生了什么



  private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
    final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    //创建结果返回对象,这里也就是调用反射创建了User对象,这个时候还没有附上值
    Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
    if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
      //生成类元数据,提供了很多方便操作对象的方法
      final MetaObject metaObject = configuration.newMetaObject(rowValue);
      boolean foundValues = this.useConstructorMappings;
      if (shouldApplyAutomaticMappings(resultMap, false)) {
        //经过这个方法后才附上了值,所以处理逻辑肯定是发生在这个里面了
        foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
      }
      foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
      foundValues = lazyLoader.size() > 0 || foundValues;
      rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
    }
    return rowValue;
  }



再接着往下看


  private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
    //创建还未自动匹配的字段列
    List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
    boolean foundValues = false;
    if (!autoMapping.isEmpty()) {
      //循环处理
      for (UnMappedColumnAutoMapping mapping : autoMapping) {
        //这里就可以看到类型转换器的调用了,通过类型转换器去获取具体的值出来
        final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
        if (value != null) {
          foundValues = true;
        }
        if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
          // gcode issue #377, call setter on nulls (value is not 'found')
          //把数据设置到对象中
          metaObject.setValue(mapping.property, value);
        }
      }
    }
    return foundValues;
  }



这里再一次看到了类型转换器的设置,同时也看到了使用反射设置对象数据,那么到这里整个逻辑就已经结束了


其他的就是一些分支处理,或者说细节处理,细节的话有兴趣可以再到对应的方法里面去进行分析,这里整个流程差不多就要结束了

last

前面的结果查询后就又回到这个方法了



 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      //执行查询,刚刚就是从这里查询出来的
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      //先移除缓存
      localCache.removeObject(key);
    }
    //查后的数据放到缓存中
    localCache.putObject(key, list);
    //又放到了一个专门的缓存中,StatementType.CALLABLE的情况下
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

到处,整个流程就分析完了,也不知道大家能不能看懂我说的...

2025-09-24 16:01:43.664 [reactor-http-nio-6] DEBUG reactor.netty.http.server.HttpServerOperations - [50206d33, L:/192.168.10.130:9000 - R:/192.168.10.130:61997] New http connection, requesting read 2025-09-24 16:01:43.664 [reactor-http-nio-6] DEBUG reactor.netty.transport.TransportConfig - [50206d33, L:/192.168.10.130:9000 - R:/192.168.10.130:61997] Initialized pipeline DefaultChannelPipeline{(reactor.left.httpCodec = io.netty.handler.codec.http.HttpServerCodec), (reactor.left.httpTrafficHandler = reactor.netty.http.server.HttpTrafficHandler), (reactor.right.reactiveBridge = reactor.netty.channel.ChannelOperationsHandler)} 2025-09-24 16:01:43.665 [reactor-http-nio-6] DEBUG reactor.netty.http.server.HttpServerOperations - [50206d33, L:/192.168.10.130:9000 - R:/192.168.10.130:61997] Increasing pending responses, now 1 2025-09-24 16:01:43.665 [reactor-http-nio-6] DEBUG reactor.netty.http.server.HttpServer - [50206d33-1, L:/192.168.10.130:9000 - R:/192.168.10.130:61997] Handler is being applied: org.springframework.http.server.reactive.ReactorHttpHandlerAdapter@f732ab5 2025-09-24 16:01:43.665 [reactor-http-nio-6] DEBUG o.s.web.server.adapter.HttpWebHandlerAdapter - [50206d33-13] HTTP PUT "/master/shipperPart/updateBindings" 2025-09-24 16:01:43.665 [reactor-http-nio-6] DEBUG o.s.w.r.r.m.a.RequestMappingHandlerMapping - [50206d33-13] Mapped to com.hvlink.controller.ShipperPartController#updateBindings(ShipperPartParam) 2025-09-24 16:01:43.666 [reactor-http-nio-6] DEBUG o.s.w.r.r.m.a.RequestBodyMethodArgumentResolver - [50206d33-13] Content-Type:application/json 2025-09-24 16:01:43.666 [reactor-http-nio-6] DEBUG o.s.w.r.r.m.a.RequestBodyMethodArgumentResolver - [50206d33-13] 0..1 [com.hvlink.entity.param.master.ShipperPartParam] 2025-09-24 16:01:43.666 [reactor-http-nio-6] DEBUG reactor.netty.channel.FluxReceive - [50206d33-1, L:/192.168.10.130:9000 - R:/192.168.10.130:61997] [terminated=false, cancelled=false, pending=0, error=null]: subscribing inbound receiver 2025-09-24 16:01:43.667 [reactor-http-nio-6] DEBUG o.s.http.codec.json.Jackson2JsonDecoder - [50206d33-13] Decoded [ShipperPartParam(shipperId=28, partIds=[456, 530])] ShipperPartParam(shipperId=28, partIds=[456, 530]) 2025-09-24 16:01:43.667 [reactor-http-nio-6] DEBUG o.s.jdbc.datasource.DataSourceTransactionManager - Creating new transaction with name [com.hvlink.service.impl.ShipperPartServiceImpl.updateShipperParts]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Exception 2025-09-24 16:01:43.667 [reactor-http-nio-6] DEBUG o.s.jdbc.datasource.DataSourceTransactionManager - Acquired Connection [ConnectionID:3 ClientConnectionId: 64c42d95-bfb2-4b07-b196-8943f3b70bce] for JDBC transaction 2025-09-24 16:01:43.667 [reactor-http-nio-6] DEBUG o.s.jdbc.datasource.DataSourceTransactionManager - Switching JDBC Connection [ConnectionID:3 ClientConnectionId: 64c42d95-bfb2-4b07-b196-8943f3b70bce] to manual commit 2025-09-24 16:01:43.683 [reactor-http-nio-6] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession 2025-09-24 16:01:43.683 [reactor-http-nio-6] DEBUG org.mybatis.spring.SqlSessionUtils - Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6eda94a6] 2025-09-24 16:01:43.683 [reactor-http-nio-6] DEBUG o.m.spring.transaction.SpringManagedTransaction - JDBC Connection [ConnectionID:3 ClientConnectionId: 64c42d95-bfb2-4b07-b196-8943f3b70bce] will be managed by Spring 2025-09-24 16:01:43.683 [reactor-http-nio-6] DEBUG c.h.m.m.ShipperPartMapper.selectPartIdsByShipperId - ==> Preparing: SELECT part_id FROM tm_shipper_part WHERE shipper_id = ? 2025-09-24 16:01:43.683 [reactor-http-nio-6] DEBUG c.h.m.m.ShipperPartMapper.selectPartIdsByShipperId - ==> Parameters: 28(Integer) 2025-09-24 16:01:43.694 [reactor-http-nio-6] DEBUG c.h.m.m.ShipperPartMapper.selectPartIdsByShipperId - <== Total: 2 2025-09-24 16:01:43.694 [reactor-http-nio-6] DEBUG org.mybatis.spring.SqlSessionUtils - Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6eda94a6] 2025-09-24 16:01:43.694 [reactor-http-nio-6] INFO com.hvlink.service.impl.ShipperPartServiceImpl - 发货方28绑定更新: 新增0个, 删除0个 2025-09-24 16:01:43.694 [reactor-http-nio-6] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6eda94a6] 2025-09-24 16:01:43.695 [reactor-http-nio-6] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6eda94a6] 2025-09-24 16:01:43.695 [reactor-http-nio-6] DEBUG org.mybatis.spring.SqlSessionUtils - Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6eda94a6] 2025-09-24 16:01:43.695 [reactor-http-nio-6] DEBUG o.s.jdbc.datasource.DataSourceTransactionManager - Initiating transaction commit 2025-09-24 16:01:43.695 [reactor-http-nio-6] DEBUG o.s.jdbc.datasource.DataSourceTransactionManager - Committing JDBC transaction on Connection [ConnectionID:3 ClientConnectionId: 64c42d95-bfb2-4b07-b196-8943f3b70bce] 2025-09-24 16:01:43.784 [reactor-http-nio-6] DEBUG o.s.jdbc.datasource.DataSourceTransactionManager - Releasing JDBC Connection [ConnectionID:3 ClientConnectionId: 64c42d95-bfb2-4b07-b196-8943f3b70bce] after transaction 2025-09-24 16:01:43.785 [reactor-http-nio-6] DEBUG o.s.w.r.r.m.annotation.ResponseBodyResultHandler - [50206d33-13] Using 'application/json' given [application/json, text/plain, */*] and supported [application/json, application/*+json, application/x-ndjson, text/event-stream] 2025-09-24 16:01:43.785 [reactor-http-nio-6] DEBUG o.s.w.r.r.m.annotation.ResponseBodyResultHandler - [50206d33-13] 0..1 [com.hvlink.common.Result<java.lang.String>] 2025-09-24 16:01:43.785 [reactor-http-nio-6] DEBUG o.s.http.codec.json.Jackson2JsonEncoder - [50206d33-13] Encoding [Result(status=200, msg=成功, data=绑定关系更新成功, timestamp=1758700903784)] 2025-09-24 16:01:43.785 [reactor-http-nio-6] DEBUG reactor.netty.http.server.HttpServerOperations - [50206d33-1, L:/192.168.10.130:9000 - R:/192.168.10.130:61997] Detected non persistent http connection, preparing to close 2025-09-24 16:01:43.786 [reactor-http-nio-6] DEBUG reactor.netty.http.server.HttpServerOperations - [50206d33-1, L:/192.168.10.130:9000 - R:/192.168.10.130:61997] Last HTTP packet was sent, terminating the channel 2025-09-24 16:01:43.786 [reactor-http-nio-6] DEBUG reactor.netty.channel.ChannelOperations - [50206d33-1, L:/192.168.10.130:9000 - R:/192.168.10.130:61997] [HttpServer] Channel inbound receiver cancelled (operation cancelled). 2025-09-24 16:01:43.786 [reactor-http-nio-6] DEBUG o.s.web.server.adapter.HttpWebHandlerAdapter - [50206d33-13] Completed 200 OK 2025-09-24 16:01:43.914 [reactor-http-nio-7] DEBUG reactor.netty.http.server.HttpServerOperations - [e34ccef0, L:/192.168.10.130:9000 - R:/192.168.10.130:61998] New http connection, requesting read 2025-09-24 16:01:43.915 [reactor-http-nio-7] DEBUG reactor.netty.transport.TransportConfig - [e34ccef0, L:/192.168.10.130:9000 - R:/192.168.10.130:61998] Initialized pipeline DefaultChannelPipeline{(reactor.left.httpCodec = io.netty.handler.codec.http.HttpServerCodec), (reactor.left.httpTrafficHandler = reactor.netty.http.server.HttpTrafficHandler), (reactor.right.reactiveBridge = reactor.netty.channel.ChannelOperationsHandler)} 2025-09-24 16:01:43.915 [reactor-http-nio-7] DEBUG reactor.netty.http.server.HttpServerOperations - [e34ccef0, L:/192.168.10.130:9000 - R:/192.168.10.130:61998] Increasing pending responses, now 1 2025-09-24 16:01:43.915 [reactor-http-nio-7] DEBUG reactor.netty.http.server.HttpServer - [e34ccef0-1, L:/192.168.10.130:9000 - R:/192.168.10.130:61998] Handler is being applied: org.springframework.http.server.reactive.ReactorHttpHandlerAdapter@f732ab5 2025-09-24 16:01:43.916 [reactor-http-nio-7] DEBUG o.s.web.server.adapter.HttpWebHandlerAdapter - [e34ccef0-14] HTTP POST "/master/shipperPart/listPartsWithStatus?supplierId=1&shipperId=28&spliceDesc=" 2025-09-24 16:01:43.916 [reactor-http-nio-7] DEBUG o.s.w.r.r.m.a.RequestMappingHandlerMapping - [e34ccef0-14] Mapped to com.hvlink.controller.ShipperPartController#listPartsWithStatus(Integer, Integer, String) 2025-09-24 16:01:43.917 [reactor-http-nio-7] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession 2025-09-24 16:01:43.917 [reactor-http-nio-7] DEBUG org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@43072a63] was not registered for synchronization because synchronization is not active 2025-09-24 16:01:43.917 [reactor-http-nio-7] DEBUG o.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource 2025-09-24 16:01:43.918 [reactor-http-nio-7] DEBUG o.m.spring.transaction.SpringManagedTransaction - JDBC Connection [ConnectionID:3 ClientConnectionId: 64c42d95-bfb2-4b07-b196-8943f3b70bce] will not be managed by Spring 2025-09-24 16:01:43.918 [reactor-http-nio-7] DEBUG c.h.m.master.PartMapper.selectPartsWithBindStatus - ==> Preparing: SELECT id, part_code, part_desc, supplier_code, assigned FROM ( SELECT p.id, p.part_code, p.part_desc, p.supplier_code, CASE WHEN sp.part_id IS NOT NULL THEN 1 ELSE 0 END AS assigned, ROW_NUMBER() OVER (PARTITION BY p.part_code ORDER BY p.id) AS rn FROM tm_part p LEFT JOIN tm_shipper_part sp ON p.id = sp.part_id AND sp.shipper_id = ? WHERE p.supplier_code = ( SELECT supplier_code FROM tm_supplier WHERE id = ? ) ) t WHERE rn = 1 ORDER BY part_code 2025-09-24 16:01:43.918 [reactor-http-nio-7] DEBUG c.h.m.master.PartMapper.selectPartsWithBindStatus - ==> Parameters: 28(Integer), 1(Integer) 2025-09-24 16:01:44.106 [reactor-http-nio-7] DEBUG c.h.m.master.PartMapper.selectPartsWithBindStatus - <== Total: 637 2025-09-24 16:01:44.106 [reactor-http-nio-7] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@43072a63] 2025-09-24 16:01:44.107 [reactor-http-nio-7] DEBUG o.s.w.r.r.m.annotation.ResponseBodyResultHandler - [e34ccef0-14] Using 'application/json' given [application/json, text/plain, */*] and supported [application/json, application/*+json, application/x-ndjson, text/event-stream] 2025-09-24 16:01:44.107 [reactor-http-nio-7] DEBUG o.s.w.r.r.m.annotation.ResponseBodyResultHandler - [e34ccef0-14] 0..1 [com.hvlink.common.Result<java.util.List<com.hvlink.entity.vo.master.PartVO>>] 2025-09-24 16:01:44.108 [reactor-http-nio-7] DEBUG o.s.http.codec.json.Jackson2JsonEncoder - [e34ccef0-14] Encoding [Result(status=200, msg=成功, data=[PartVO(id=704, SupplierId=null, partCode=, partDesc=, spliceDesc= - (truncated)...] 2025-09-24 16:01:44.110 [reactor-http-nio-7] DEBUG reactor.netty.http.server.HttpServerOperations - [e34ccef0-1, L:/192.168.10.130:9000 - R:/192.168.10.130:61998] Detected non persistent http connection, preparing to close 2025-09-24 16:01:44.124 [reactor-http-nio-7] DEBUG reactor.netty.http.server.HttpServerOperations - [e34ccef0-1, L:/192.168.10.130:9000 - R:/192.168.10.130:61998] Last HTTP packet was sent, terminating the channel 2025-09-24 16:01:44.124 [reactor-http-nio-7] DEBUG reactor.netty.channel.ChannelOperations - [e34ccef0-1, L:/192.168.10.130:9000 - R:/192.168.10.130:61998] [HttpServer] Channel inbound receiver cancelled (operation cancelled). 2025-09-24 16:01:44.125 [reactor-http-nio-7] DEBUG o.s.web.server.adapter.HttpWebHandlerAdapter - [e34ccef0-14] Completed 200 OK 2025-09-24 16:01:44.633 [reactor-http-nio-8] DEBUG reactor.netty.http.server.HttpServerOperations - [da1572ab, L:/192.168.10.130:9000 - R:/192.168.10.130:61999] New http connection, requesting read 2025-09-24 16:01:44.633 [reactor-http-nio-8] DEBUG reactor.netty.transport.TransportConfig - [da1572ab, L:/192.168.10.130:9000 - R:/192.168.10.130:61999] Initialized pipeline DefaultChannelPipeline{(reactor.left.httpCodec = io.netty.handler.codec.http.HttpServerCodec), (reactor.left.httpTrafficHandler = reactor.netty.http.server.HttpTrafficHandler), (reactor.right.reactiveBridge = reactor.netty.channel.ChannelOperationsHandler)} 2025-09-24 16:01:44.633 [reactor-http-nio-8] DEBUG reactor.netty.http.server.HttpServerOperations - [da1572ab, L:/192.168.10.130:9000 - R:/192.168.10.130:61999] Increasing pending responses, now 1 2025-09-24 16:01:44.633 [reactor-http-nio-8] DEBUG reactor.netty.http.server.HttpServer - [da1572ab-1, L:/192.168.10.130:9000 - R:/192.168.10.130:61999] Handler is being applied: org.springframework.http.server.reactive.ReactorHttpHandlerAdapter@f732ab5 2025-09-24 16:01:44.633 [reactor-http-nio-8] DEBUG o.s.web.server.adapter.HttpWebHandlerAdapter - [da1572ab-15] HTTP POST "/master/shipper/query" 2025-09-24 16:01:44.634 [reactor-http-nio-8] DEBUG o.s.w.r.r.m.a.RequestMappingHandlerMapping - [da1572ab-15] Mapped to com.hvlink.controller.ShipperController#getShipper(ShipperParam) 2025-09-24 16:01:44.634 [reactor-http-nio-8] DEBUG o.s.w.r.r.m.a.RequestBodyMethodArgumentResolver - [da1572ab-15] Content-Type:application/json 2025-09-24 16:01:44.634 [reactor-http-nio-8] DEBUG o.s.w.r.r.m.a.RequestBodyMethodArgumentResolver - [da1572ab-15] 0..1 [com.hvlink.entity.param.master.ShipperParam] 2025-09-24 16:01:44.634 [reactor-http-nio-8] DEBUG reactor.netty.channel.FluxReceive - [da1572ab-1, L:/192.168.10.130:9000 - R:/192.168.10.130:61999] [terminated=false, cancelled=false, pending=0, error=null]: subscribing inbound receiver 2025-09-24 16:01:44.634 [reactor-http-nio-8] DEBUG o.s.http.codec.json.Jackson2JsonDecoder - [da1572ab-15] Decoded [ShipperParam(id=null, companyCode=null, supplierCode=null, supplierName=, shipperName=, isDefault=nu (truncated)...] 2025-09-24 16:01:44.635 [reactor-http-nio-8] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession 2025-09-24 16:01:44.635 [reactor-http-nio-8] DEBUG org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6a810d1c] was not registered for synchronization because synchronization is not active 2025-09-24 16:01:44.639 [reactor-http-nio-8] DEBUG o.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource 2025-09-24 16:01:44.639 [reactor-http-nio-8] DEBUG o.m.spring.transaction.SpringManagedTransaction - JDBC Connection [ConnectionID:3 ClientConnectionId: 64c42d95-bfb2-4b07-b196-8943f3b70bce] will not be managed by Spring 2025-09-24 16:01:44.639 [reactor-http-nio-8] DEBUG c.h.mapper.master.ShipperMapper.getShipper_mpCount - ==> Preparing: SELECT COUNT(*) FROM (SELECT DISTINCT t.id, s.id AS supplier_id, t.supplier_code, s.supplier_name, t.shipper_name, t.is_default, t.create_time FROM tm_shipper t LEFT JOIN tm_supplier s ON s.supplier_code = t.supplier_code) TOTAL 2025-09-24 16:01:44.639 [reactor-http-nio-8] DEBUG c.h.mapper.master.ShipperMapper.getShipper_mpCount - ==> Parameters: 2025-09-24 16:01:44.709 [reactor-http-nio-8] DEBUG c.h.mapper.master.ShipperMapper.getShipper_mpCount - <== Total: 1 2025-09-24 16:01:44.709 [reactor-http-nio-8] DEBUG com.hvlink.mapper.master.ShipperMapper.getShipper - ==> Preparing: SELECT DISTINCT t.id, s.id as supplier_id, t.supplier_code, s.supplier_name, t.shipper_name, t.is_default, t.create_time FROM tm_shipper t LEFT JOIN tm_supplier s ON s.supplier_code = t.supplier_code ORDER BY t.create_time DESC OFFSET ? ROWS FETCH NEXT ? ROWS ONLY 2025-09-24 16:01:44.710 [reactor-http-nio-8] DEBUG com.hvlink.mapper.master.ShipperMapper.getShipper - ==> Parameters: 0(Long), 10(Long) 2025-09-24 16:01:44.726 [reactor-http-nio-8] DEBUG com.hvlink.mapper.master.ShipperMapper.getShipper - <== Total: 10 2025-09-24 16:01:44.726 [reactor-http-nio-8] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6a810d1c] 2025-09-24 16:01:44.726 [reactor-http-nio-8] DEBUG o.s.w.r.r.m.annotation.ResponseBodyResultHandler - [da1572ab-15] Using 'application/json' given [application/json, text/plain, */*] and supported [application/json, application/*+json, application/x-ndjson, text/event-stream] 2025-09-24 16:01:44.726 [reactor-http-nio-8] DEBUG o.s.w.r.r.m.annotation.ResponseBodyResultHandler - [da1572ab-15] 0..1 [com.hvlink.common.Result<com.hvlink.pagination.PageResult<com.hvlink.entity.vo.master.ShipperVO>>] 2025-09-24 16:01:44.727 [reactor-http-nio-8] DEBUG o.s.http.codec.json.Jackson2JsonEncoder - [da1572ab-15] Encoding [Result(status=200, msg=成功, data=PageResult(total=280, records=[ShipperVO(id=41, SupplierId=null, sup (truncated)...] 2025-09-24 16:01:44.727 [reactor-http-nio-8] DEBUG reactor.netty.http.server.HttpServerOperations - [da1572ab-1, L:/192.168.10.130:9000 - R:/192.168.10.130:61999] Detected non persistent http connection, preparing to close 2025-09-24 16:01:44.728 [reactor-http-nio-8] DEBUG reactor.netty.http.server.HttpServerOperations - [da1572ab-1, L:/192.168.10.130:9000 - R:/192.168.10.130:61999] Last HTTP packet was sent, terminating the channel 2025-09-24 16:01:44.728 [reactor-http-nio-8] DEBUG reactor.netty.channel.ChannelOperations - [da1572ab-1, L:/192.168.10.130:9000 - R:/192.168.10.130:61999] [HttpServer] Channel inbound receiver cancelled (operation cancelled). 2025-09-24 16:01:44.728 [reactor-http-nio-8] DEBUG o.s.web.server.adapter.HttpWebHandlerAdapter - [da1572ab-15] Completed 200 OK
09-25
2025-09-28 19:02:25.726 [http-nio-9001-exec-4] DEBUG o.s.web.server.adapter.HttpWebHandlerAdapter - [393b4106] HTTP POST "/order/query" 2025-09-28 19:02:25.727 [http-nio-9001-exec-4] DEBUG o.s.w.r.r.m.a.RequestMappingHandlerMapping - [393b4106] Mapped to com.hvlink.controller.PurchaseOrderController#queryPage(PurchaseOrderParam) 2025-09-28 19:02:25.727 [http-nio-9001-exec-4] DEBUG o.s.w.r.r.m.a.RequestBodyMethodArgumentResolver - [393b4106] Content-Type:application/json 2025-09-28 19:02:25.727 [http-nio-9001-exec-4] DEBUG o.s.w.r.r.m.a.RequestBodyMethodArgumentResolver - [393b4106] 0..1 [com.hvlink.entity.param.order.PurchaseOrderParam] 2025-09-28 19:02:25.727 [http-nio-9001-exec-4] DEBUG o.s.http.codec.json.Jackson2JsonDecoder - [393b4106] Decoded [PurchaseOrderParam(factoryCode=, factoryName=null, warehouseCode=, warehouseName=null, purchaseOrder (truncated)...] 2025-09-28 19:02:25.727 [http-nio-9001-exec-4] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession 2025-09-28 19:02:25.727 [http-nio-9001-exec-4] DEBUG org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@11a05fe4] was not registered for synchronization because synchronization is not active 2025-09-28 19:02:25.739 [http-nio-9001-exec-4] DEBUG o.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource 2025-09-28 19:02:25.739 [http-nio-9001-exec-4] DEBUG o.m.spring.transaction.SpringManagedTransaction - JDBC Connection [ConnectionID:7 ClientConnectionId: 043d5662-b1a5-4e6e-8d88-375074543f8e] will not be managed by Spring 2025-09-28 19:02:25.739 [http-nio-9001-exec-4] DEBUG c.h.m.o.T.queryPurchaseOrderPage_mpCount - ==> Preparing: SELECT COUNT(*) AS total FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY purchase_order_no, company_code ORDER BY serial_version_num DESC) AS rn FROM tb_order_main WHERE is_deleted = 0 AND order_type = 2) m LEFT JOIN tm_supplier s ON m.supplier_code = s.supplier_code AND m.company_code = s.company_code LEFT JOIN tm_factory f ON m.factory_code = f.factory_code AND m.company_code = f.company_code LEFT JOIN tm_warehouse w ON m.warehouse_code = w.warehouse_code AND m.company_code = w.company_code WHERE m.rn = 1 AND f.status = 1 AND w.status = 1 2025-09-28 19:02:25.740 [http-nio-9001-exec-4] DEBUG c.h.m.o.T.queryPurchaseOrderPage_mpCount - ==> Parameters: 2025-09-28 19:02:25.828 [http-nio-9001-exec-4] DEBUG c.h.m.o.T.queryPurchaseOrderPage_mpCount - <== Total: 1 2025-09-28 19:02:25.829 [http-nio-9001-exec-4] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@11a05fe4] 2025-09-28 19:02:25.829 [http-nio-9001-exec-4] DEBUG o.s.w.r.r.m.annotation.ResponseBodyResultHandler - [393b4106] Using 'application/json' given [application/json, text/plain, */*] and supported [application/json, application/*+json, application/x-ndjson, text/event-stream] 2025-09-28 19:02:25.829 [http-nio-9001-exec-4] DEBUG o.s.w.r.r.m.annotation.ResponseBodyResultHandler - [393b4106] 0..1 [com.hvlink.common.Result<com.hvlink.pagination.PageResult<com.hvlink.entity.vo.order.PurchaseOrderMainVO>>] 2025-09-28 19:02:25.829 [http-nio-9001-exec-4] DEBUG o.s.http.codec.json.Jackson2JsonEncoder - [393b4106] Encoding [Result(status=200, msg=成功, data=null, timestamp=1759057345829)] 2025-09-28 19:02:25.830 [http-nio-9001-exec-4] DEBUG o.s.web.server.adapter.HttpWebHandlerAdapter - [393b4106] Completed 200 OK 2025-09-28 19:02:42.305 [http-nio-9001-exec-5] DEBUG o.s.web.server.adapter.HttpWebHandlerAdapter - [33fcd68f] HTTP POST "/order/query" 2025-09-28 19:02:42.305 [http-nio-9001-exec-5] DEBUG o.s.w.r.r.m.a.RequestMappingHandlerMapping - [33fcd68f] Mapped to com.hvlink.controller.PurchaseOrderController#queryPage(PurchaseOrderParam) 2025-09-28 19:02:42.305 [http-nio-9001-exec-5] DEBUG o.s.w.r.r.m.a.RequestBodyMethodArgumentResolver - [33fcd68f] Content-Type:application/json 2025-09-28 19:02:42.305 [http-nio-9001-exec-5] DEBUG o.s.w.r.r.m.a.RequestBodyMethodArgumentResolver - [33fcd68f] 0..1 [com.hvlink.entity.param.order.PurchaseOrderParam] 2025-09-28 19:02:42.306 [http-nio-9001-exec-5] DEBUG o.s.http.codec.json.Jackson2JsonDecoder - [33fcd68f] Decoded [PurchaseOrderParam(factoryCode=, factoryName=null, warehouseCode=, warehouseName=null, purchaseOrder (truncated)...] 2025-09-28 19:02:42.306 [http-nio-9001-exec-5] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession 2025-09-28 19:02:42.306 [http-nio-9001-exec-5] DEBUG org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1c84d1e8] was not registered for synchronization because synchronization is not active 2025-09-28 19:02:42.319 [http-nio-9001-exec-5] DEBUG o.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource 2025-09-28 19:02:42.319 [http-nio-9001-exec-5] DEBUG o.m.spring.transaction.SpringManagedTransaction - JDBC Connection [ConnectionID:7 ClientConnectionId: 043d5662-b1a5-4e6e-8d88-375074543f8e] will not be managed by Spring 2025-09-28 19:02:42.319 [http-nio-9001-exec-5] DEBUG c.h.m.o.T.queryPurchaseOrderPage_mpCount - ==> Preparing: SELECT COUNT(*) AS total FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY purchase_order_no, company_code ORDER BY serial_version_num DESC) AS rn FROM tb_order_main WHERE is_deleted = 0 AND order_type = 2) m LEFT JOIN tm_supplier s ON m.supplier_code = s.supplier_code AND m.company_code = s.company_code LEFT JOIN tm_factory f ON m.factory_code = f.factory_code AND m.company_code = f.company_code LEFT JOIN tm_warehouse w ON m.warehouse_code = w.warehouse_code AND m.company_code = w.company_code WHERE m.rn = 1 AND f.status = 1 AND w.status = 1 2025-09-28 19:02:42.319 [http-nio-9001-exec-5] DEBUG c.h.m.o.T.queryPurchaseOrderPage_mpCount - ==> Parameters: 2025-09-28 19:02:42.405 [http-nio-9001-exec-5] DEBUG c.h.m.o.T.queryPurchaseOrderPage_mpCount - <== Total: 1 2025-09-28 19:02:42.405 [http-nio-9001-exec-5] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1c84d1e8] 2025-09-28 19:02:42.405 [http-nio-9001-exec-5] DEBUG o.s.w.r.r.m.annotation.ResponseBodyResultHandler - [33fcd68f] Using 'application/json' given [application/json, text/plain, */*] and supported [application/json, application/*+json, application/x-ndjson, text/event-stream] 2025-09-28 19:02:42.406 [http-nio-9001-exec-5] DEBUG o.s.w.r.r.m.annotation.ResponseBodyResultHandler - [33fcd68f] 0..1 [com.hvlink.common.Result<com.hvlink.pagination.PageResult<com.hvlink.entity.vo.order.PurchaseOrderMainVO>>] 2025-09-28 19:02:42.406 [http-nio-9001-exec-5] DEBUG o.s.http.codec.json.Jackson2JsonEncoder - [33fcd68f] Encoding [Result(status=200, msg=成功, data=null, timestamp=1759057362405)] 2025-09-28 19:02:42.406 [http-nio-9001-exec-5] DEBUG o.s.web.server.adapter.HttpWebHandlerAdapter - [33fcd68f] Completed 200 OK data为什么是null
09-29
==> Preparing: INSERT INTO SYS_LOGS ( ID, ADDRESS, REQUEST_BROWSER, EXCEPTION_DETAIL, LOG_TYPE, METHOD_NAME, METHOD_PARAMETERS, REQUEST_IP, USED_TIME, USER_NAME, CREATED_BY_WORKCODE, CREATED_TIME, CREATED_BY_COMPANY ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) ==> Parameters: 4605(Long), 内网IP(String), Chrome 13(String), at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:92) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:440) at com.sun.proxy.$Proxy82.selectOne(Unknown Source) at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:159) at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(MybatisMapperMethod.java:108) at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:96) at com.sun.proxy.$Proxy119.selectOne(Unknown Source) at com.baomidou.mybatisplus.extension.service.impl.ServiceImpl.getOne(ServiceImpl.java:184) at com.baomidou.mybatisplus.extension.service.IService.getOne(IService.java:225) at com.smec.apps.coa.service.impl.CoaMaterialParameterServiceImpl.getMaterialParameterByMaterialNoAndParameterName(CoaMaterialParameterServiceImpl.java:231) ...(String), ERROR(String), com.smec.apps.coa.controller.AutoCalculationController.uploadFile()(String), { request: org.springframework.web.multipart.support.StandardMultipartHttpServletRequest@50b3c02d file: org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@1dd82c9d}(String), 10.200.115.36(String), 0(Long), ME00545(String), ME00545(String), 2025-10-24 14:34:09.926(Timestamp), SX01(String) <== Updates: 1 Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@34ad4d7] Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@34ad4d7] Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@34ad4d7] Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@34ad4d7] 2025-10-24 14:34:09.962 ERROR 959288 --- [nio-8000-exec-8] c.o.group.apps.core.aop.ConsolePrintAop : 抛出异常 : nested exception is org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 2 2025-10-24 14:34:09.962 ERROR 959288 --- [nio-8000-exec-8] c.o.g.a.c.c.e.Error500Controller : 捕获到错误: MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 2 2025-10-24 14:35:00.004 INFO 959288 --- [ scheduling-1] com.smec.apps.coa.schedule.SendCoaTask : sendSrmoPpap running Creating a new SqlSession Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@67d8199e] JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@40a54255] will be managed by Spring ==> Preparing: SELECT ID,CASE_NO,CREATED_TIME,DEPARTMENT,CREATED_BY_ACCOUNT,CREATED_BY_COMPANY,MATERIAL_NO,MATERIAL_TYPE,MATERIAL_CHINESE_NAME,MATERIAL_ENGLISH_NAME,VENDOR_CODE,VENDOR_NAME,PPAP_LEVEL,APPROVER,EMAIL,CREATED_BY_FAB,STATUS,VERSION,PSW_TIME,REMARK,SUPPLIER_CODE,CREATED_BY_WORKCODE,UPDATED_BY_WORKCODE,UPDATED_TIME,REQUEST_ID,UUID,CREATED_BY_OA_ID,UPDATED_BY_OA_ID,SITE,SYNC_FLAG,LEVEL_MSG,VENDOR_ABOVE,OTHER_REASON FROM CERP_COA_PPAP_DATA WHERE (SYNC_FLAG = ?) ORDER BY CREATED_TIME DESC ==> Parameters: 0(String) <== Total: 0 Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@67d8199e] Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@67d8199e] Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@67d8199e] Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@67d8199e] 2025-10-24 14:35:00.009 INFO 959288 --- [ scheduling-1] com.smec.apps.coa.schedule.SendCoaTask : sendSrmoPpap end 2025-10-24 14:35:00.010 INFO 959288 --- [ scheduling-1] com.smec.apps.coa.schedule.SendCoaTask : sendPpapToSrm running Creating a new SqlSession Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@43751a1c] JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@4f6deb7c] will be managed by Spring ==> Preparing: SELECT ID,CASE_NO,CREATED_TIME,DEPARTMENT,CREATED_BY_ACCOUNT,CREATED_BY_COMPANY,MATERIAL_NO,MATERIAL_TYPE,MATERIAL_CHINESE_NAME,MATERIAL_ENGLISH_NAME,VENDOR_CODE,VENDOR_NAME,PPAP_LEVEL,APPROVER,EMAIL,CREATED_BY_FAB,STATUS,VERSION,PSW_TIME,REMARK,SUPPLIER_CODE,CREATED_BY_WORKCODE,UPDATED_BY_WORKCODE,UPDATED_TIME,REQUEST_ID,UUID,CREATED_BY_OA_ID,UPDATED_BY_OA_ID,SITE,SYNC_FLAG,LEVEL_MSG,VENDOR_ABOVE,OTHER_REASON FROM CERP_COA_PPAP_DATA WHERE (SYNC_FLAG = ? AND STATUS = ?) ORDER BY CREATED_TIME DESC ==> Parameters: 0(String), Vendor Confirm(String) <== Total: 0 Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@43751a1c] 2025-10-24 14:35:00.013 INFO 959288 --- [ scheduling-1] com.smec.apps.coa.schedule.SendCoaTask : sendPpapToSrm end Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@43751a1c] Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@43751a1c] Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@43751a1c]
最新发布
10-25
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值