灵活查询优化
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)
}
现在,我有以下问题:
您将如何处理不为给定查询 return 计算值的查询?例如,如果 table 中不存在登录信息?我做的对吗?或者,还有更好的方法?
我原以为 slick 会在连接中自动满足 Option
值,但似乎不会。为什么 Option
值没有提升?
上面会不会被翻译成两个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(你通常永远不可能)。
这个查询只会访问数据库一次,并且可能会导致一个连接查询(您需要查看创建的语句,以确保)。
我有以下 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)
}
现在,我有以下问题:
您将如何处理不为给定查询 return 计算值的查询?例如,如果 table 中不存在登录信息?我做的对吗?或者,还有更好的方法?
我原以为 slick 会在连接中自动满足
Option
值,但似乎不会。为什么Option
值没有提升?上面会不会被翻译成两个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(你通常永远不可能)。
这个查询只会访问数据库一次,并且可能会导致一个连接查询(您需要查看创建的语句,以确保)。