灵活查询优化

Slick Query Optimization

我有以下 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

  }

其中查询一个用户 table 和一个 login_info table。用户 table 在 login_info table.

中有一个外键

目前,findByProviderIdAndProviderKey 方法 return 是 Future[LoginInfo]。但是,对于不存在的用户 ID,我想将其更改为 return a 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. 您将如何处理不为给定查询 return 计算值的查询?例如,如果 table 中不存在登录信息?我做的对吗?或者,还有更好的方法?

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

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

提前致谢!

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

根据您的问题 loginInfoRepository.findByProviderIdAndProviderKey returns a 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 没有 return 任何东西,第二行将永远不会被执行(for 理解的性质)并且 slick 将 return 什么都没有。 如果第一行 return 是什么,但是给定的 loginId 没有 User,slick 也将 return 什么都没有。 只有当两行都 return 时,slick 才会实际 return 值。

到 return 您使用结果集中的一个选项 userQuery.result.headOption。你不应该使用 userQuery.result.head 除非你 100% 确定某些东西会被 returned(你通常永远不可能)。

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