NamedEntityGraph - JPA / Hibernate抛出org.hibernate.loader.MultipleBagFetchException:不能同时获取多个包 [英] NamedEntityGraph - JPA / Hibernate throwing org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags

查看:684
本文介绍了NamedEntityGraph - JPA / Hibernate抛出org.hibernate.loader.MultipleBagFetchException:不能同时获取多个包的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们有一个项目,我们需要懒惰地加载一个实体的集合,但在某些情况下,我们需要它们急切地加载。我们已经为我们的实体添加了一个 @NamedEntityGraph 注释。在我们的仓库方法中,我们添加一个javax.persistence.loadgraph提示,以便加载所述注释中定义的4个属性。当我们调用这个查询时,Hibernate会抛出 org.hibernate.loader.MultipleBagFetchException:不能同时获取多个包。



有趣的是,当我将所有这些集合重新定义为渴望获取时,Hibernate 没有立即获取它们而没有MultipleBagFetchException。



这里是蒸馏代码。
实体:

  @Entity 
@NamedEntityGraph(name =Post.Full,attributeNodes = {
@NamedAttributeNode(comments),
@NamedAttributeNode(plusoners),
@NamedAttributeNode(sharedWith)
}

public class Post {
@OneToMany(cascade = CascadeType.ALL,mappedBy =postId)
private List< Comment>注释;

@ElementCollection
@CollectionTable(name =post_plusoners)
private List< PostRelatedPerson> plusoners;

@ElementCollection
@CollectionTable(name =post_shared_with)
private List< PostRelatedPerson>和...分享;




$ p
$ b

查询方法(全部使它变得可能):

  @Override 
公共页面< Post> findFullPosts(规范< Post> spec,Pageable pageable){
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery< Post> query = builder.createQuery(Post.class);
Root< Post> post = query.from(Post.class);
谓词postsPredicate = spec.toPredicate(post,query,builder);
query.where(postsPredicate);

EntityGraph<?> entityGraph = entityManager.createEntityGraph(PlusPost.Full);

TypedQuery< GooglePlusFullPost> typedQuery = entityManager.createQuery(query);
typedQuery.setHint(javax.persistence.loadgraph,entityGraph);

query.setFirstResult(pageable.getOffset());
query.setMaxResults(pageable.getPageSize());

Long total = QueryUtils.executeCountQuery(getPostCountQuery(specification));

列表< P> resultList = total> pageable.getOffset()? query.getResultList():集合。< P> emptyList();
返回新的PageImpl< P>(resultList,pageable,total);





$ b

为什么这个函数需要在实体级别上进行提取,而不是与动态实体图吗?

解决方案

我打赌你认为工作的渴望提取,实际上工作不正确。

当您渴望获取多个包(一个允许重复的非订购集合)时,用于执行渴望获取的SQL(左外连接)将返回多个结果对于这个 SO答案所解释的加入的关联。所以,当你有多个 List 时,hibernate不会抛出 org.hibernate.loader.MultipleBagFetchException 由于上述原因,不会返回准确的结果。

然而,当你给查询实体图提示时,hibernate会(正确)抱怨。 Hibernate开发人员Emmanuel Bernard解决了引发此异常的原因


预先获取本身不是问题,在一个SQL查询中使用多个连接。它不仅限于静态获取策略;它从来没有被支持(财产),因为它在概念上是不可能的。


Emmanuel接着在另一个JIRA评论中表示,


非索引列表或原始集合的大多数用法都是错误的,应该在语义上是集合。


,以获得多次提前取用,以满足您的需求:


  • 使用 Set $ c>而不是 List

  • 使用JPA保持 List 2的 @OrderColumn 注释,
  • 如果一切都失败,则回退到Hibernate特定的提取注释( FetchMode.SELECT FetchMode.SUBSELECT



  • 编辑



    相关:


    We have a project where we need to lazily load collections of an entity, but in some cases we need them loaded eagerly. We have added a @NamedEntityGraph annotation to our entity. In our repository methods we add a "javax.persistence.loadgraph" hint to eagerly load 4 of attributes defined in said annotation. When we invoke that query, Hibernate throws org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags.

    Funnily, when I redefine all of those collection as eagerly fetched Hibernate does fetch them eagerly with no MultipleBagFetchException.

    Here is the distilled code. Entity:

    @Entity
    @NamedEntityGraph(name = "Post.Full", attributeNodes = {
            @NamedAttributeNode("comments"),
            @NamedAttributeNode("plusoners"),
            @NamedAttributeNode("sharedWith")
        }
    )
    public class Post {
        @OneToMany(cascade = CascadeType.ALL, mappedBy = "postId")
        private List<Comment> comments;
    
        @ElementCollection
        @CollectionTable(name="post_plusoners")
        private List<PostRelatedPerson> plusoners;
    
        @ElementCollection
        @CollectionTable(name="post_shared_with")
        private List<PostRelatedPerson> sharedWith;
    
    }
    

    Query method (all cramped together to make it postable):

    @Override
    public Page<Post> findFullPosts(Specification<Post> spec, Pageable pageable) {
        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
        CriteriaQuery<Post> query = builder.createQuery(Post.class);
        Root<Post> post = query.from(Post.class);
        Predicate postsPredicate = spec.toPredicate(post, query, builder);
        query.where(postsPredicate);
    
        EntityGraph<?> entityGraph = entityManager.createEntityGraph("PlusPost.Full");
    
        TypedQuery<GooglePlusFullPost> typedQuery = entityManager.createQuery(query);
        typedQuery.setHint("javax.persistence.loadgraph", entityGraph);
    
        query.setFirstResult(pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());
    
        Long total = QueryUtils.executeCountQuery(getPostCountQuery(specification));
    
        List<P> resultList = total > pageable.getOffset() ? query.getResultList() : Collections.<P>emptyList();
        return new PageImpl<P>(resultList, pageable, total);
    }
    

    Any hints on why is this working with eager fetches on entity level, but not with dynamic entity graphs?

    解决方案

    I'm betting the eager fetches you think were working, were actually working incorrectly.

    When you eager fetch more than one "bag" (an unorder collection allowing duplicates), the sql used to perform the eager fetch (left outer join) will return multiple results for the joined associations as explained by this SO answer. So while hibernate does not throw the org.hibernate.loader.MultipleBagFetchException when you have more than one List eagerly fetched it would not return accurate results for the reason given above.

    However, when you give the query the entity graph hint, hibernate will (rightly) complain. Hibernate developer, Emmanuel Bernard, addresses the reasons for this exception to be thrown:

    eager fetching is not the problem per se, using multiple joins in one SQL query is. It's not limited to the static fetching strategy; it has never been supported (property), because it's conceptually not possible.

    Emmanuel goes on to say in a different JIRA comment that,

    most uses of "non-indexed" List or raw Collection are erroneous and should semantically be Sets.

    So bottom line, in order to get the multiple eager fetching to work as you desire:

    • use a Set rather than a List
    • persist the List index using JPA 2's @OrderColumn annotation,
    • if all else fails, fallback to Hibernate specific fetch annotations (FetchMode.SELECT or FetchMode.SUBSELECT)

    EDIT

    related:

    这篇关于NamedEntityGraph - JPA / Hibernate抛出org.hibernate.loader.MultipleBagFetchException:不能同时获取多个包的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆