【问题标题】:SORM and trait as field typeSORM 和 trait 作为字段类型
【发布时间】:2014-03-01 16:45:07
【问题描述】:

我正在使用最新的 SORM v0.3.15 和 MySQL。

是否可以将特征用作实体的字段类型?因为我想实现以下内容:

trait Votable

case class Post(...) extends Votable
case class Comment(...) extends Votable

case class VoteHistory(.., source: Votable)

由于 sorm 的工作原理(反射等),我对此有些怀疑

现在我有一个例外Caused by: sorm.core.SormException: Unsupported type: models.entities.Votable

【问题讨论】:

    标签: mysql scala sorm


    【解决方案1】:

    根据源代码,您的 Votable 类型应该是以下之一:Range、Seq、Set、Map、Option、AnyVal、String、BigDecimal、Date、Time、Enumeration、Tuple1-22 ,或案例类。 详情请查看./src/main/scala/sorm/mappings/MappingKind.scala。这是一个sn-p:

    sealed trait MappingKind
    
    object MappingKind {
    
      case object Enum             extends MappingKind
      case object Entity           extends MappingKind
      case object Value            extends MappingKind
      case object Tuple            extends MappingKind
      case object OptionToTable    extends MappingKind
      case object OptionToNullable extends MappingKind
      case object Seq              extends MappingKind
      case object Set              extends MappingKind
      case object Map              extends MappingKind
      case object Range            extends MappingKind
    
      def apply
        ( reflection : Reflection )
        : MappingKind
        = reflection match {
            case _
              if reflection <:< Reflection[scala.Range]
              => Range
            case _
              if reflection <:< Reflection[collection.Seq[_]]
              => Seq
    
            ...
    
            case _
              if (reflection <:< Reflection[Tuple1[_]])
              || (reflection <:< Reflection[Tuple2[_, _]])
              || (reflection <:< Reflection[Tuple3[_, _, _]])
    
            ...
    
              || (reflection <:< Reflection[Tuple22[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]])
              => Tuple
            case _
              if reflection.isCaseClass
              => Entity
            case _
              => throw new SormException("Unsupported type: " + reflection)
          }
    }
    

    支持的数据类型doc

    那里使用的反射测试是./src/main/scala/sorm/reflection/Reflection.scala

      def isCaseClass
        = s match {
            case s : ClassSymbol => s.isCaseClass
            case _ => false
          }
    

    Votable 的问题在于它不是这些列出的任何类型的子类型。因此 Scala 不能确保Votable 的子类将是MappingKind 的兼容子类型。换句话说,不能保证你不会这样做:class A extends Votable 不能被 Sorm 序列化,然后它会在运行时爆炸。

    现在问题很清楚了,所以我们希望例如将Votable 设为一个案例类并从它继承另一个案例类,但由于违反平等,这在 Scala 中是不允许的。此外,Scala 中没有统一的类型将某物标记为案例类。

    例如,试图伪造“案例类类型”失败:

    scala> trait Votable
    defined trait Votable
    
    scala> case class Comment(b: String) extends Votable
    defined class Comment
    
    scala> def isCaseClassOrWhat_?(v: AnyRef): Boolean = {
         |   import reflect.runtime.universe._
         |   val typeMirror = runtimeMirror(v.getClass.getClassLoader)
         |   val instanceMirror = typeMirror.reflect(v)
         |   val symbol = instanceMirror.symbol
         |   symbol.isCaseClass
         | }
    isCaseClassOrWhat_$qmark: (v: AnyRef)Boolean
    
    scala> isCaseClassOrWhat_?(new Votable {});
    res0: Boolean = false
    
    scala> isCaseClassOrWhat_?(Comment("df"));
    res1: Boolean = true
    
    scala> trait VotableCaseClass extends Product with Serializable
    defined trait VotableCaseClass
    
    scala> isCaseClassOrWhat_?(new VotableCaseClass { def canEqual(that: Any): Boolean = ???; def productArity: Int = ???; def productElement(n: Int): Any = ??? });
    res2: Boolean = false
    

    如果我没看错的话,从 Scala 编译器代码中我只能理解 isCaseClass 是通过编译器在类上设置 CASE 标志和 isCaseClass 测试该标志的存在来实现的。

    我不知道如何解决这个特殊问题。也许 Storm 可以为此提供另一个案例/标记界面。它可能是结构类型检查或Product with Serializable 或类似的东西。也许可以使用 Scala 宏来做到这一点 - 我对它们了解不多。

    编辑:

    您可以使用Map 类型进行序列化并将案例类转换为Map,如下所示:

    def caseClassToMap(cc: Product): Map[String,Any] = {
      val fields = cc.getClass.getDeclaredFields.map(_.getName)
      fields.zip(cc.productIterator.to).toMap
    }
    
    scala> case class A(a: Int)
    defined class A
    
    scala> caseClass2Map(A(123))
    res14: scala.collection.immutable.Map[String,Any] = Map(a -> 123)
    

    我没有测试它是否适用于 SORM。

    【讨论】:

    • 确实,SORM 确实只对最终类型进行操作,因为它从中提取了所有映射信息。像特征这样的“开放”类型不能提供足够的信息。在 OP 的情况下,我建议考虑 SORM 支持的标准 Scala 枚举。
    • 感谢@NikitaVolkov 的确认
    • 嗯,看起来实现目标(或类似目标)的唯一方法是从 PostComment 到案例​​类 Votable 的隐式转换
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多