mybatis selectone查不到数据时返回null是设计行为而非bug,需用optional.ofnullable包装后链式安全操作,避免npe。

MyBatis 查询结果为 null 时,直接在 Function 中使用容易触发 NullPointerException。关键不是“避免 null”,而是让 null 可控、可预期、可转化。
明确 MyBatis 的 null 返回场景
MyBatis 的 selectOne() 在查不到数据时返回 null(不是空集合),这是设计行为,不是 bug。尤其在用 Function<t r></t> 做链式转换(比如 userMapper.selectById(id).map(User::getName))时,若未判空就调用方法,必然崩溃。
- 单对象查询(
selectOne、getMapper().xxx())→ 返回T或null - 列表查询(
selectList)→ 永远返回List<t></t>(空集合,非 null) -
Optional不是 MyBatis 原生支持的返回类型,需手动包装
用 Optional 封装单对象查询结果
把可能为 null 的 Mapper 方法返回值主动转成 Optional<t></t>,再配合 Function 安全使用。
推荐写法(不改 Mapper 接口):
Optional<user> userOpt = Optional.ofNullable(userMapper.selectById(userId));</user>
之后就能安全链式操作:
userOpt.map(User::getName).orElse("未知用户")userOpt.filter(u -> u.getStatus() == ACTIVE).map(User::getEmail).orElse(null)- 配合 Stream:
Stream.ofNullable(userMapper.selectById(id)).map(...).findFirst()
定义带语义的空值处理策略
不要无脑用 orElse(null) 或 orElseThrow()。根据业务含义选择合适 fallback:
- 缺省值型:如用户名为空 →
orElse("游客") - 异常兜底型:如订单必须存在 →
orElseThrow(() -> new OrderNotFoundException("id=" + id)) - 跳过处理型:如做统计时忽略缺失记录 →
filter(Objects::nonNull).map(...) - 统一空对象型:定义
User.EMPTY或使用 Builder 模式构造默认实例
在 Function 链中提前防御 null
如果无法控制上游返回(比如第三方封装的 Mapper 调用),可在 Function 内部做防御性检查:
Function<user string> safeGetName = user -> {<br> if (user == null) return "N/A";<br> return user.getName();<br>};</user>
更简洁写法(Java 8+):
- 用
Objects.toString(user?.getName(), "N/A")(需先判 user) - 组合函数:
Function<user string> getName = User::getName;<br>Function<user string> safeGet = u -> Optional.ofNullable(u).map(getName).orElse("N/A");</user></user>
注意:不要在 lambda 表达式里写复杂逻辑,保持纯函数特性;复杂转换建议提取为独立方法。
核心是把 null 当作合法状态来设计,而不是异常。MyBatis 的 null 是信号,不是错误 —— 关键在于你是否听懂了它想表达什么。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











