我厌倦了总是做以下的事情,以便使用光滑的每个我的域实体进行数据库访问。
database withSession {
implicit session =>
val entities = TableQuery[EntityTable]
val id = //Some ID
val q = for {
e <- entities if e.id === id
} yield (e)
val entity = q.first
}
(注意:EntityTable的定义与描述here相同)
所以我决定要一个通用的数据库访问对象来处理这个问题。用法应该类似于
[...]
val entityDAO = new GenericDAO[Entity, EntityTable, String]()
[...]
database withSession { implicit session =>
val id = // Some ID
val entity = entityDAO.get(id)
}
我尝试实现GenericDAO看起来像这样
class GenericDAO[T, TB, PK](implicit session: Session) {
val entities = TableQuery[TB] // Line 1
def get(id: PK): T = {
val q = for {
e <- entities
} yield (e)
val res: T = q.first
res
}
}
但是第1行给我留下了一个编译错误,指出TB参数有问题。
此行有多个标记 - 类型参数[TB]符合任何值的重载替代值的界限:[E&lt;: scala.slick.lifted.AbstractTable []] =&GT; scala.slick.lifted.TableQuery [E,E#TableElementType] [E&lt;: scala.slick.lifted.AbstractTable []](缺点:scala.slick.lifted.Tag =&gt; E)scala.slick.lifted.TableQuery [E,E#TableElementType] - 重载方法值的错误数量的类型参数适用于替代方案:[E&lt ;:scala.slick.lifted.AbstractTable []] =&gt; scala.slick.lifted.TableQuery [E,E#TableElementType] [E&lt;: scala.slick.lifted.AbstractTable []](缺点:scala.slick.lifted.Tag =&gt; E)scala.slick.lifted.TableQuery [E,E#TableElementType]
有关此问题的任何建议吗?或许我错了,它应该以另一种方式实施。我愿意接受任何解决方案。谢谢!
答案 0 :(得分:8)
首先,你可以写
val entities = TableQuery[EntityTable] // put in a central place for re-use
然后
database.withSession(
(for {
e <- entities if e.id === /*Some ID*/
} yield e).first()(_)
)
或者
database.withSession(entities.filter(_.id === /*Some ID*/).first()(_))
或者
val get = entities.findBy(_.id) // <- reuse this
database.withSession(get(/*Some ID*/).first()(_))
为了简洁起见。这可能会使你的整个DAO变得不必要(这很棒:)!)。
关于您收到的错误消息。 TableQuery[TB]
是一个宏,它是TableQuery(tag => new TB(tag))
的简写,TB
必须是Table
并且支持对象创建。您不能只在从DAO包装器获得的无约束类型参数上使用TableQuery
宏。您可以约束TB <: Table[_]
但它仍然不支持对象创建,您无法在Scala中约束它。你只能为你的DAO提供一个工厂(一个常见的模式是将一个工具作为一个隐式参数获取),但是当你只需编写一次TableQuery并将其存储在一个全局可访问的地方时,这一切都没有意义。
<强>更新强>
快捷方式适用于所有这些方法。这是普通的Scala。您只需将方法转换为函数并将其传递给带有Session的高阶函数,这需要从会话到任何事物的函数。请注意,有些Slick方法有一个空的参数列表,需要()(_)
将它们转换为函数,有些只有隐式参数列表,这只需要(_)
。例如。 database.withSession(entities.filter(_.id === /*Some ID*/).delete(_))
。
如果你想知道_
。 Scala将方法与函数区分开来。 def foo(a: A, b: B, ...): R
是一种方法,但可以使用(A,B,C) => R
将其转换为foo _
类型的函数。这种转换称为eta扩展,谷歌搜索将会显示更多信息。有时当一个函数需要时,但是你提供了一个方法,Scala编译器会推断出_
,你不必明确地编写它。您还可以提供一些参数,并使用_
代替您不想应用的参数。在这种情况下,您将部分应用该方法并返回一个函数。这就是我们在这里所做的。我们在方法通常期望会话的位置使用_
并获取一个会话的函数。完全必须使用_
或(_)
或()(_)
时,方法签名和隐式参数列表之间的细节之间的相互作用,nullary方法,带有空参数列表的方法,这是一般的Scala知识值得研究。
答案 1 :(得分:3)
这对我来说是一个巨大的帮助。一个完整的工作通用dao https://gist.github.com/lshoo/9785645