记录 MyBatis 返回值错误带来的问题

gomkiri 发布于 2025-12-25 68 次阅读


AI 摘要

MyBatis返回值类型暗藏玄机:Map类型竟自动添加selectOne限制?查询结果超1条直接报错!本文详解mapper各种返回类型的正确用法,避免踩坑。

场景复现

在需求开发的时候,写了一个查询接口,作用根据全局 ID 查询一个表中的对应人员,实现方式是 MyBatis ,但是在测试的过程中就发现了一个问题:当查询到的数据超过两条时,就会直接抛出异常:
TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 2
但是在后端看了一圈,也没有发现有类似于selectOne()的条件,经过 AI 辅助排查,发现问题出现在了 mapper 接口的定义上:
Map<String, Object> getMeetingCommentsByGlobalId(@Param("globalId") String globalId,@Param("hjType") Integer hjType);
经了解,对于返回值类型为 Map 的 mapper 语句,会自动在 sql 中添加一个selectOne()的限制,一旦查询出现的数据大于等于2,就会直接发生上面的报错。

想解决这个问题也很简单:将返回类型改为 list 即可,并且我已经写好了他对应的 Java Bean,所以只要将这里的返回值类型改为List<javaBean>即可完美解决。

总结梳理

mapper 常见的返回类型有下面这些:

  1. int / long :仅仅返回一个数字,通常是用来表示数量查询或者有多少受影响的行。
  2. boolean :结果为空时返回 false ;否则返回 true
  3. List<T> / Set<T> :用于返回多条结果。
    需要注意的是,当得到的结果为0条时,不会返回一个 null ,而是空的 List 集合,所以我们在判断查询结果是否为空时不能使用 = null , 而是 isEmpty()。
  4. pojo / map : 用于返回单行数据,查询结果必须是0条或者1条,大于一会直接报错。查询结果0条是返回null。
  5. Optional<T>:
    当返回类型为 pojo/map 时,查询结果为0条时返回的是 null。这就要求我们在使用之前必须判断是否真的拿到了数据,否则很容易出现空指针异常。而 Optional<T> 可以通过链式使用的方式帮我们优化这个流程。

Optional<T> 示例:

用一个 getUserById 为例,首先对接口进行改造:
User getUserById(String id); --> Optional<User> getUserById(String id);

在原来的mapper.getUserById(id)的基础上,我们必须链式的添加一个orElse方法来添加一个没找到数据时的兜底方案。

找不到数据时 new 一个兜底数据:

User user = mapper.getUserById(id).orElse(new User("匿名用户"));
Java

直接抛出特定异常:

User user = mapper.getUserById(id)
                  .orElseThrow(() -> new ServiceException("未找到相关记录"));
Java

对数据进行预处理后再做错误校验:

String userName = mapper.getUserById(id)
                        .map(User::getName)
                        .orElse("Unknown");
Java
小码农 & GPT调教糕手
最后更新于 2025-12-28