mybatis关联查询,数据库返回多条数据,resultMap映射之后只有一条

71 阅读1分钟

原因:

MyBatis为了降低内存开销,采用ResultHandler逐行读取的JDBC ResultSet结果集的,这个方法是:

   1. 根据每一行的属性列key和对应的值value生成一个CacheKey作为主键.
        此处需要注意,只计算List对象自身的单个属性,association中的对象不参与计算
        <resultMap id="deviceAlarmInfo" type="com.zx.ponding.model.alarm.DeviceAlarmInfo">
            <result column="num" property="num"/>
            <result column="region" property="region"/>
            <result column="deviceId" property="deviceId"/>
            <result column="sn" property="snNumber"/>
            <result column="protocolType" property="protocolType"/>
            <result column="depthLevel" property="depthLevel"/>
            <result column="batteryCapacity" property="battery"/>
            <result column="deviceState" property="deviceState"/>
            <result column="SignoState" property="signoState"/>
            <result column="createUser" property="protocolType"/>
            <result column="imeiNumber" property="imeiNumber"/>
            <association property="alarmInfo" resultMap="alarmInfo"/>
        源码如下:
        private void createRowKeyForMappedProperties(ResultMap resultMap, ResultSetWrapper rsw, CacheKey cacheKey, List<ResultMapping> resultMappings, String columnPrefix) throws SQLException {
          for (ResultMapping resultMapping : resultMappings) {
            if (resultMapping.isSimple()) {
              final String column = prependPrefix(resultMapping.getColumn(), columnPrefix);
              final TypeHandler<?> th = resultMapping.getTypeHandler();
              List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
              // Issue #114
              if (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH))) {
                final Object value = th.getResult(rsw.getResultSet(), column);
                if (value != null || configuration.isReturnInstanceForEmptyRow()) {
                  cacheKey.update(column);
                  cacheKey.update(value);
                }
              }
            }
          }
        }
 
    2. 然后根据此cacheKey去缓存中获取记录
    3. 如果不能够获取到对应的记录,则将此数据添加到List中
    4. 如果能够获取到则说明此数据已经存在,则处理下一条数据,此数据忽略。然后重复以上步骤
    2-4步源码如下:
    private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
      final DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
      ResultSet resultSet = rsw.getResultSet();
      skipRows(resultSet, rowBounds);
      Object rowValue = previousRowValue;
      while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
        final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
        final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);
        Object partialObject = nestedResultObjects.get(rowKey);
        // issue #577 && #542
        if (mappedStatement.isResultOrdered()) {
          if (partialObject == null && rowValue != null) {
            nestedResultObjects.clear();
            storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
          }
          rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
        } else {
          rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
          if (partialObject == null) {
            storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
          }
        }
      }
      if (rowValue != null && mappedStatement.isResultOrdered() && shouldProcessMoreRows(resultContext, rowBounds)) {
        storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
        previousRowValue = null;
      } else if (rowValue != null) {
        previousRowValue = rowValue;
      }
    }

解决办法:

设置一个可以区分不同数据的属性列,例如查询的属性列中加入主键ID