场景复现
在需求开发的时候,写了一个查询接口,作用根据全局 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 常见的返回类型有下面这些:
- int / long :仅仅返回一个数字,通常是用来表示数量查询或者有多少受影响的行。
- boolean :结果为空时返回 false ;否则返回 true
- List<T> / Set<T> :用于返回多条结果。
需要注意的是,当得到的结果为0条时,不会返回一个 null ,而是空的 List 集合,所以我们在判断查询结果是否为空时不能使用 = null , 而是 isEmpty()。 - pojo / map : 用于返回单行数据,查询结果必须是0条或者1条,大于一会直接报错。查询结果0条是返回null。
- 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
Comments NOTHING