【问题标题】:Anorm and enum fields异常和枚举字段
【发布时间】:2012-01-23 15:19:43
【问题描述】:

我只是想知道如何在带有 Anorm 的案例类中使用枚举文件(类,从枚举扩展)。例如:

object Type extends Enumeration {
    type Type = Value
    val User, Admin = Value
}

case class User (
    id: Pk[Long],
    type: Type.Type
)

如果我尝试这样的代码,我会得到“RuntimeException: no supported constructors for type models.User”。我应该为此目的使用什么数据库字段类型?我试过 varchar 没有运气。

【问题讨论】:

  • 我很惊讶您没有收到编译器错误。 type 是 scala 中的关键字,因此尝试在案例类中定义 val type 应该会中断。
  • 你找到解决方案了吗?
  • Dustin Getz:不,刚刚切换回 JPA。

标签: scala enums playframework anorm


【解决方案1】:

你可以通过反思来做到这一点。给定下面的代码(scala 2.10),做一个像

这样的导入
import AnormEnumerationExtension.ToId._

并且您的枚举通过它的 id 持续存在。如果你想坚持它的名字使用

import AnormEnumerationExtension.ToName._

最后是 EnumExtension:

import java.lang.reflect.InvocationTargetException
import scala.reflect.runtime.universe._

object AnormEnumerationExtension {      
  private lazy val rm = scala.reflect.runtime.currentMirror

  object ToId {
    implicit def enumToParameterValue[E <: Enumeration: TypeTag](enumValue: E#Value): ParameterValue[Int] = enumValue.id

    implicit def rowToEnumValue[E <: Enumeration: TypeTag]: Column[E#Value] = Column.nonNull { (value, meta) =>
      val MetaDataItem(qualified, _, _) = meta
      value match {
        case int: Int => {
          val methodSym = typeTag[E].tpe.member(newTermName("apply")).asMethod
          val im = rm.reflect(rm.reflectModule(typeOf[E].termSymbol.asModule).instance)
          try {
            Right(im.reflectMethod(methodSym)(int).asInstanceOf[E#Value])
          } catch {
            case e: InvocationTargetException if e.getCause.isInstanceOf[NoSuchElementException] =>
              Left(SqlMappingError(s"Can't convert $int to ${typeTag[E].tpe}, because it isn't a valid value: $qualified"))
          }
        }
        case _ => Left(TypeDoesNotMatch(s"Can't convert [$value:${value.asInstanceOf[AnyRef].getClass}] to ${typeTag[E].tpe}: $qualified"))
      }
    }
  }

  object ToName {
    implicit def enumToParameterValue[E <: Enumeration: TypeTag](enumValue: E#Value): ParameterValue[String] = enumValue.toString

    implicit def rowToEnumValue[E <: Enumeration: TypeTag]: Column[E#Value] = Column.nonNull { (value, meta) =>
      val MetaDataItem(qualified, _, _) = meta
      value match {
        case string: String => {
          val methodSym = typeTag[E].tpe.member(newTermName("withName")).asMethod
          val im = rm.reflect(rm.reflectModule(typeOf[E].termSymbol.asModule).instance)
          try {
            Right(im.reflectMethod(methodSym)(string).asInstanceOf[E#Value])
          }
          catch {
            case e: InvocationTargetException if e.getCause.isInstanceOf[NoSuchElementException] =>
              Left(SqlMappingError(s"Can't convert '$string' to ${typeTag[E].tpe}, because it isn't a valid value: $qualified"))
          }
        }
        case _ => Left(TypeDoesNotMatch(s"Can't convert [$value:${value.asInstanceOf[AnyRef].getClass}] to ${typeTag[E].tpe}: $qualified"))
      }
    }
  }
}

【讨论】:

  • 我偶尔会使用此代码得到scala.reflect.internal.Symbols$CyclicReference: illegal cyclic reference involving object ProcessTemplateStatus 错误,其中ProcessTemplateStatus 是我的枚举。知道为什么吗?
  • 从来没有出现过这种错误,但无论如何最好不要使用上面的代码,直到 scala 的运行时反射是线程安全的。
  • 是的,我在 Play 2 中使用它,所以我认为这可能是线程问题。我只是为每个 Enumeration 分别创建了方法来解决它。
猜你喜欢
  • 1970-01-01
  • 2019-05-26
  • 1970-01-01
  • 1970-01-01
  • 2018-10-21
  • 1970-01-01
  • 1970-01-01
  • 2016-01-16
  • 2011-12-07
相关资源
最近更新 更多