我们有一个项目需要延迟加载实体的集合,但在某些情况下我们需要急切加载它们。我们为我们的实体添加了一个 @NamedEntityGraph
注释。在我们的存储库方法中,我们添加了一个“javax.persistence.loadgraph”提示以急切地加载在所述注释中定义的 4 个属性。当我们调用该查询时,Hibernate 抛出 org.hibernate.loader.MultipleBagFetchException: cannot simultaneous fetch multiple bags
。
有趣的是,当我将所有这些集合重新定义为急切获取时,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> comments;
@ElementCollection
@CollectionTable(name="post_plusoners")
private List<PostRelatedPerson> plusoners;
@ElementCollection
@CollectionTable(name="post_shared_with")
private List<PostRelatedPerson> sharedWith;
}
查询方法(全部挤在一起以使其可发布):
@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);
}
关于为什么这适用于实体级别的预取,但不适用于动态实体图,有什么提示吗?
最佳答案
我敢打赌,您认为有效的急切获取实际上工作不正常。
当您渴望获取多个“包”(允许重复的无序集合)时,用于执行渴望获取(左外连接)的 sql 将为连接的关联返回多个结果,如此 SO answer 所述。 .因此,当您急切地获取多个 List
时,hibernate 不会抛出 org.hibernate.loader.MultipleBagFetchException
,但由于上述原因,它不会返回准确的结果。
但是,当您为查询提供实体图提示时,hibernate 会(正确地)提示。 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那个,
most uses of "non-indexed" List or raw Collection are erroneous and should semantically be Sets.
所以最重要的是,为了让多个急切的抓取按照你的意愿工作:
- 使用
Set
而不是List
- 使用 JPA 2 的
@OrderColumn
注释保留List
索引, - 如果一切都失败了,回退到 Hibernate 特定的提取注释(
FetchMode.SELECT
或FetchMode.SUBSELECT
)
编辑
相关:
关于java - NamedEntityGraph - JPA/Hibernate 抛出 org.hibernate.loader.MultipleBagFetchException : cannot simultaneously fetch multiple bags,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26934011/