Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/325.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何在一个JPQL查询中使用多个联接获取_Java_Hibernate_Jpa_Join_Jpql - Fatal编程技术网

Java 如何在一个JPQL查询中使用多个联接获取

Java 如何在一个JPQL查询中使用多个联接获取,java,hibernate,jpa,join,jpql,Java,Hibernate,Jpa,Join,Jpql,我有以下实体: 公共类类别{ 私有整数id; @onetomanaymappedby=父对象 私人列表主题; } 公开课主题{ 私有整数id; @onetomanaymappedby=父对象 私人名单员额; @许多酮 @JoinColumnname=id 私人类别家长; } 公营职位{ 私有整数id; @许多酮 @JoinColumnname=id 私人话题家长; /*邮政领域*/ } 我想使用JPQL查询获取所有具有连接主题和连接帖子的类别。我写了如下查询: SELECT c FROM Cat

我有以下实体:

公共类类别{ 私有整数id; @onetomanaymappedby=父对象 私人列表主题; } 公开课主题{ 私有整数id; @onetomanaymappedby=父对象 私人名单员额; @许多酮 @JoinColumnname=id 私人类别家长; } 公营职位{ 私有整数id; @许多酮 @JoinColumnname=id 私人话题家长; /*邮政领域*/ } 我想使用JPQL查询获取所有具有连接主题和连接帖子的类别。我写了如下查询:

SELECT c FROM Category c
JOIN FETCH c.topics t
JOIN FETCH t.posts p WHERE 
但是我弄错了

org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
我找到了关于这个错误的文章,但这些文章只描述了在一个实体中有两个集合要连接的情况。我的问题有点不同,我不知道如何解决它


可以在一个查询中完成此操作?

考虑到我们有以下实体:

并且,您希望获取一些父Post实体以及所有关联的注释和标记集合

如果正在使用多个JOIN FETCH指令:

List<Post> posts = entityManager.createQuery("""
    select p
    from Post p
    left join fetch p.comments
    left join fetch p.tags
    where p.id between :minId and :maxId
    """, Post.class)
.setParameter("minId", 1L)
.setParameter("maxId", 50L)
.getResultList();
Hibernate抛出此异常的原因是它不允许获取多个包,因为这样会生成笛卡尔积

其他人可能会向你推销最糟糕的解决方案 现在,你会发现很多答案、博客帖子、视频或其他资源告诉你使用集合而不是列表来收集

这是个糟糕的建议。不要那样做

使用集合而不是列表将消除MultipleBagFetchException,但笛卡尔积仍然存在,这实际上更糟糕,因为在应用此修复程序很久之后,您就会发现性能问题

正确的解决方案 您可以执行以下操作:

List<Post> posts = entityManager.createQuery("""
    select distinct p
    from Post p
    left join fetch p.comments
    where p.id between :minId and :maxId
    """, Post.class)
.setParameter("minId", 1L)
.setParameter("maxId", 50L)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();

posts = entityManager.createQuery("""
    select distinct p
    from Post p
    left join fetch p.tags t
    where p in :posts
    """, Post.class)
.setParameter("posts", posts)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
在第一个JPQL查询中,distinct不会转到SQL语句。这就是为什么我们将PASS_DISTINCT_THROUGH JPA查询提示设置为false

DISTINCT在JPQL中有两种含义,在这里,我们需要它在Java端(而不是SQL端)重复删除getResultList返回的Java对象引用

只要您使用JOIN fetch获取最多一个集合,就可以了

通过使用多个查询,您将避免使用笛卡尔乘积,因为除了第一个集合之外,任何其他集合都是使用辅助查询获取的

始终避免使用FetchType.EAGER策略 如果在映射时为@OneToMany或@ManyToMany关联使用FetchType.EAGER策略,则很容易导致MultipleBagfetch异常

您最好从FetchType.EAGER切换到FetchType.LAZY,因为急切抓取是一个可怕的想法,可能会导致严重的应用程序性能问题

结论 避免FetchType.EAGER,不要从List切换到Set,因为这样做会使Hibernate将MultipleBagFetchException隐藏在地毯下。一次只取一件收藏品,你就没事了


只要在初始化集合时使用相同数量的查询,就可以了。不要在循环中初始化集合,因为这会触发查询问题,这也会影响性能。

下面是一个复杂连接和多重考虑的工作示例:

    String query_findByProductDepartmentHospital = "select location from ProductInstallLocation location "
            + " join location.product prod " + " join location.department dep "
            + " join location.department.hospital hos " + " where  prod.name = :product "
            + " and dep.name.name = :department " + " and hos.name = :hospital ";

    @Query(query_findByProductDepartmentHospital)
    ProductInstallLocation findByProductDepartmentHospital(@Param("product") String productName,@Param("department") String departName, @Param("hospital") String hospitalName);

一种解决方法是同时使用@Query和@EntityGraph,就像本文末尾提到的那样:尽管使用索引列表或集合总是更好。如何创建索引列表?此查询给出以下错误,但仍在运行:无法使用标识变量定义联接获取表达式。只有一个问题,您的查询返回一个post,没有任何类别。@Vladmichalcea有点不相关-但是,当您加入获取所有组合成员时,使用“加入获取”与“渴望”有什么好处?这没有获取,因此不会初始化加入的集合。此响应与此上下文无关。发生错误的原因是fetch子句。
    String query_findByProductDepartmentHospital = "select location from ProductInstallLocation location "
            + " join location.product prod " + " join location.department dep "
            + " join location.department.hospital hos " + " where  prod.name = :product "
            + " and dep.name.name = :department " + " and hos.name = :hospital ";

    @Query(query_findByProductDepartmentHospital)
    ProductInstallLocation findByProductDepartmentHospital(@Param("product") String productName,@Param("department") String departName, @Param("hospital") String hospitalName);