4

我知道我的 Scala Slick 数据访问层应该是什么样子,但我不确定它是否真的可能。

假设我有一个 User 表,其中包含 id、email、密码等常用字段。

  object Users extends Table[(String, String, Option[String], Boolean)]("User") {
    def id = column[String]("id", O.PrimaryKey)
    def email = column[String]("email")
    def password = column[String]("password")
    def active = column[Boolean]("active")
    def * = id ~ email ~ password.? ~ active
  }

而且我希望以不同的方式查询它们,目前丑陋的方式是创建一个新的数据库会话,执行 for 理解,然后执行不同的 if 语句来实现我想要的。

例如

  def getUser(email: String, password: String): Option[User] = {
    database withSession { implicit session: Session =>
      val queryUser = (for {
        user <- Users
          if user.email === email &&
             user.password === password &&
             user.active === true
      } //yield and map to user class, etc...
  }

  def getUser(identifier: String): Option[User] = {
    database withSession { implicit session: Session =>
      val queryUser = (for {
        user <- Users
        if user.id === identifier &&
           user.active === true
      } //yield and map to user class, etc...
  }

我希望有一个用于查询的私有方法,然后是定义查询的公共方法

type UserQuery = User => Boolean

private def getUserByQuery(whereQuery: UserQuery): Option[User] = {
  database withSession { implicit session: Session =>
      val queryUser = (for {
        user <- Users
          somehow run whereQuery here to filter
      } // yield and boring stuff
  }

def getUserByEmailAndPassword(email, pass){ ... define by query and call getUserByQuery ...}

getUserById(id){….}

getUserByFoo{….} 

这样,查询逻辑被封装在相关的公共函数中,而实际查询和映射到用户对象是在一个其他人不需要关心的可重用函数中。

我遇到的问题是试图将“where”位重构为我可以传递的函数。尝试在 intellij 中选择它们并使用重构会导致一些非常疯狂的打字。

有没有人有任何例子可以证明他们正在接近我想要实现的目标?

4

2 回答 2

6

1) 在 def 中包装查询意味着查询语句在每个请求上重新生成,并且由于查询参数未绑定,因此没有准备好的语句传递给底层 DBMS。

2)你没有利用作文

相反,如果您定义 def 查询包装器调用的参数化查询 val,您可以获得两全其美的效果。

val uBase = for{
  u  <- Users
  ur <- UserRoles if u.id is ur.UserID
} yield (u,ur)

// composition: generates prepared statement one time, on startup
val byRole = for{ roleGroup <- Parameters[String]
  (u,ur) <- uBase
  r <- Roles if(r.roleGroup is roleGroup) && (r.id is ur.roleID)
} yield u

def findByRole(roleGroup: RoleGroup): List[User] = {
  db withSession { implicit ss:SS=>
    byRole(roleGroup.toString).list
  }
}

如果您需要单一属性的一次性查找器,请使用:

val byBar = Foo.createFinderBy(_.bar)
val byBaz = Foo.createFinderBy(_.baz)

不记得在哪里,可能在 SO 或 Slick 用户组上,但我确实看到了一个非常有创意的解决方案,它允许多个绑定参数,基本上是createFinderBy类固醇。不过对我来说不是那么有用,因为解决方案仅限于单个映射器/表对象。

无论如何,为理解而作曲似乎在做你想做的事情。

于 2013-01-24T13:59:11.277 回答
0

我最近做了类似的事情,一种方法可以是遵循,编写一个通用的选择方法,它需要一个谓词

def select(where: Users.type => Column[Boolean]): Option[User] = {
    database withSession { implicit session: Session =>
      val queryUser = (for {
        user <- Users where(user)

      } //yield and map to user class, etc...
  }

and then write the method which passes the actual predicate as a higher order function

def getUserByEmail(email:String):Option[User]={
      select((u: Users.type) => u.*._2 === email)
}
similarly 

def getActiveUserByEmail(email:String):Option[User]={
     select((u: Users.type) => u.*._2 === email && u.*._4 === true)
}
于 2013-06-03T08:15:03.383 回答