I have problems with fully loading a very complex object from the database in a reasonable amount of time and with a reasonable number of queries.
My object has many built-in objects, each object has links to other objects, other objects refer to another, etc. (Thus, the nesting level is 6)
So, I created an example to demonstrate what I want: https://github.com/gladorange/hibernate-lazy-loading
I have a user.
The user has the @OneToMany collection of favorite oranges, apples, vines and peaches. Each vine has @OneToMany grape collection. Each fruit is another object with one string field.
I create a user with 30 favorite fruits of each type, and each vine has 10 grape varieties. So, I have 421 entities 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 should not give a large set of results (large is a result set with more than 200 entries for this example).
My ideal solution would be the following:
6 inquiries. The first query returns information about the user, and the size of the result set is 1.
The second Apple request return information for this user and the result set size is 30.
The third, fourth, and fifth queries return the same as the second (with result set size = 30), but for the vine, oranges, and peaches.
The sixth query returns Grape for ALL grapevines
It is very simple in the SQL world, but I cannot achieve this with JPA (Hibernate).
I tried the following approaches:
Use a join to retrieve, e.g. from User u join fetch u.oranges ... This is terrible. The result set is 30 * 30 * 30 * 30, and the execution time is 10 seconds. Number of queries = 3. I tried this without grapes, with grapes you get x10 the size of the result set.
Just use lazy loading. This is the best result in this example (with @Fetch = DIVISION for grapes). But in this case, I need to manually iterate over each collection of elements. Also, subselect fetch is too global a parameter, so I would like to have something that could work at the query level. A set of results and time are near ideal. 6 requests and 43 ms.
Loading with an entity graph. Same as the introduction, but it also makes a request for each grape to get a vine. However, the result time is better (6 seconds), but still terrible. Number of requests> 30.
I tried to trick JPA into manually loading objects into a separate request. How:
SELECT u FROM User where id = 1;
SELECT a FROM Apple where a.user_id = 1;
This is slightly worse than lazy loading, because for each collection two requests are required: the first request to manually load objects (I have full control over this request, including loading related objects), the second request for lazy loading the same objects by Hibernate itself (this is done automatically using hibernate)
The lead time is 52, the number of requests = 10 (1 for the user, 1 for grapes, 4 * 2 for each fruit collection)
In fact, the โmanualโ solution combined with the SUBSELECT fetch allows me to use โsimpleโ extraction connections to load the necessary entities into a single request (for example, @OneToOne ). Therefore, I will use it. But I do not like that I need to fulfill two requests to load the collection.
Any suggestions?