【问题标题】:Slick Query Optimization灵活的查询优化
【发布时间】:2016-07-03 17:25:17
【问题描述】:

我有以下 Scala/Slick/PlayFramework 代码:

class UserRepository @Inject()(dbConfigProvider: DatabaseConfigProvider, loginInfoRepository: LoginInfoRepository) extends BaseRepository[UserTable, User](TableQuery[UserTable], dbConfigProvider.get[JdbcProfile].db) with UserQuery {

  val db = dbConfigProvider.get[JdbcProfile].db
  def findUserByProviderIdAndKey(providerId: String, providerKey: String): Future[User] = {
    for {
      maybeLoginInfo <- loginInfoRepository.findByProviderIdAndProviderKey(providerId, providerKey)
      maybeUser <- db.run(query.filter(_.loginId === maybeLoginInfo.loginId).result.head) //query refers to the user table with a loginId column
    } yield maybeUser

  }

查询用户表和登录信息表。用户表在 login_info 表中有一个外键。

目前,findByProviderIdAndProviderKey 方法返回一个Future[LoginInfo]。但是,我想将其更改为为不存在的用户 ID 返回 Future[Option[LoginInfo]]。它可以在findByProviderIdAndProviderKey 方法中轻松更改,但我不确定如何更改理解以处理选项值。

我想我可以像下面这样(需要重构):

    class UserRepository @Inject()(dbConfigProvider: DatabaseConfigProvider, loginInfoRepository: LoginInfoRepository) extends BaseRepository[UserTable, User](TableQuery[UserTable], dbConfigProvider.get[JdbcProfile].db) with UserQuery {

      val db = dbConfigProvider.get[JdbcProfile].db
      def findUserByProviderIdAndKey(providerId: String, providerKey: String): Future[User] = {
       loginInfoRepository.findByProviderIdAndProviderKey(providerId, providerKey).map{
       case (Some(x)) => db.run(query.fitler(_.loginId == x.loginInd).result.headOption)
       case None=> Option(None)
}

现在,我有以下问题:

  1. 您将如何处理不返回给定查询值的查询?例如,如果登录信息不存在于表中?我做对了吗?或者,还有更好的方法?

  2. 我原以为 slick 会在连接中自动满足 Option 值,但似乎没有。为什么没有提升Option 值?

  3. 以上内容会被翻译成两个 SQL 查询和数据库调用吗?如果是,我该如何重新构建它,使其只发出一个正确连接的数据库调用?

提前致谢!

【问题讨论】:

  • 既然您的问题是关于您的findByProviderIdAndProviderKey 方法,您能否也发布此代码?

标签: scala slick slick-3.0


【解决方案1】:

由于您只发布了部分代码,我将尝试根据您的问题构建一个示例。

根据您的问题loginInfoRepository.findByProviderIdAndProviderKey 返回一个Future[LoginInfo],这意味着您已经在该方法中调用了db.run。所以回答第 3 点:是的,因为您调用 db.run 两次,它会导致两次往返您的数据库。

Slicks 查询是可组合的,所以您可能想要做的是,在 findUserByProviderIdAndKey 中使用 findByProviderIdAndProviderKey 的查询而不是结果:

def findUserByProviderIdAndKey(providerId: String, providerKey: String): Future[Option[User]] = {
    val userQuery = for {
      loginInfo <- loginInfoQuery if loginInfo.providerId === providerId && loginInfo.providerKey === providerKey
      user <- query if user.loginId === loginInfo.loginId
    } yield user

    db.run( userQuery.result.headOption )
}

for comprehension 中的第一行使用给定参数查询所有 LoginInfos(我猜到了您的查询和字段名称)。 第二行采用LoginInfos 并使用给定的loginId 查询Users

现在,如果 loginInfoQuery 没有返回任何内容,则第二行将永远不会被执行(for 理解的性质)并且 slick 将不会返回任何内容。 如果第一行返回了一些东西,但是给定的User 没有loginId,那么 slick 也不会返回任何东西。 只有当两行都返回一些东西时,slick 才会真正返回值。

要从结果集中返回一个选项,请使用userQuery.result.headOption。你永远不应该使用userQuery.result.head,除非你 100% 确定会返回一些东西(你通常永远不可能)。

此查询只会访问数据库一次,并且可能会导致连接查询(您需要查看创建的语句,以确保)。

【讨论】:

  • 谢谢!我自己尝试了一些事情后得出了相同的解决方案..但感谢您的回答。这确实是我想要的。
  • 道歉..我赞成答案但忘记接受它..现在接受了:-)
猜你喜欢
  • 2015-08-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-29
  • 1970-01-01
  • 2015-11-28
  • 1970-01-01
  • 1970-01-01
  • 2015-09-25
相关资源
最近更新 更多