使用Squeryl和基类抽象CRUD操作



我加入了一群自豪的人的行列,他们试图使用Squeryl作为我的网络应用程序ORM框架。(郑重声明,我使用Scalatra作为实际的web框架,但我不认为这是Scalatra的问题。)这意味着我已经加入了试图构建一个有效抽象层来DRY我们常见操作的行列。例如,很常见这样的例子:

// 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. 在抽象BaseEntity中声明val table。这让我陷入了一个非常荒谬的类型检查混乱。val table: Table[T]只有在我也将T定义为类型的情况下才有效,然后子类在试图提供不同类型的表时会导致编译器错误
  2. 编写基类以期望将表作为参数传递给每个函数。这意味着每个模型仍然必须调用其父方法来传递table参数
  3. 我已经破解了这个SO帖子,它使用TypeTags。然而,海报并没有为我提供足够的信息来理解他的实施
  4. 前面提到的SO帖子有一条评论,其中建议使用org.queryl.Schema.findTablesFor方法。再次谈到潜在的新手问题:我还没有在如何实现这一答案方面取得进展。我试过这样的东西:

    class BaseEntity{
    val table=findTables(this)

但是,我得到了一个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时会抛出错误(正如您所说的那样)。处理错误不是问题;找不到桌子有点像。

想法?

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

您可以使用self-type来完成此操作。我不确定我会推荐它,但它应该有效:

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

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

最新更新