Evi*_*nge 7 java optimization hibernate jpa
我在合理的时间内使用DB完全加载非常复杂的对象并且查询数量合理.
我的对象有很多嵌入式实体,每个实体都引用另一个实体,另一个实体引用另一个实体等等(所以,嵌套级别为6)
所以,我已经创建了一个示例来演示我想要的内容:https: //github.com/gladorange/hibernate-lazy-loading
我有用户.
用户拥有@OneToMany
最喜欢的橘子,苹果,葡萄藤和桃子的集合.每个Grapevine都有@OneToMany
葡萄收藏.每个水果都是另一个只有一个String字段的实体.
我正在创造用户,每种类型有30种最喜欢的水果,每种葡萄都有10种葡萄.所以,我在DB中有421个实体--30*4个水果,100*30个葡萄和一个用户.
我想要的是:我想使用不超过6个SQL查询加载它们.并且每个查询不应该产生大的结果集(对于该示例,大的结果集具有超过200个记录).
我理想的解决方案如下:
6个要求.第一个请求返回有关用户的信息,结果集的大小为1.
关于该用户的苹果的第二个请求返回信息和结果集的大小是30.
第三,第四和第五个请求返回相同,第二个(结果集大小= 30),但对于Grapevines,Oranges和Peaches.
第六个请求返回所有葡萄藤的葡萄
这在SQL世界中非常简单,但我无法用JPA(Hibernate)实现这一点.
我尝试了以下方法:
使用fetch join,就像from User u join fetch u.oranges ...
.这太糟糕了.结果集为30*30*30*30,执行时间为10秒.请求数量= 3.我尝试没有葡萄,葡萄你会得到x10大小的结果集.
只需使用延迟加载.这是此示例中的最佳结果(对于葡萄,@ Fetch = SUBSELECT).但在这种情况下,我需要手动迭代每个元素集合.此外,subselect fetch太全局设置,所以我想有一些可以在查询级别工作的东西.结果集和时间接近理想.6个查询和43毫秒.
加载实体图.与获取连接相同,但它也要求每种葡萄都能获得葡萄藤.但是,结果时间更好(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 fetch的"手动"解决方案允许我使用"简单"的获取连接来在一个查询中加载必要的实体(比如@OneToOne
实体)所以我将使用它.但我不喜欢我必须执行两个查询来加载集合.
有什么建议?
我通常通过对实体和集合使用批量提取来覆盖99%的此类用例.如果您在读取它们的同一事务/会话中处理获取的实体,那么您不需要另外执行任何操作,只需导航到处理逻辑所需的关联,生成的查询将是非常最佳的.如果要将已提取的实体作为分离返回,则手动初始化关联:
User user = entityManager.find(User.class, userId);
Hibernate.initialize(user.getOranges());
Hibernate.initialize(user.getApples());
Hibernate.initialize(user.getGrapevines());
Hibernate.initialize(user.getPeaches());
user.getGrapevines().forEach(grapevine -> Hibernate.initialize(grapevine.getGrapes()));
Run Code Online (Sandbox Code Playgroud)
请注意,最后一个命令实际上不会为每个小道消息执行查询,因为初始化第一个命令时会初始化多个grapes
集合(直到指定的集合@BatchSize
).您只需迭代所有这些以确保所有都已初始化.
这种技术类似于您的手动方法,但效率更高(查询不会针对每个集合重复),并且在我看来更易读和可维护(您只需调用Hibernate.initialize
而不是手动编写Hibernate自动生成的相同查询).
我将建议关于如何在 Grapevine 中延迟获取葡萄集合的另一种选择:
@OneToMany
@BatchSize(size = 30)
private List<Grape> grapes = new ArrayList<>();
Run Code Online (Sandbox Code Playgroud)
这个方法不是进行子选择,而是一次in (?, ?, etc)
获取许多 s 集合。Grape
相反,?
Grapevine ID 将被传递。List<Grape>
这与一次查询 1 个集合相反。
这只是你的武器库中的另一种技术。
归档时间: |
|
查看次数: |
1137 次 |
最近记录: |