原因:
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