我在合理的时间内从数据库完全加载非常复杂的对象并使用合理的查询数量时遇到问题。
我的对象有很多嵌入实体,每个实体都引用另一个实体,另一个实体引用另一个实体,依此类推(因此,嵌套级别为 6)
因此,我创建了示例来演示我想要的内容:https://github.com/gladorange/hibernate-lazy-loading https://github.com/gladorange/hibernate-lazy-loading
我有用户。
用户有@OneToMany
最喜欢的收藏橙子、苹果、葡萄树和桃子。每个葡萄藤都有@OneToMany
葡萄的集合。每个水果都是另一个只有一个字符串字段的实体。
我正在为用户创建每种类型 30 个最喜欢的水果,每个葡萄藤有 10 颗葡萄。因此,我在数据库中总共有 421 个实体 - 30*4 个水果、100*30 个葡萄和一个用户。
我想要的是:我想使用不超过 6 个 SQL 查询来加载它们。
并且每个查询不应产生大结果集(大是指该示例中包含超过 200 条记录的结果集)。
我理想的解决方案如下:
6 个请求。第一个请求返回有关用户的信息,结果集的大小为 1。
第二个请求返回有关该用户的 Apple 的信息,结果集的大小为 30。
第三个、第四个和第五个请求返回与第二个相同的结果(结果集大小 = 30),但针对葡萄树、橙子和桃子。
第六个请求返回所有葡萄树的 Grape
这在 SQL 世界中非常简单,但我无法使用 JPA (Hibernate) 实现这一点。
我尝试了以下方法:
使用 fetch join,例如from User u join fetch u.oranges ...
。这太糟糕了。结果集为30*30*30*30,执行时间为10秒。请求数 = 3。我在没有葡萄的情况下尝试过,使用葡萄你将得到 x10 大小的结果集。
只需使用延迟加载即可。这是本例中的最佳结果(使用 @Fetch=
SUBSELECT 用于葡萄)。但在这种情况下,我需要手动迭代每个元素集合。另外,子选择获取过于全局设置,所以我希望有一些可以在查询级别工作的东西。结果集和时间接近理想。 6 个查询和 43 毫秒。
加载实体图。与 fetch join 相同,但它也向每个葡萄发出请求以获取小道消息。然而,结果时间更好(6 秒),但仍然很糟糕。请求数 > 30。
-
我试图通过在单独的查询中“手动”加载实体来欺骗 JPA。喜欢:
SELECT u FROM User where id=1;
SELECT a FROM Apple where a.user_id=1;
这比延迟加载有点糟糕,因为它需要每个集合进行两个查询:第一个查询手动加载实体(我可以完全控制此查询,包括加载关联实体),第二个查询延迟加载相同的实体由 Hibernate 本身执行(这是由 Hibernate 自动执行的)
执行时间为52,查询次数=10(用户1次,葡萄1次,每个水果集合4*2)
实际上,“手动”解决方案与 SUBSELECT 获取相结合允许我使用“简单”获取连接在一个查询中加载必要的实体(例如@OneToOne
实体)所以我要使用它。但我不喜欢必须执行两个查询来加载集合。
有什么建议么?