2019独角兽企业重金招聘Python工程师标准>>>

一、问题

如果select中的联合主键组合成的key不唯一(当只select部分联合主键时可能发生),那么就会把相同的key合并成一条数据。

例如KEY_AKEY_BKEY_C是联合主键

KEY_A KEY_B KEY_C des
A1 B1 C1 数据1
A1 B2 C1 数据2
A1 B2 C2 数据3

如果只返回KEY_AKEY_B,那么SQL查询结果是三条,但是MyBatis返回方结果只有2条,数据3的key和数据2的key是一样的,所以不会返回数据3,MyBatis返回的结果为

KEY_A KEY_B KEY_C des
A1 B1 C1 数据1
A1 B2 C1 数据2

如果是单主键则没有问题

二、问题分析

1.联合主键时MyBatis的处理方式

MyBatis调用query(Statement statement, ResultHandler resultHandler)方法查询

package org.apache.ibatis.executor.statement;
...
public class PreparedStatementHandler extends BaseStatementHandler {...@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.execute();//处理SQL的查询结果return resultSetHandler.<E> handleResultSets(ps);}...
}

类所在JAR包如下图:

其中resultSetHandler.<E> handleResultSets(ps);方法的实现如下

package org.apache.ibatis.executor.resultset;
...
public class DefaultResultSetHandler implements ResultSetHandler {...@Overridepublic 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);List<ResultMap> resultMaps = mappedStatement.getResultMaps();int resultMapCount = resultMaps.size();validateResultMapsCount(rsw, resultMapCount);while (rsw != null && resultMapCount > resultSetCount) {ResultMap resultMap = resultMaps.get(resultSetCount);//处理结果集handleResultSet(rsw, resultMap, multipleResults, null);rsw = getNextResultSet(stmt);cleanUpAfterHandlingResultSet();resultSetCount++;}String[] resultSets = mappedStatement.getResulSets();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++;}}return collapseSingleResultList(multipleResults);}...
}

其中调用handleResultSet(rsw, resultMap, multipleResults, null);处理结果集

package org.apache.ibatis.executor.resultset;
...
public class DefaultResultSetHandler implements ResultSetHandler {...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 {if (resultHandler == null) {DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);//处理结果集handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);multipleResults.add(defaultResultHandler.getResultList());} else {handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);}}} finally {// issue #228 (close resultsets)closeResultSet(rsw.getResultSet());}}...
}

其中handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);处理结果集。

package org.apache.ibatis.executor.resultset;
...
public class DefaultResultSetHandler implements ResultSetHandler {...private void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {if (resultMap.hasNestedResultMaps()) {ensureNoRowBounds();checkResultHandler();//处理结果集handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);} else {//【#单主键时调用】handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);}}...
}

其中handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);处理结果集

package org.apache.ibatis.executor.resultset;
...
public class DefaultResultSetHandler implements ResultSetHandler {...private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {final DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();skipRows(rsw.getResultSet(), rowBounds);Object rowValue = null;//循环每条记录while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);//生成rowKeyfinal CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);//通过rowKey去nestedResultObjects取partialObjectObject partialObject = nestedResultObjects.get(rowKey);// issue #577 && #542//【#resultOrdered为true时】resultOrdered官方解释:这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false。if (mappedStatement.isResultOrdered()) {if (partialObject == null && rowValue != null) {//会clear,这样下个循环中partialObject就为null了nestedResultObjects.clear();//加的是上一行的数据rowValuestoreObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());}//保存当前行的数据,给下一行用rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, rowKey, null, partialObject);} else {rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, rowKey, null, partialObject);//如果partialObject为null,即在nestedResultObjects中通过rowKey查询不到结果;正常是null的,除非生成的rowKey与其它行的数据的rowKey重复了,这样就不会调用storeObject方法,不会把数据加到resultHandler中if (partialObject == null) {//在resultHandler的list中增加当前行数据storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());}}}//【#resultOrdered为true时】isResultOrdered()为true,且是最后一行,增加当前行数据到resultHandlerif (rowValue != null && mappedStatement.isResultOrdered() && shouldProcessMoreRows(resultContext, rowBounds)) {storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());}}...
}

其中调用createRowKey(discriminatedResultMap, rsw, null);生成rowKey

package org.apache.ibatis.executor.resultset;
...
public class DefaultResultSetHandler implements ResultSetHandler {...  private CacheKey createRowKey(ResultMap resultMap, ResultSetWrapper rsw, String columnPrefix) throws SQLException {final CacheKey cacheKey = new CacheKey();cacheKey.update(resultMap.getId());//主键字段组成的listList<ResultMapping> resultMappings = getResultMappingsForRowKey(resultMap);if (resultMappings.size() == 0) {if (Map.class.isAssignableFrom(resultMap.getType())) {createRowKeyForMap(rsw, cacheKey);} else {createRowKeyForUnmappedProperties(resultMap, rsw, cacheKey, columnPrefix);}} else {//如果表设置了主键//修改cacheKey属性createRowKeyForMappedProperties(resultMap, rsw, cacheKey, resultMappings, columnPrefix);}return cacheKey;}...
}

其中createRowKeyForMappedProperties(resultMap, rsw, cacheKey, resultMappings, columnPrefix);修改cacheKey属性

package org.apache.ibatis.executor.resultset;
...
public class DefaultResultSetHandler implements ResultSetHandler {...  private void createRowKeyForMappedProperties(ResultMap resultMap, ResultSetWrapper rsw, CacheKey cacheKey, List<ResultMapping> resultMappings, String columnPrefix) throws SQLException {//循环主键包含的字段for (ResultMapping resultMapping : resultMappings) {if (resultMapping.getNestedResultMapId() != null && resultMapping.getResultSet() == null) {// Issue #392final ResultMap nestedResultMap = configuration.getResultMap(resultMapping.getNestedResultMapId());createRowKeyForMappedProperties(nestedResultMap, rsw, cacheKey, nestedResultMap.getConstructorResultMappings(),prependPrefix(resultMapping.getColumnPrefix(), columnPrefix));} else if (resultMapping.getNestedQueryId() == null) {//主键字段之一final String column = prependPrefix(resultMapping.getColumn(), columnPrefix);final TypeHandler<?> th = resultMapping.getTypeHandler();//查询出来的结果集中的字段的名称组成的listList<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) {//如果是,且字段中有值,把column和value更新到cacheKey中cacheKey.update(column);cacheKey.update(value);}}}}}...
}

所以,如果查询出的主键字段组合后不唯一,那么生成的cacheKey就不唯一,那么在handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)Object partialObject = nestedResultObjects.get(rowKey);的partialObject就不为空。

2.单主键时MyBatis的处理方式

在上述handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)方法中会调用handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);方法

package org.apache.ibatis.executor.resultset;
...
public class DefaultResultSetHandler implements ResultSetHandler { ...private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)throws SQLException {DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();skipRows(rsw.getResultSet(), rowBounds);//循环增加行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());}}...
}

三、解决方式

1.SQL中把select中的主键字段写全

<resultMap id="BaseResultMap" type="com.test.model.Entity"><id column="KEY_A" jdbcType="DECIMAL" property="keyA" /><id column="KEY_B" jdbcType="DECIMAL" property="keyB" /><id column="KEY_C" jdbcType="DECIMAL" property="keyC" /><result column="DES" jdbcType="DECIMAL" property="des" />
</resultMap>
<select id="findEntity" parameterType="com.test.model.Entity" resultMap="ResultMap">select KEY_A,KEY_B,KEY_C,DESfrom ENTITY
</select>

2.设置resultOrderedtrue

<resultMap id="BaseResultMap" type="com.test.model.Entity"><id column="KEY_A" jdbcType="DECIMAL" property="keyA" /><id column="KEY_B" jdbcType="DECIMAL" property="keyB" /><id column="KEY_C" jdbcType="DECIMAL" property="keyC" /><result column="DES" jdbcType="DECIMAL" property="des" />
</resultMap>
<select id="findEntity" parameterType="com.test.model.Entity" resultMap="ResultMap"resultOrdered="true">select KEY_A,KEY_B,DESfrom ENTITY
</select>

转载于:https://my.oschina.net/jerrypan/blog/1522772

MyBatis联合主键结果集与SQL查询结果不一致的问题相关推荐

  1. mysql联合主键语句6_初探SQL语句复合主键与联合主键

    一.复合主键 所谓的复合主键 就是指你表的主键含有一个以上的字段组成,不使用无业务含义的自增id作为主键. 比如 create table test ( name varchar(19), id nu ...

  2. mybatis-plus/mybatis的组件们——拦截器、字段填充器、类型处理器、表名替换、SqlInjector(联合主键处理)

    最近有个练手的小例子,大概就是配置两个数据源,从一个数据源读取数据写到另一个数据源,虽然最后做了出来,但是不支持事务...就当是对mybatis-plus/mybatis组件使用方式的记录吧,本次例子 ...

  3. SQL Server中的联合主键、聚集索引、非聚集索引、mysql 联合索引

    我们都知道在一个表中当需要2列以上才能确定记录的唯一性的时候,就需要用到联合主键,当建立联合主键以后,在查询数据的时候性能就会有很大的提升,不过并不是对联合主键的任何列单独查询的时候性能都会提升,但我 ...

  4. mysql 联合主键_Mysql 创建联合主键

    Mysql 创建联合主键 2008年01月11日 星期五 下午 5:21 使用primary key (fieldlist) 比如: create table mytable ( aa int, bb ...

  5. oracle联合主键效率,Oracle主键与复合主键的性能分析

    总结: 1.主键和复合主键,查询性能相同(索引高度相同,恰当的运用索引). 2.主键和复合主键,(update,insert)性能不同(因为复合主键会用更多的块来创建索引,所以update,inser ...

  6. MySql基础篇---003 SQL之DDL、DML、DCL使用篇:创建和管理表 ,数据处理之增删改,MySQL数据类型精讲 ,约束:联合主键

    第10章_创建和管理表 讲师:尚硅谷-宋红康(江湖人称:康师傅) 官网:http://www.atguigu.com 1. 基础知识 1.1 一条数据存储的过程 存储数据是处理数据的第一步.只有正确地 ...

  7. sqlite创建表联合主键的sql写法、执行sql文件、不支持右连接、获取年份、case when 的使用

    sqlite创建表时,联合主键,要写在建表语句最后一行,primary key (),括号里面: 执行sql文件:使用 .read xxx.sql 命令: 下图执行错误,应该是字段名含有中文,不能读取 ...

  8. 数据库原理与应用(SQL Server)教程 主键、外键以及联合主键、复合主键和设置种子数目和增量

    文章目录 前言 一.主键.联合主键和复合主键 (一)主键 (二)联合主键 (三)复合主键 二.外键.设置种子数目和增量 (一)外键的概念 (二)添加外键 (三)设置种子数目和增量 结语 前言 这篇文章 ...

  9. SQL联合主键 查重

    2014年最后一天,今天在给数据库导入数据的时候,遇到一个问题,就是联合主键去重. 事情是这样的,现有一个表M,我想找个表中导入了许多数据,并需要将字段A(int)和B(int)联合设置为主键. 但是 ...

最新文章

  1. 字符串扩展_JAVA
  2. web root下放置图片_Apache HTTP存在提权漏洞,威胁共享Web主机安全性
  3. Git基础操作及常见命令——详解
  4. nodejs不同浏览器跳转问题
  5. 三 .数据库(表操作)
  6. python给女朋友_【转】python实战——教你用微信每天给女朋友说晚安
  7. Hadoop学习笔记一:单节点安装
  8. 好用的论文翻译工具集锦
  9. JavaWeb项目实战一(Servlet+Jsp项目项目搭建及登录界面)
  10. cache stm32h7_STM32H7的Cache和MPU
  11. MIPAV - Talairach ACPC transform
  12. 种子计数法对种子公司的好处
  13. AV1的CDEF过程介绍
  14. 啥叫一个好售前​顾问
  15. IP101GR/IP101GA原理图和代码
  16. 【软件测试】自动化测试战零基础教程——Python自动化从入门到实战(六)
  17. python爬取地图地址_网络爬虫-python爬取高德地图地点
  18. 复杂网络入门详解 适用于初学者。超详细~~
  19. 单片微型计算机原理及应用实验报告,小学期单片机实验报告_相关文章专题_写写帮文库...
  20. 怎么使用ArcGIS进行坡度和坡向分析

热门文章

  1. 前后端交互ajax和axios入门讲解,以及http与服务器基础
  2. Android9 电池优化,Android 9 Pie正式发布!手势操作+优化电池,谷歌“亲儿子”尝...
  3. 【机器学习系列】概率图模型第三讲:深入浅出无向图中的条件独立性和因子分解
  4. 全网最全的云原生存储 OpenEBS 使用指南
  5. 找不到dns linux,linux – 服务器找不到XXX.in-addr.arpa:NXDOMAIN
  6. 营销策划 —— 论 营销策划书
  7. 华为机试题:【中级】报文转换
  8. 知名外企急招:网络工程师,安全专家,语音工程师,自动化等职位
  9. 有趣的8086汇编小程序
  10. 掌握网购DAPP的编写