使用Squeryl和基类抽象CRUD操作

时间:2013-12-23 16:52:02

标签: scala scalatra squeryl

我加入了那些试图将Squeryl用作我的网络应用程序ORM框架的人们。 (为了记录,我使用Scalatra作为实际的Web框架 - 但我不认为这是一个Scalatra问题。)这意味着我加入了试图建立一个有效的抽象层来干我们的人的行列。常见的操作。例如,看到这样的例子很常见:

// First Model

package com.myproj
import com.myproj.Schema

class Foo() extends KeyedEntity {
  val id = 0
  def getAll() = { from(Schema.Foo)(s => select(s)) }    
}

// Different Model

package com.myproj
import com.myproj.Schema

class Bar() extends KeyedEntity {
  val id = 0    
  def getAll() = { from(Schema.Boo)(s => select(s)) }   
}

所以一方面,我正在挖掘Squeryl语法的简洁性。另一方面:这是非常重复的。我想要的更像是:

// Base

class BaseEntity extends KeyedEntity {
  val id = 0
  def getAll() = { from(table)(s => select(s)) }
}

// New model

class Foo extends BaseEntity

// New model

class Bar extends BaseEntity

所以我主要有这个工作。扩展KeyedEntity非常简单。只有一个问题:你如何在BaseEntity中定义一个表,以便扩展它的类可以访问它?说实话,这可能从根本上说是我对Scala类型系统缺乏足够深入理解的问题。无论如何我在这里摆姿势。

我尝试了几件事:

  1. 在摘要val table中声明BaseEntity。这让我陷入了一个非常荒谬的类型检查混乱。 val table: Table[T]仅在我将T定义为类型时才有效,然后子类在尝试提供不同类型的表时会导致编译器错误。
  2. 编写基类以期望将表作为参数传递给每个函数。这意味着每个模型仍然必须调用它的父方法来传递table参数。
  3. 我使用TypeTags攻击了this SO post。但是,海报并没有为我提供足够的信息来理解他的实施情况。
  4. 前面提到的SO帖子有一个评论,其中建议使用org.squeryl.Schema.findTablesFor方法。再次提出潜在的新手问题:我没有在如何实现这一点作为答案方面取得进展。我尝试过这样的事情:

    class BaseEntity {
      val table = findTablesFor(this)
    }

  5. 然而,我回到了一个Iterable,我有点不确定该如何处理它。

    因此。有没有“正确”的方法来做到这一点?当然有一种干净的方法可以将CRUD操作转移到基类中 - 我似乎无法弄明白。

    修改

    所以,这就是我所拥有的,使用Squeryl 9.5-6:

    // Schema
    package com.myproj.schema
    object MySchema extends Schema {
      val foo = table[Foo]("foos")
      val bar = table[Bar]("bars")
    }
    
    // BaseEntity
    package com.myproj.models
    import com.myproj.schema.MySchema
    abstract class BaseEntity extends Keyedentity[Long] {
      val id: Long = 0
      val table = MySchema.findTablesFor(this).head
    }
    
    // Class
    package com.myproj.models
    case class Foo (
      val name: String,
      val extra: Option[String]
    ) extends BaseEntity {
      def this() = this("", None)
    }
    

    像这样设置。 findTablesFor总是返回一个空的迭代器。它编译,但在运行时抛出错误,试图在空迭代器上调用head(正如你所说的那样)。处理错误不是问题;无法找到一张桌子。

    思想?

1 个答案:

答案 0 :(得分:3)

  

val表:表[T]仅在我将T定义为类型时才有效,然后子类在尝试提供不同类型的表时会导致编译器错误。

您可以使用自我类型执行此操作。我不确定我会推荐它,但它应该有效:

class BaseEntity[T] {
  self: T =>

  val table: Table[T]

}

然后您的实现将如下所示:

class MyEntity extends BaseEntity[MyEntity]

使用findTablesFor可能是更好的解决方案。没有什么可以阻止您将类映射到Squeryl模式中的多个表。你可以:

val tableA = table[MyEntity]

val tableB = table[MyEntity]

因此,为所有匹配表返回Iterable的原因。如果你知道你不打算这样做,你可以使用第一个结果:

val table = MySchema.findTableFor(this).head

请注意,如果找不到相关的表,这将抛出异常。