Fetching strategies are used to optimize the SQL generated by Hibernate. Using Object Relational Mapping (ORM) tools does have a overhead for the all the benefits it provides but this can be reduced by understanding it.
Firstly there is FetchType, this can be either EAGER or LAZY. Best to explain is with example. If you have a table “owner” that has a one to many relationship with “dog”. When the collection “dog” is set to EAGER all “dogs” will be loaded when the “owner” is selected form the table. When the collection “dog” is set to LAZY the collection will only be initialized when you call and use owner.getDogs(). Not going to go more into detail about this since there is loads of info online about this and different ways to implement JPA/hibernate annotation or XML.
Fetch mode (@FetchMode annotation) has 3 options but there is a 4rd additional mode (@BatchSize annotation):
JOIN – Hibernate retrieves the associated instance or collection in the same SELECT, using an OUTER JOIN
SELECT – a second SELECT is used to retrieve the associated entity or collection. Unless you explicitly disable lazy fetching by specifying lazy=”false” (EAGER), this second select will only be executed when you access the association.
SUBSELECT – a second SELECT is used to retrieve the associated collections for all entities retrieved in a previous query or fetch. Unless you explicitly disable lazy fetching by specifying lazy=”false”, this second select will only be executed when you access the association.
BATCH – an optimization strategy for select fetching. Hibernate retrieves a batch of entity instances or collections in a single SELECT by specifying a list of primary or foreign keys.
Example Time
Fist things first I will use hibernate version 4.3.7.Final with MariaDB using JDBC driver 1.1.7. To start we create two very simple tables.
In the examples below I will repeat for each combination using a single select and a multi select. For single select I will use:
For multi select I will use:
1. FetchMode.DEFAULT and LAZY
Please note that FetchMode.JOIN is the default so I have left it out below but will be @Fetch(FetchMode.JOIN)
1.1 Single
When I now perform a single lookup you get the following SQL. (enitityManger.find(Owner.class, 1))
When you use the getDogs() call on Owner will generate a second query:
1.2 Multiple
Multiple will be the same it will execute SQL:
Then as you call owner.getDogs() it will execute the following SQL for each first call to each owner.getDogs().
2. FetchMode.DEFAULT and EAGER
Please note that FetchMode.JOIN is the default so I have left it out below but will be @Fetch(FetchMode.JOIN)
2.1 Single
This perfroms a single query doing a LEFT OUTER JOIN with table “Dogs”. This causes duplication of “Owner” details. This could be a problem depending on how many “Dogs” a “Owner” has and how many columns the “Owner” has.
Owner.ID
Dog.ID
1
1
1
2
1
3
2.2 Multiple
This cause a single call to pull all the “Owners” then for each owner it produces a call to get all the associated “Dogs”. This is not ideal.
3. FetchMode.JOIN and LAZY/EAGER
Something to note there when you set FetchMode to JOIN it disables LAZY, sets it to EAGER.
This will produce exactly the same results as in 2 FetchMode.DEFAULT and EAGER
4. FetchMode.SELECT and LAZY/EAGER
4.1 Single:
4.2 Multiple:
For both LAZY and EAGER the following is executed:
For EAGER the following is also executed:
For LAZY each of the “Dogs” (each line above) is executed one at time as needed when the owner.getDogs() is called on each of the owners.
5. FetchMode.SELECT and LAZY/EAGER with BatchSize
5.1 Single:
This is the same as 4.1
5.2 Multiple:
Again LAZY will only execute line 2 once owner.getDogs() is called on any of the owners. As you can see here it now instead of calling each of the owners dogs individually it does it in a batch.