记一次MyBatisPlus中SelectOne踩坑 一个数据查询结果集不符的问题

一、背景

今天一个线上问题报错如下:

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<UserEntity>().eq("mobile", mobile));
	  }

Java

其中的selectOne()操作也是单条数据查询的。

于是我们找到如下部分代码:

         // 这里是MyBatisPlus源码
         @Override
         public T getOne(Wrapper<T> queryWrapper, boolean throwEx) {
             if (throwEx) {
                return baseMapper.selectOne(queryWrapper);
             }
             return SqlHelper.getObject(baseMapper.selectList(queryWrapper));
         }


我们可以发现:

。有异常抛给上层处理

。正常情况下getOne()的操作是selectList()实现的


③ 当有多条数据时就有了如下的情况:

            // 这里是MyBatisPlus源码
            /**
             * <p>
             * 从list中取第一条数据返回对应List中泛型的单个结果
             * </p>
             *
             * @param list
             * @param <E>
             * @return
            */
            public static <E> E getObject(List<E> 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


五、后记

遇到问题只知道怎么解决是不够的,能追其本质才是乐趣所在。

举报
评论 0