Doobie 只是 JDBC 的一个包装器,它提供了针对 SQL 注入的安全性。那么,您将如何查询原始 SQL 以获取您想要的数据?也许有这样的事情(只是一个例子,我没有检查过):
SELECT m."ID",
m."TITLE",
m."YEAR",
array_agg(a."NAME") as "ACTORS",
m."DIRECTOR"
FROM "MOVIES" m
JOIN "MOVIES_ACTORS" ma ON m."ID" = ma."ID_MOVIES"
JOIN "ACTORS" a ON ma."ID_ACTORS" = a."ID"
GROUP BY (m."ID",
m."TITLE",
m."YEAR",
m."DIRECTOR")
这正是我在 Doobie 中获取它的方法:
// import doobie normal utils
// import postgresql extensions for PG arrays and uuids
sql"""
|SELECT m."ID",
| m."TITLE",
| m."YEAR",
| array_agg(a."NAME") as "ACTORS",
| m."DIRECTOR"
|FROM "MOVIES" m
|JOIN "MOVIES_ACTORS" ma ON m."ID" = ma."ID_MOVIES"
|JOIN "ACTORS" a ON ma."ID_ACTORS" = a."ID"
|GROUP BY (m."ID",
| m."TITLE",
| m."YEAR",
| m."DIRECTOR")
|""".stripMargin
.query[Movies] // requires values to be fetched in the same order as in case class
.to[List]
.transact(transactor)
或者,您可以使用 3 个查询:
(for {
// fetch movies
movies <- sql"""SELECT m."ID",
| m."TITLE",
| m."YEAR",
| m."DIRECTOR"
|FROM movies
|""".stripMargin
.query[UUID, String, String, String]
.to[List]
// fetch joins by movies IDs
pairs <- NonEmptyList.fromList(movies.map(_._1)) match {
// query if there is something to join
case Some(ids) =>
(sql"""SELECT "MOVIES_ID",
| "ACTORS_ID"
|FROM "MOVIES_ACTORS"
|WHERE""".stripMargin ++
Fragments.in(fr""" "MOVIES_ID" """, ids))
.query[(UUID, Int)].to[List]
// avoid query altogether since condition would be empty
case None =>
List.empty[(UUID, Int)].pure[ConnectionIO]
}
// fetch actors by IDs
actors <- NonEmptyList.fromList(pairs.map(_._2)) match {
// query if there is something to join
case Some(ids) =>
(sql"""SELECT "ID",
| "NAME"
|FROM "ACTORS"
|WHERE""".stripMargin ++
Fragments.in(fr""" "ID" """, ids))
.query[(Int, String)].to[List]
// avoid query altogether since condition would be empty
case None =>
List.empty[(Int, String)].pure[ConnectionIO]
}
} yield {
// combine 3 results into 1
movies.map { case (movieId, title, year, director) =>
val actorIds = pairs.collect {
// get actorId if first of the pair is == movieId
case (`movieId`, actorId) => actorId
}.toSet
val movieActors = actors.collect {
// get actor name if id among actors from movie
case (id, name) if actorsIds.contains(id) => name
}
Movie(movieId, title, year, movieActors, director)
}
})
.transact(transactor)
由于它在代码中执行 JOIN ON 和 GROUP BY 的逻辑,因此更加冗长(并且可能更需要内存),但它表明您可以将多个查询组合到一个事务中。