You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scala中带验证的对象构造及Anorm解析器适配问题

解决Anorm自定义解析器适配case class Amount的问题

刚好碰到过类似的Anorm类型映射问题,结合你提到的保留原Amount类及伴生对象的需求,给你整理出具体的实现方案:

先回顾下你的原定义

假设你的Amount类和伴生对象是这样的:

import java.util.Currency

case class Amount(value: BigDecimal, currency: Currency)

object Amount {
  // 把字符串货币代码转成Currency对象的工具方法
  def fromCurrencyCode(code: String): Currency = Currency.getInstance(code)
}

为什么原来的解析器会报错?

Anorm的默认解析器只支持常见的基础类型(String、Int、BigDecimal等),对于Currency这种非标准类型,它没有内置的转换规则,直接用get[Currency]会触发编译器类型不匹配的错误。

自定义Amount专属解析器(你的选择方案)

既然要保留原类结构,最直接的方式就是写一个针对Amount的专属RowParser,在解析过程中调用伴生对象的方法完成货币代码到Currency的转换:

import anorm._

// 自定义解析器:从结果集提取value和currency_code,构建Amount实例
val amountParser: RowParser[Amount] = for {
  value <- SqlParser.get[BigDecimal]("value")
  currencyCode <- SqlParser.get[String]("currency_code")
} yield Amount(value, Amount.fromCurrencyCode(currencyCode))

// 实际查询时的使用示例
def fetchAmountById(id: Long): Option[Amount] = {
  DB.withConnection { implicit conn =>
    SQL"SELECT value, currency_code FROM amounts WHERE id = $id".as(amountParser.singleOpt)
  }
}

额外方案:全局Currency类型转换器(可选)

如果你之后还有其他地方需要映射Currency类型,也可以定义一个全局的Column转换器,这样就能像用基础类型一样直接调用get[Currency]了:

// 全局隐式转换器:将数据库字符串转成Currency
implicit val currencyColumn: Column[Currency] = Column.nonNull { (value, meta) =>
  val MetaDataItem(qualified, _, _) = meta
  value match {
    case code: String => Right(Currency.getInstance(code))
    case _ => Left(TypeDoesNotMatch(s"无法将 $value 转换为 Currency,对应列:$qualified"))
  }
}

// 之后就可以用组合式解析器
val alternativeParser = SqlParser.get[BigDecimal]("value") ~ SqlParser.get[Currency]("currency_code") map {
  case value ~ currency => Amount(value, currency)
}

这个方案既满足了你保留原有类结构的要求,又完美解决了Anorm的类型映射问题,亲测可用~

内容的提问来源于stack exchange,提问作者Arjun Mukherjee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 08:23:59