具有Scala Slick的高阶函数可带来DRY品质 [英] Higher order functions with Scala Slick for DRY goodness

查看:94
本文介绍了具有Scala Slick的高阶函数可带来DRY品质的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对使用Scala Slick的数据访问层的外观有所了解,但我不确定是否确实可行.

I have an idea how my data access layer with Scala Slick should look like, but I'm not sure if it's really possible.

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

Let's assume I have a User table which has the usual fields like id, email, password, etc.

  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语句来实现我想要的功能.

And I wish to query them in different ways, currently the ugly way is to have a new database session, do the for comprehension and then do different if statements to achieve what I want.

例如

  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...
  }

我更希望有一个用于查询的私有方法,然后有一个公共方法,它们按照以下内容定义查询:

What I would prefer is to have a private method for the query and then public methods which define queries along the lines of

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{….} 

这样,查询逻辑被封装在相关的公共功能中,而实际的查询和映射到用户对象是可重用的功能,而其他人则无需关注.

That way, the query logic is encapsulated in the relevant public functions and the actual querying and mapping to the user object is in a reusable function that other people dont need to be concerned with.

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

The problem I have is trying to refactor the "where" bit's into functions that I can pass around. Trying to do things like select them in intellij and using the refactoring results in some pretty crazy typing going on.

有人能展示一些与我要达到的目标接近的例子吗?

Does anyone have any examples they could show of doing close to what I am trying to achieve?

推荐答案

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

1) wrapping queries in a def means the query statement is re-generated on every single request, and, since query params are not bound, no prepared statement is passed to the underlying DBMS.

2)您没有利用构图

相反,如果您定义def查询包装器调用的参数化查询值,那么您将获得两全其美的体验.

Instead, if you define parameterized query vals that def query wrappers call, you can get the best of both worlds.

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
  }
}

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

If you need one-off finders for a single property, use:

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

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

Can't remember where, maybe on SO, or Slick user group, but I did see a very creative solution that allowed for multiple bound params, basically a createFinderBy on steroids. Not so useful to me though, as the solution was limited to a single mapper/table object.

无论如何,理解力似乎都可以满足您的要求.

At any rate composing for comprehensions seems to do what you're trying to do.

这篇关于具有Scala Slick的高阶函数可带来DRY品质的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆