Scala项目中写入数据库后读取用Option#get是否真的不可取?
解决Wartremover对Option#get的报错问题(针对刚写入即读取的场景)
我完全懂你的感受——刚把Answer写入数据库,紧接着就读取,按道理不可能出现None,用get看起来简直天经地义。但Wartremover的规则就是要杜绝所有潜在的get风险,不过我们有几个既合规又健壮的方案:
1. 用fold实现兜底(Wartremover推荐方式)
既然Wartremover推荐用fold,那我们就用它来处理理论上不会出现的None情况,同时抛出一个比默认异常更有意义的错误:
def create(answer: Answer): Future[Answer] = { writeToDb(answer) // 返回Future[Long] .flatMap { answerId => readFromDb(answerId) // 返回Future[Option[Answer]] .map { _.fold( throw new IllegalStateException(s"刚创建的Answer(id=$answerId)无法读取,可能存在数据库一致性问题!") )(existingAnswer => existingAnswer) } } }
这样做的好处:
- 完全符合Wartremover的规则,不会再报错
- 即使真的出现极端情况(比如数据库写入后未立即同步),你会得到一个带有明确上下文的异常,而不是模糊的
NoSuchElementException,方便快速排查问题
2. 用getOrElse配合自定义异常
如果觉得fold的写法有点啰嗦,也可以用getOrElse来实现类似的兜底:
def create(answer: Answer): Future[Answer] = { writeToDb(answer) .flatMap { answerId => readFromDb(answerId) .map(_.getOrElse( throw new IllegalStateException(s"Failed to fetch newly created Answer with id: $answerId") )) } }
这个写法更简洁,同样能满足规范,并且提供清晰的错误信息。
3. 从根源优化数据库交互(可选)
如果你的架构允许,可以考虑调整writeToDb的返回值——让它直接返回写入后的Future[Answer],而不是仅仅返回id。这样就完全不需要后续的readFromDb操作,自然也就避开了Option的问题:
def writeToDb(answer: Answer): Future[Answer] = { // 执行插入逻辑后,直接返回包含完整信息的Answer } def create(answer: Answer): Future[Answer] = writeToDb(answer)
当然,这个方案取决于你的数据库层实现是否支持,但如果能做到的话,这是最简洁的解决方式。
最后说句题外话
虽然你坚信这里不会出现None,但生产环境总有各种意外:比如分布式数据库的读写延迟、插入操作的隐式失败(比如事务回滚但返回了id)等。用明确的异常兜底,比直接get要健壮得多,也更符合Scala的函数式编程理念。
内容的提问来源于stack exchange,提问作者Cedric Reichenbach
相关产品推荐
相关产品推荐

