Spring Data JPA OneToMany分页FetchType子选择

时间:2016-07-07 18:08:09

标签: java spring hibernate jpa spring-data

使用Spring Data JPA(目前为4.0.8)和Hibernate(目前为4.2.16)我遇到了一个问题,即分页到JPA OneToMany列表的子选择点击。在内部列表中,子选择选择要与主要元素组合的所有元素,而不仅仅是给定页面的元素。

例如:

主要JPA实体:

@Entity
@NamedQuery(name="Message.findAll", query="SELECT m FROM Message m")
public class Message implements Serializable {
...
//uni-directional many-to-one association to Message_Review
@OneToMany(cascade={CascadeType.ALL}, fetch=FetchType.EAGER)
@JoinColumn(name="message_id", referencedColumnName="message_id", insertable=false, updatable=false)
@Fetch(FetchMode.SUBSELECT) 
private Set<Message_Review> messageReviews;

生成用于获取JPA实体主列表的SQL是这样的:

SELECT TOP ? MESSAGE0_.MESSAGE_ID AS MESSAGE_1_0_, MESSAGE0_....... AND NOT (EXISTS (SELECT MESSAGEREV1_.MESSAGE_ID, MESSAGEREV1_.......))

在JPA实体的基本列表之后,Spring Data JPA(或Hibernate)然后查询OneToMany列表(根据@ FetchMode.SUBSELECT)。它生成SQL:

SELECT messagerev0_.message_id as message_2_0_1_, 
.....
where messagetop0_.message_id in 
(select message0_.message_id 
from .......
)

请注意,OneToMany匹配的内部选择没有与之关联的TOP。因此,它正在撤回所有消息评论(而不仅仅是JPA需要匹配它们的相关评论)。通常这不是一个大问题,但是在OneToMany列表中有大量数据集时,有效负载可能会从数据库中返回数百万个项目,当它只需要给定页面的一个非常小的子集时。

当使用@FetchMode.SubSelect来限制SQ​​L命中数时,有关如何让Paging在JPA实体中进行OneToMany列表的任何想法吗?

1 个答案:

答案 0 :(得分:0)

当您进行Subselect提取时,这是非常正常的行为。这就是它应该是的样子。如果您想要更多可滚动的行为,可以使用@BatchSize(size = 5)注释,其中大小告诉您一次将获取多少个集合。

所以我们假设你有类似的东西 - &gt;项目关系,你做:

从类别

中选择*

然后你开始迭代类别。 当您访问项目时,它将返回5个相关集合。不仅适用于您浏览过的类别,也适用于下一个类别。

查询fir项看起来像:

从(1,2,3,4,5)

中的categoryID中选择*

在我看来,你需要的是@BatchSize和FetchType.Select。

我的解释不是很好,所以我也给你发了一个链接。请注意,Subselect很早就使用过了。http://www.mkyong.com/hibernate/hibernate-fetching-strategies-examples/

BatchSize与Subselect的优缺点:

批量大小越大,内存消耗越大,所需的CPU就越多。我认为保持批量小是有利于资源有限的设备。如果您打开Hibernate教程,您将看到性能调整章节中描述了@BatchSize。与Subselect策略相同,提示子选择对于大型数据集可能非常重。在这方面,使用BatchSize是关于N + 1选择问题的一个很好的折衷方案,同时保持低内存消耗。