MyBatis联合主键结果集与SQL查询结果不一致的问题
2019独角兽企业重金招聘Python工程师标准>>>
一、问题
如果select中的联合主键组合成的key不唯一(当只select部分联合主键时可能发生),那么就会把相同的key合并成一条数据。
例如KEY_A
,KEY_B
,KEY_C
是联合主键
KEY_A | KEY_B | KEY_C | des |
---|---|---|---|
A1 | B1 | C1 | 数据1 |
A1 | B2 | C1 | 数据2 |
A1 | B2 | C2 | 数据3 |
如果只返回KEY_A
,KEY_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.设置resultOrdered
为true
<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查询结果不一致的问题相关推荐
- mysql联合主键语句6_初探SQL语句复合主键与联合主键
一.复合主键 所谓的复合主键 就是指你表的主键含有一个以上的字段组成,不使用无业务含义的自增id作为主键. 比如 create table test ( name varchar(19), id nu ...
- mybatis-plus/mybatis的组件们——拦截器、字段填充器、类型处理器、表名替换、SqlInjector(联合主键处理)
最近有个练手的小例子,大概就是配置两个数据源,从一个数据源读取数据写到另一个数据源,虽然最后做了出来,但是不支持事务...就当是对mybatis-plus/mybatis组件使用方式的记录吧,本次例子 ...
- SQL Server中的联合主键、聚集索引、非聚集索引、mysql 联合索引
我们都知道在一个表中当需要2列以上才能确定记录的唯一性的时候,就需要用到联合主键,当建立联合主键以后,在查询数据的时候性能就会有很大的提升,不过并不是对联合主键的任何列单独查询的时候性能都会提升,但我 ...
- mysql 联合主键_Mysql 创建联合主键
Mysql 创建联合主键 2008年01月11日 星期五 下午 5:21 使用primary key (fieldlist) 比如: create table mytable ( aa int, bb ...
- oracle联合主键效率,Oracle主键与复合主键的性能分析
总结: 1.主键和复合主键,查询性能相同(索引高度相同,恰当的运用索引). 2.主键和复合主键,(update,insert)性能不同(因为复合主键会用更多的块来创建索引,所以update,inser ...
- MySql基础篇---003 SQL之DDL、DML、DCL使用篇:创建和管理表 ,数据处理之增删改,MySQL数据类型精讲 ,约束:联合主键
第10章_创建和管理表 讲师:尚硅谷-宋红康(江湖人称:康师傅) 官网:http://www.atguigu.com 1. 基础知识 1.1 一条数据存储的过程 存储数据是处理数据的第一步.只有正确地 ...
- sqlite创建表联合主键的sql写法、执行sql文件、不支持右连接、获取年份、case when 的使用
sqlite创建表时,联合主键,要写在建表语句最后一行,primary key (),括号里面: 执行sql文件:使用 .read xxx.sql 命令: 下图执行错误,应该是字段名含有中文,不能读取 ...
- 数据库原理与应用(SQL Server)教程 主键、外键以及联合主键、复合主键和设置种子数目和增量
文章目录 前言 一.主键.联合主键和复合主键 (一)主键 (二)联合主键 (三)复合主键 二.外键.设置种子数目和增量 (一)外键的概念 (二)添加外键 (三)设置种子数目和增量 结语 前言 这篇文章 ...
- SQL联合主键 查重
2014年最后一天,今天在给数据库导入数据的时候,遇到一个问题,就是联合主键去重. 事情是这样的,现有一个表M,我想找个表中导入了许多数据,并需要将字段A(int)和B(int)联合设置为主键. 但是 ...
最新文章
- 字符串扩展_JAVA
- web root下放置图片_Apache HTTP存在提权漏洞,威胁共享Web主机安全性
- Git基础操作及常见命令——详解
- nodejs不同浏览器跳转问题
- 三 .数据库(表操作)
- python给女朋友_【转】python实战——教你用微信每天给女朋友说晚安
- Hadoop学习笔记一:单节点安装
- 好用的论文翻译工具集锦
- JavaWeb项目实战一(Servlet+Jsp项目项目搭建及登录界面)
- cache stm32h7_STM32H7的Cache和MPU
- MIPAV - Talairach ACPC transform
- 种子计数法对种子公司的好处
- AV1的CDEF过程介绍
- 啥叫一个好售前​顾问
- IP101GR/IP101GA原理图和代码
- 【软件测试】自动化测试战零基础教程——Python自动化从入门到实战(六)
- python爬取地图地址_网络爬虫-python爬取高德地图地点
- 复杂网络入门详解 适用于初学者。超详细~~
- 单片微型计算机原理及应用实验报告,小学期单片机实验报告_相关文章专题_写写帮文库...
- 怎么使用ArcGIS进行坡度和坡向分析
热门文章
- 前后端交互ajax和axios入门讲解,以及http与服务器基础
- Android9 电池优化,Android 9 Pie正式发布!手势操作+优化电池,谷歌“亲儿子”尝...
- 【机器学习系列】概率图模型第三讲:深入浅出无向图中的条件独立性和因子分解
- 全网最全的云原生存储 OpenEBS 使用指南
- 找不到dns linux,linux – 服务器找不到XXX.in-addr.arpa:NXDOMAIN
- 营销策划 —— 论 营销策划书
- 华为机试题:【中级】报文转换
- 知名外企急招:网络工程师,安全专家,语音工程师,自动化等职位
- 有趣的8086汇编小程序
- 掌握网购DAPP的编写