如何使用单个查询选择不相关的实体
Posted
技术标签:
【中文标题】如何使用单个查询选择不相关的实体【英文标题】:How to select unrelated entities with a single query 【发布时间】:2018-12-21 18:53:24 【问题描述】:假设我们有 5 个表,它们之间没有任何关系,但它们都共享同一列。让我们将这些表命名为 ClojureConf
、KotlinConf
、ScalaConf
、GroovyConf
、JavaConf
。他们都有一个专栏UserId
。其他列的数量和数据类型各不相同。给定的用户可能参加了零个或多个会议。
任务是从给定UserId
的 5 个表中选择所有记录,将它们转换为 DTO 并以 json 格式返回。
目前,代码会执行 5 次访问数据库以从每个表中获取结果列表。
hibernate/jpa 中是否有库支持单次访问数据库?目标是提高性能。
是否可以为看起来类似于此的实体定义投影:
interface ConfAttended
List<ClojureConf> getClojureConfs();
List<KotlinConf> getKotlinConfs();
List<ScalaConf> getScalaConfs();
List<GroovyConf> getGroovyConfs();
List<JavaConf> getJavaConfs();
以及一个可以一次性选择和映射结果的存储库
interface ConfAttendedDAO extends JpaRepository<User, Long>
@Query("SELECT c, k, s, g, j FROM ClojureConf c " +
"JOIN KotlinConf k ON c.UserId = k.UserId " +
"JOIN ScalaConf s ON c.UserId = s.UserId " +
"JOIN GroovyConf g ON c.UserId = g.UserId " +
"JOIN JavaConf j ON c.UserId = j.UserId " +
"WHERE c.UserId = :userId")
ConfAttended findByUserIdForProjection(@Param("userId") long userId);
?
【问题讨论】:
如果您想从 5 个表中获取 5 条单条记录,并将它们全部检索为一行 - 您需要做的就是使用 JOIN 或 CROSS APPLY ,或 LEFT join 或 OUTER APPLY。你可以在选择的条件下加入 - 你的问题似乎有这个 我想调查可能的选项。对于给定的UserId
,一个表中可能有 0 条记录,而另一个表中可能有 10 条记录。是否可以编写一个 SELECT 来连接所有表中的所有列并用空值或重复值填充缺失?
JPQL 不允许对不相关的实体使用“JOIN”语法。
有没有办法为不相关的实体发出批量SELECT?
【参考方案1】:
我最终得到了这样的查询:
interface ConfAttendedDAO extends JpaRepository<User, Long>
@Query("SELECT c, k, s, g, j FROM User u " +
"LEFT JOIN ClojureConf c ON c.UserId = :userId " +
"LEFT JOIN KotlinConf k ON k.UserId = :userId " +
"LEFT JOIN ScalaConf s ON s.UserId = :userId " +
"LEFT JOIN GroovyConf g ON g.UserId = :userId " +
"LEFT JOIN JavaConf j ON j.UserId = :userId " +
"WHERE u.Id = :userId")
List<Object[]> findAllByUserId(@Param("userId") long userId);
Hibernate 负责将行映射到实体。每个Object[]
都有所有 5 个实体(或空值)作为其元素。从User
中选择是强制查询返回结果。否则,如果第一个表不返回任何内容 - 整个查询不返回任何内容。另一个缺点是,如果一个表有 10 个结果而另一个有 1 个结果,则结果较少的表会使它们重复。
至于性能(做这一切的唯一目的),获取和处理结果比使用 5 个单独的SELECT
s 快 4-5 倍。
【讨论】:
你使用的是哪个版本的休眠?以上是关于如何使用单个查询选择不相关的实体的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 JPA Criteria API 连接不相关的实体