记一次MyBatisPlus中SelectOne踩坑

2020-12-21 22:04:23  卢浮宫  版权声明:本文为站长原创文章,转载请写明出处


一、背景

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

    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());
}

        ② 这里可以看到是调用了selectOne()方法,同样我们在使用条件构造器进行查询时:

         // 日常项目中构造器查询代码示例
         @Override
public UserEntity queryByMobile(String mobile) {
return baseMapper.selectOne(new QueryWrapper<UserEntity>().eq("mobile", mobile));
}

            其中的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


五、后记

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

    更多精彩内容,欢迎持续关注:guangmuhua.com

    [舔屏]




最新评论: