我加入了一群自豪的人的行列,他们试图使用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类型的系统没有足够深入的理解。不管怎样,我在这里摆姿势。
我试过几件事:
- 在抽象
BaseEntity
中声明val table
。这让我陷入了一个非常荒谬的类型检查混乱。val table: Table[T]
只有在我也将T定义为类型的情况下才有效,然后子类在试图提供不同类型的表时会导致编译器错误 - 编写基类以期望将表作为参数传递给每个函数。这意味着每个模型仍然必须调用其父方法来传递
table
参数 - 我已经破解了这个SO帖子,它使用
TypeTags
。然而,海报并没有为我提供足够的信息来理解他的实施 前面提到的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
请注意,如果找不到相关的表,这将引发异常。