为光滑构建通用DAO

时间:2014-02-12 13:50:51

标签: scala generics slick

我厌倦了总是做以下的事情,以便使用光滑的每个我的域实体进行数据库访问。

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]

有关此问题的任何建议吗?或许我错了,它应该以另一种方式实施。我愿意接受任何解决方案。谢谢!

2 个答案:

答案 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