使用Scala Slick实现更高阶的功能以实现DRY良好性

qui*_*qui 3 scala higher-order-functions slick

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

假设我有一个User表,其中包含id,email,password等常用字段.

  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
  }
Run Code Online (Sandbox Code Playgroud)

我希望以不同的方式查询它们,目前丑陋的方式是拥有一个新的数据库会话,进行理解,然后执行不同的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...
  }
Run Code Online (Sandbox Code Playgroud)

我更喜欢的是为查询提供一个私有方法,然后使用公共方法来定义查询

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{….} 
Run Code Online (Sandbox Code Playgroud)

这样,查询逻辑被封装在相关的公共函数中,并且对用户对象的实际查询和映射具有其他人不需要关注的可重用功能.

我遇到的问题是尝试将"where"位重构为我可以传递的函数.尝试做一些事情,比如在intellij中选择它们并使用重构导致一些非常疯狂的打字.

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

vir*_*yes 6

1)在def中包装查询意味着在每个请求上重新生成查询语句,并且由于未绑定查询参数,因此不会将预准备语句传递给基础DBMS.

2)你没有利用作文

相反,如果您定义def查询包装器调用的参数化查询值,您可以充分利用这两个世界.

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
  }
}
Run Code Online (Sandbox Code Playgroud)

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

val byBar = Foo.createFinderBy(_.bar)
val byBaz = Foo.createFinderBy(_.baz)
Run Code Online (Sandbox Code Playgroud)

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

无论如何,为理解而作出的努力似乎正在做你想要做的事情.