光滑:组成的可选列为组成的可选类型

时间:2019-06-25 22:09:58

标签: scala slick optional

给出:

case class Money(value: BigDecimal, currency: Currency)
trait Currency
case class EUR ( ... ) extends Currency
... and so on

我想对一个可选的Money类型进行建模,该类型包含一个value部分(BigDecimal)和一个Currency部分(一个Enum到Enumeratum)。

要在数据库中映射它,我有两个可选的列:

    private def moneyOptionalValue: Rep[Option[BigDecimal]]  = column[Option[BigDecimal]]("money_value")     
    private def moneyOptionalCurrency: Rep[Option[Currency]] = column[Option[Currency]]("money_currency")    

所需状态是:

   def money: Rep[Option[Money]] = ... 

我尝试了这种方法,如 http://slick.lightbend.com/doc/3.1.0/userdefined.html#using-custom-record-types-in-queries

    private implicit def myCurrencyType: BaseColumnType[Currency] = MappedColumnType.base[Currency, String](
      c => c.shortName,
      s => Currency.withNameUppercaseOnly(s.toUpperCase)
    )

    case class LiftedMoney(value: Rep[BigDecimal], currency: Rep[Currency])
    case class LiftedMoneyOptional(a: Rep[Option[BigDecimal]], b: Rep[Option[Currency]])
    case class MoneyOptional(a: Option[BigDecimal], b: Option[Currency])

    // custom case class mapping
    implicit object MoneyOptionalShape extends CaseClassShape(LiftedMoneyOptional.tupled, MoneyOptional.tupled)
    implicit object MoneyShape         extends CaseClassShape(LiftedMoney.tupled, Money.tupled)

    implicit object MoneyToOptional
        extends Isomorphism[Option[Money], MoneyOptional](
          {
            case Some(m) => MoneyOptional(Some(m.value), Some(m.currency))
            case None    => MoneyOptional(None, None)
          }, {
            case MoneyOptional(Some(v), Some(c)) => Some(Money(v, c))
            case MoneyOptional(_, _)             => None
          }
        )

    private def moneyOptionalValue: Rep[Option[BigDecimal]]  = column[Option[BigDecimal]]("money_value")
    private def moneyOptionalCurrency: Rep[Option[Currency]] = column[Option[Currency]]("money_currency")

    def moneyOptional: Rep[MoneyOptional] =
      moneyOptionalValue.zip(moneyOptionalCurrency).mapTo[MoneyOptional]

    def moneyOptional1: MappedProjection[MoneyOptional, (Option[BigDecimal], Option[Currency])] =
      moneyOptionalValue.zip(moneyOptionalCurrency).mapTo[MoneyOptional]


   // this compiles, if its not optional, but would break at runtime, when null values are in the columns:
    private def moneyValue: Rep[BigDecimal]  = column[BigDecimal]("money_value") // this column can be nullable!
    private def moneyCurrency: Rep[Currency] = column[Currency]("money_currency") // // this column can be nullable!

    def money: Rep[Money] = moneyValue.zip(moneyCurrency).value.mapTo[Money]

// I also tried this, but cannot compile my code with that for some reason

    def optionMoney: Rep[Option[Money]] = RepOption[Money](money, money.toNode)

任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:0)

那一个呢?

  def money = (moneyOptionalValue, moneyOptionalCurrency) <> [Option[Money]](
    {
      case (value,currency) if value.isDefined && currency.isDefined => Some(Money(value.get, currency.get))
      case _ => None
    },
    (mapped:Option[Money]) => mapped.map( m => Some(Some(m.value), Some(m.currency))).getOrElse(Some(None,None))
  )