编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

记一次MyBatisPlus中SelectOne踩坑

wxchong 2024-07-16 10:08:00 开源技术 22 ℃ 0 评论

一、背景

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

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

Copy

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

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

Java

Copy

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

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

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

Java

Copy

我们可以发现:

。有异常抛给上层处理

。正常情况下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;
}

Java

Copy

④ 通过上述表明:

。当只有一条数据返回时程序能正常执行

。当数据返回多条时就会给出异常提示了


四、问题解决

① 直接使用MyBatisPlus的last方法在sql末尾追加语句“limit 1”,代码如下:

BHOrderCargoSizeEntity splitCargoSize =
bhOrderCargoSizeService.lambdaQuery().eq(BHOrderCargoSizeEntity::getSplitId,x.getId())
.last("limit 1").one();

② 网上有一些重写selectOne方法的,基本通过重写或切面编程。有兴趣可以了解下(不建议新手这样操作!

PS:关于MyBatisPlus的语法知识点也可以参考我之前的文章:

初见MyBatisPlus


五、后记

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

更多精彩内容,欢迎持续关注:光华技术

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表