Hibernate:复杂对象的初始化 [英] Hibernate: initialization of complex object

查看:85
本文介绍了Hibernate:复杂对象的初始化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在合理的时间内以合理的查询次数从数据库完全加载非常复杂的对象时遇到问题。

我的对象有很多嵌入式实体,每个实体都引用另一个实体,另一个实体引用另一个实体,等等(所以嵌套级别是6) / p>

因此,我创建了一个示例来演示我想要的内容:
https://github.com/gladorange/hibernate-lazy-loading



我有User。

>

用户拥有 @OneToMany 收藏的最喜欢的橙子,苹果,葡萄和桃子。每个Grapevine都有 @OneToMany 葡萄集合。每个水果是只有一个字符串字段的另一个实体。

我为每个类型的30个最喜欢的水果创建用户,每个葡萄藤有10个葡萄。所以,我完全有数据库中的421个实体--30 * 4个水果,100 * 30个葡萄和一个用户。



我想要的是:我想用no超过6个SQL查询。
并且每个查询不应该产生大的结果集(big就是一个结果集,例如有200个记录)。

我的理想解决方案是如下:


  • 6个请求。第一个请求返回有关用户和结果集大小的信息为1。第二个请求返回关于此用户的Apples的信息,结果集的大小为30.

  • / p>
  • 第三,第四和第五个请求的返回值与第二个(结果集大小= 30)相同,但对于Grapevines,Oranges和Peaches。 这在SQL世界中非常简单,但我无法通过JPA(Hibernate)实现。



    我尝试了以下方法:


    1. 使用fetch连接,如来自用户的连接fetch u.oranges ... 。这太可怕了。结果集为30 * 30 * 30 * 30,执行时间为10秒。请求数量= 3.我尝试了没有葡萄,用葡萄你会得到x10大小的结果集。

    2. 只需使用延迟加载。这是这个例子中最好的结果(@ Fetch =
      SUBSELECT for grapes)。但在这种情况下,我需要手动迭代每个元素集合。另外,子查询获取太全局设置,所以我想有一些可以在查询级别上工作的东西。结果集和时间接近理想状态。 6个查询和43毫秒。

    3. 使用实体图加载。同获取加入一样,但它也要求每一个葡萄获得葡萄。但是,结果时间会更好(6秒),但仍然很糟糕。请求数> 30。

    4. 我尝试在单独查询中以手动方式加载实体来欺骗JPA。喜欢:

       
      SELECT u FROM user其中id = 1;
      SELECT a FROM Apple where a.user_id = 1;




      延迟加载有点恶化,因为它需要两个查询每个集合:第一个查询到手动加载的实体(我完全控制这个查询,包括加载关联的实体),第二个查询延迟加载相同的实体由Hibernate本身(这是由Hibernate自动执行)



      执行时间为52,查询次数= 10(用户1,葡萄1,每个水果收集4 * 2)

      <实际上,与SUBSELECT fetch相结合的手动解决方案允许我使用简单获取连接在一个查询中加载必要的实体(如 @OneToOne entities)打算使用它。但我不喜欢我必须执行两个查询才能加载集合。


      有什么建议吗?

      解决方案

      我将建议另一个选项,以便如何在Grapevine中延迟获取Grapes集合:

        @OneToMany 
      @BatchSize(size = 30)
      private List< Grape> grapes = new ArrayList<>();

      不是做一个子选择,而是使用 ?等)可以一次获取许多 Grape 的集合。相反,葡萄藤ID将被传递。这与查询1 List< Grape> 集合是一回事。



      这只是另一种技巧阿森纳。

      I have problems with full loading of very complex object from DB in a reasonable time and with reasonable count of queries.

      My object has a lot of embedded entities, each entity has references to another entities, another entities references yet another and so on (So, the nesting level is 6)

      So, I've created example to demonstrate what I want: https://github.com/gladorange/hibernate-lazy-loading

      I have User.

      User has @OneToMany collections of favorite Oranges,Apples,Grapevines and Peaches. Each Grapevine has @OneToMany collection of Grapes. Each fruit is another entity with just one String field.

      I'm creating user with 30 favorite fruits of each type and each grapevine has 10 grapes. So, totally I have 421 entity in DB - 30*4 fruits, 100*30 grapes and one user.

      And what I want: I want to load them using no more than 6 SQL queries. And each query shouldn't produce big result set (big is a result set with more that 200 records for that example).

      My ideal solution will be the following:

      • 6 requests. First request returns information about user and size of result set is 1.

      • Second request return information about Apples for this user and size of result set is 30.

      • Third, Fourth and Fifth requests returns the same, as second (with result set size = 30) but for Grapevines, Oranges and Peaches.

      • Sixth request returns Grape for ALL grapevines

      This is very simple in SQL world, but I can't achieve such with JPA (Hibernate).

      I tried following approaches:

      1. Use fetch join, like from User u join fetch u.oranges .... This is awful. The result set is 30*30*30*30 and execution time is 10 seconds. Number of requests = 3. I tried it without grapes, with grapes you will get x10 size of result set.

      2. Just use lazy loading. This is the best result in this example (with @Fetch= SUBSELECT for grapes). But in that case that I need to manually iterate over each collection of elements. Also, subselect fetch is too global setting, so I would like to have something which could work on query level. Result set and time near ideal. 6 queries and 43 ms.

      3. Loading with entity graph. The same as fetch join but it also make request for every grape to get it grapevine. However, result time is better (6 seconds), but still awful. Number of requests > 30.

      4. I tried to cheat JPA with "manual" loading of entities in separate query. Like:

        SELECT u FROM User where id=1;
        SELECT a FROM Apple where a.user_id=1;
        

      This is a little bit worse that lazy loading, since it requires two queries for each collection: first query to manual loading of entities (I have full control over this query, including loading associated entities), second query to lazy-load the same entities by Hibernate itself (This is executed automatically by Hibernate)

      Execution time is 52, number of queries = 10 (1 for user, 1 for grape, 4*2 for each fruit collection)

      Actually, "manual" solution in combination with SUBSELECT fetch allows me to use "simple" fetch joins to load necessary entities in one query (like @OneToOne entities) So I'm going to use it. But I don't like that I have to perform two queries to load collection.

      Any suggestions?

      解决方案

      I'm going to suggest yet another option on how to lazily fetch collections of Grapes in Grapevine:

      @OneToMany
      @BatchSize(size = 30)
      private List<Grape> grapes = new ArrayList<>();
      

      Instead of doing a sub-select this one would use in (?, ?, etc) to fetch many collections of Grapes at once. Instead ? Grapevine IDs will be passed. This is opposed to querying 1 List<Grape> collection at a time.

      That's just yet another technique to your arsenal.

      这篇关于Hibernate:复杂对象的初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆