一、背景 今天一个线上问题报错如下: org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 2 通过报错信息可以知道这个是一个数据查询结果集不符的问题,程序中预期是只有一条数据的,但是查出来的结果有多个。 二、问题排查 ① 先看数据:提取出来SQL语句执行发现真的是有2条符合记录 ② 那就是SQL语句的编写有问题,定位SQL语句产生的那一行代码 ③ 发现使用了MyBatisPlus的LambdaQuery()查询中的one()方法 PS: 这里贴一下出错的代码行 BHOrderCargoSizeEntity splitCargoSize = bhOrderCargoSizeService.lambdaQuery().eq(BHOrderCargoSizeEntity::getSplitId,x.getId()).one(); 三、问题深究3.1、selectOne()第一印象 可能对于多数人one()方法给人的第一印象是:取查询结果的其中一个然后返回3.2、真正的selectOne() 既然都已经产生的错误那selectOne()的实际操作肯定不是和上面的想的一样的。我们点进去MP的源码可以看到如下: ① 先点击去One()方法可以看到如下: // 这里是MyBatisPlus源码 /** * 获取单个 * * @return 单个 */ default T one() { return getBaseMapper().selectOne(getWrapper()); } Java ② 这里可以看到是调用了selectOne()方法,同样我们在使用条件构造器进行查询时: // 日常项目中构造器查询代码示例 @Override public UserEntity queryByMobile(String mobile) { return baseMapper.selectOne(new QueryWrapper().eq("mobile", mobile)); } Java 其中的selectOne()操作也是单条数据查询的。 于是我们找到如下部分代码: // 这里是MyBatisPlus源码 @Override public T getOne(Wrapper queryWrapper, boolean throwEx) { if (throwEx) { return baseMapper.selectOne(queryWrapper); } return SqlHelper.getObject(baseMapper.selectList(queryWrapper)); } 我们可以发现: 。有异常抛给上层处理 。正常情况下getOne()的操作是selectList()实现的 ③ 当有多条数据时就有了如下的情况: // 这里是MyBatisPlus源码 /** * * 从list中取第一条数据返回对应List中泛型的单个结果 * * * @param list * @param
* @return */ public static E getObject(List list) { if (CollectionUtils.isNotEmpty(list)) { int size = list.size(); if (size > 1) { logger.warn(String.format("Warn: execute Method There are %s results.", size)); } return list.get(0); } return null; } ④ 通过上述表明: 。当只有一条数据返回时程序才能正常执行 。当数据返回多条时就会给出异常提示了 四、问题解决 ① 直接使用MyBatisPlus的last方法在sql末尾追加语句"limit 1",代码如下: BHOrderCargoSizeEntity splitCargoSize = bhOrderCargoSizeService.lambdaQuery().eq(BHOrderCargoSizeEntity::getSplitId,x.getId()).last("limit 1") .one(); ② 网上有一些重写selectOne方法的,基本通过重写或切面编程。有兴趣可以了解下(不建议新手这样操作! ) PS:关于MyBatisPlus的语法知识点也可以参考我之前的文章: 初见MyBatisPlus 五、后记 遇到问题只知道怎么解决是不够的,能追其本质才是乐趣所在。