在合理的时间内从数据库中完全加载非常复杂的对象以及合理的查询数量时,我遇到了问题。
我的对象有很多嵌入式实体,每个实体都引用了另一个实体,另一个实体又引用了另一个,依此类推(因此,嵌套级别为6)
因此,我创建了示例来演示我想要的内容:https : //github.com/gladorange/hibernate-lazy- loading
我有用户。
用户有@OneToMany喜欢的 橙子,苹果,葡萄干和桃子的 集合。每个葡萄藤都有@OneToMany葡萄的集合。每个水果都是只有一个String字段的另一个实体。
@OneToMany
我正在为用户创建每种类型的30种最喜欢的水果,每个葡萄藤有10个葡萄。因此,在数据库中总共有421个实体-30 * 4水果,100 * 30葡萄和一个用户。
我想要的是:我想使用不超过6个SQL查询来加载它们。而且每个查询都不应产生较大的结果集(对于该示例,big是一个具有200条以上记录的结果集)。
我理想的解决方案是:
6个要求。第一个请求返回有关用户的信息,结果集的大小为1。
该用户的第二个请求返回有关Apple的信息,结果集的大小为30。
第三,第四和第五个请求返回相同的结果,第二个(结果集大小为30),但对于葡萄,橙子和桃子。
第六个请求为所有葡萄返回葡萄
这在SQL世界中非常简单,但是我无法使用JPA(hibernate)来实现。
我尝试了以下方法:
使用获取联接,例如from User u join fetch u.oranges ...。太厉害了 结果集为30 * 30 * 30 * 30,执行时间为10秒。请求数=3。我在没有葡萄的情况下进行了尝试,有了葡萄,您将获得x10大小的结果集。
from User u join fetch u.oranges ...
只需使用延迟加载即可。在此示例中,这是最好的结果(对于葡萄,使用@ Fetch = SUBSELECT)。但是在那种情况下,我需要手动遍历每个元素集合。另外,subselect fetch也是全局设置,因此我希望有一些可以在查询级别使用的东西。结果集和时间接近理想。6个查询和43毫秒。
使用实体图加载。与获取联接相同,但是它也要求每个葡萄都获得它。但是,结果时间更好(6秒),但仍然很糟糕。请求数> 30。
我试图通过在单独的查询中“手动”加载实体来欺骗JPA。喜欢:
在id = 1的用户中选择u;
从a.user_id = 1;的Apple中选择一个;
这比延迟加载要差一些,因为每个集合都需要两个查询:第一个查询到手动加载实体(我对这个查询有完全控制权,包括加载关联的实体),第二个查询来延迟加载相同的实体由Hibernate本身(由Hibernate自动执行)
执行时间为52,查询数量= 10(用户1,葡萄1,每个水果收集4 * 2)
实际上,“手动”解决方案与SUBSELECT fetch结合使用使我可以使用“简单” fetch联接在一个查询中加载必要的实体(例如@OneToOne实体),因此我将使用它。但是我不喜欢我必须执行两个查询才能加载集合。
@OneToOne
有什么建议么?
我将建议如何懒惰地获取葡萄中葡萄的另一种选择:
@OneToMany @BatchSize(size = 30) private List<Grape> grapes = new ArrayList<>();
与其进行子选择in (?, ?, etc),不如使用一次子选择来立即获取许多的集合Grape。取而代之的是,?葡萄藤ID将被传递。这与一次查询1个List<Grape>集合相反。
in (?, ?, etc)
Grape
?
List<Grape>
那只是您的武器库的另一种技术。