Hibernate Fetch types


Let us understand the different modes of Fetch and When to use them


In any ORM framework, it’s very important to understand how it loads the entity especially when entity has relations and collections in it.

In Hibernate we call it as Fetch mode or fetching strategy.

Yes Hibernate decides how to load the entity based on the fetch mode.

Example:

If User entity has a Collection of Address entity in it.
When we load User, Should we load Addresses as well ? Or We should load only User ?

Answer to this question in Hibernate is based on Fetch mode we specify.

When we load User,at the same time if we load Address also, then its called as EAGER loading.

If we delay the loading of Addresses while loading User until we require Addresses then it’s called Lazy loading.

Fetch mode helps us in customizing the number of queries generated and amount of data retrieved.

Different Fetch modes supported by Hibernate

1) FetchMode JOIN

Eager loading which loads all the collections and relations at the same time.

2) FetchMode SELECT(default)

Lazy loading which loads the collections and relations only when required.

3) FetchMode SELECT with Batch Size

Fetch upto “N”collections or entities(“Not number of records”)

4) FetchMode SUBSELECT

Group the collection of an entity into a Sub-Select query.

Now let us understand these fetch modes in detail with below example

Customer entity has a Collection of Address entity

It is a one to many relation (one Customer will have many addresses)


User-Address-relation


Code to define the fetching strategy


We can define Fetch mode either through XML mapping or through annotation mapping

1) Let’s see XML mapping for the same

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
 
<hibernate-mapping package="com.kb.model">
    <class name="Customer" table="customers">
        <id name="id" type="int" column="Id">
            <generator class="increment" />
        </id>
        <property name="firstName">
            <column name="FirstName" />
        </property>
        <property name="lastName">
            <column name="LastName" />
        </property>
        <property name="city">
            <column name="City" />
        </property>
        
        <list name="addresses"  cascade="all" inverse="true"
            table="address" fetch="select" batch-size="10">
            <key>
                <column name="CUSTOMER_ID" not-null="true" />
            </key>
            <list-index column="idx" />
               <one-to-many class="Address" />
        </list>
    </class>
</hibernate-mapping>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.kb.model">
    <class name="Customer" table="customers">
        <id name="id" type="int" column="Id">
            <generator class="increment" />
        </id>
        <property name="firstName">
            <column name="FirstName" />
        </property>
        <property name="lastName">
            <column name="LastName" />
        </property>
        <property name="city">
            <column name="City" />
        </property>
        
        <list name="addresses"  cascade="all" inverse="true"
            table="address" fetch="select" batch-size="10">
            <key>
                <column name="CUSTOMER_ID" not-null="true" />
            </key>
            <list-index column="idx" />
               <one-to-many class="Address" />
        </list>
    </class>
</hibernate-mapping>


We can see that while defining list of addresses using < list > tag, we have specified fetch attribute.

We have also specified batch-size as 10 which is required in Batch fetch mode.

2) Now Let’s see how can we achieve the same through Annotations

1
2
3
4
5
6
7
8
9
10
11
12
13
@Entity
@Table(name = "customers", catalog = "javainsimpleway")
public class Customer implements Serializable{
...
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "customers")
    @Cascade(CascadeType.ALL)
    @Fetch(FetchMode.SELECT)
        @BatchSize(size = 10)
    public List<Address> getAddresses() {
        return this.addresses;
    }
...
}
@Entity
@Table(name = "customers", catalog = "javainsimpleway")
public class Customer implements Serializable{
...
	@OneToMany(fetch = FetchType.LAZY, mappedBy = "customers")
	@Cascade(CascadeType.ALL)
	@Fetch(FetchMode.SELECT)
        @BatchSize(size = 10)
	public List<Address> getAddresses() {
		return this.addresses;
	}
...
}


We can see that while defining One to Many relation using @OneToMany annotation, we have specified fetch attribute.

We have also specified batch-size as 10 using @BatchSize annotation which is required in Batch fetch mode.

We will use these 2 mapping files(XML and Annotation) to address each Fetch Mode as below


FetchMode JOIN


Modify above xml with fetch=”join”

(or)

Modify above annotated class with @Fetch(FetchMode.JOIN)

This is also called Eager loading which means load all the collections and relations no matter we use it or not.

This fetching strategy loads User and List of Address in a single query when we request only User.

It will load all the addresses when we load the User no matter whether we use the Address or not.

1
2
Customer  customer = session.get(Customer.class, customerId);
List<Address> addresses = customer.getAddresses();
Customer  customer = session.get(Customer.class, customerId);
List<Address> addresses = customer.getAddresses();


Query generated from Hibernate

Hibernate:
 
    select

        customer0_.Id as Id1_1_0_,

        customer0_.FirstName as FirstNam2_1_0_,

        customer0_.LastName as LastName3_1_0_,

        customer0_.City as City4_1_0_,

        addresses1_.CUSTOMER_ID as CUSTOMER5_0_1_,

        addresses1_.Id as Id1_0_1_,

        addresses1_.idx as idx6_1_,

        addresses1_.Id as Id1_0_2_,

        addresses1_.Street as Street2_0_2_,

        addresses1_.Landmark as Landmark3_0_2_,

        addresses1_.PostalCode as PostalCo4_0_2_,

        addresses1_.CUSTOMER_ID as CUSTOMER5_0_2_ 

    from

        customers customer0_ 

    left outer join

        address addresses1_ 
                          on customer0_.Id=addresses1_.CUSTOMER_ID 

    where

        customer0_.Id=?

Hibernate generated only one select statement, it retrieve all its related collections when the Customer is loaded using session.get(Customer.class, 1)

Select statement to retrieve the Customer records

Outer join to retrieve its related collections.

FetchMode SELECT(default)


Modify above xml with fetch=”select”

(or)

Modify above annotated class with @Fetch(FetchMode.SELECT)

This is also called Lazy loading which means loads the collections and relations only when required

It loads only Customer when we request the Customer, it will not load the addresses until we request for it.

It will load all the addresses only when we explicitly request the addresses to use in our application.

1
2
3
4
5
6
7
8
Customer  customer = session.get(Customer.class, customerId); //Select from Customer table only
 
List<Address> addresses = customer.getAddresses();
 
//Select from Address table will start here
    for (Address address : addresses) {
            System.out.println(address);
        }
Customer  customer = session.get(Customer.class, customerId); //Select from Customer table only

List<Address> addresses = customer.getAddresses();
 
//Select from Address table will start here
	for (Address address : addresses) {
			System.out.println(address);
		}


Query generated from Hibernate

Hibernate:

    select

        customer0_.Id as Id1_1_0_,

        customer0_.FirstName as FirstNam2_1_0_,

        customer0_.LastName as LastName3_1_0_,

        customer0_.City as City4_1_0_ 

    from

        customers customer0_ 

    where

        customer0_.Id=?


Hibernate: 

    select

        addresses0_.CUSTOMER_ID as CUSTOMER5_0_0_,

        addresses0_.Id as Id1_0_0_,

        addresses0_.idx as idx6_0_,

        addresses0_.Id as Id1_0_1_,

        addresses0_.Street as Street2_0_1_,

        addresses0_.Landmark as Landmark3_0_1_,

        addresses0_.PostalCode as PostalCo4_0_1_,

        addresses0_.CUSTOMER_ID as CUSTOMER5_0_1_ 

    from

        address addresses0_ 

    where

        addresses0_.CUSTOMER_ID=?

Hibernate generated 2 select statements

First Select statement to retrieve the Customer recordssession.get(Customer.class, 1)

Second Select statement to retrieve its related collections – when we iterate addresses using enhanced for loop

FetchMode SELECT with Batch Size


No change in above xml as we have specified the batch size as 10 already

(or)

No change in above annotated class as we have specified the batch size as 10 already

Fetch upto “N” collections or entities(“Not number of records”)

The biggest misunderstanding of this fetch mode is, Batch size corresponds to number of records fetched per collection.
This is Complete misunderstanding.

Batch size decides the number of collections to be loaded when we load one entity.


Example:

We have given batch size as “10

Now when we load one Customer, Hibernate loads the address collection for additional 10 Customers which are currently in the session.

It means 10 address collections are loaded one for each Customer.

Suppose, We have 20 Customers in the session and batch size is set as 10

In this case, when we load one Customer, 3 queries will be executed

1. One query to load all the 20 Customers.

2. One query to load the Address collections for 10 Customers.

3. Another query to load the Address collections for other 10 Customers

If we have only one User then queries generated with batch size is same as without batch size as below

Queries generated by Hibernate

Hibernate:

    select

        customer0_.Id as Id1_1_0_,

        customer0_.FirstName as FirstNam2_1_0_,

        customer0_.LastName as LastName3_1_0_,

        customer0_.City as City4_1_0_ 

    from

        customers customer0_ 

    where

        customer0_.Id=?



Hibernate:

    select

        addresses0_.CUSTOMER_ID as CUSTOMER5_0_1_,

        addresses0_.Id as Id1_0_1_,

        addresses0_.idx as idx6_1_,

        addresses0_.Id as Id1_0_0_,

        addresses0_.Street as Street2_0_0_,

        addresses0_.Landmark as Landmark3_0_0_,

        addresses0_.PostalCode as PostalCo4_0_0_,

        addresses0_.CUSTOMER_ID as CUSTOMER5_0_0_ 

    from

        address addresses0_ 

    where

        addresses0_.CUSTOMER_ID=?


Suppose we have 10 users and we load all of them using below query

1
2
3
4
5
6
7
8
List<Customer> customers = session.createQuery("from Customer").getResultList();
    for (Customer customer : customers) {
                       List<Address> addresses = customer.getAddresses();
                    for (Address address : addresses) {
                        System.out.println(address);
                    }   
                    
                }
List<Customer> customers = session.createQuery("from Customer").getResultList();
	for (Customer customer : customers) {
	        		   List<Address> addresses = customer.getAddresses();
	   				for (Address address : addresses) {
	   					System.out.println(address);
	   				}	
					
				}


Now see the queries generated by Hibernate with Batch size of 10

Hibernate:

    select

        customer0_.Id as Id1_1_,

        customer0_.FirstName as FirstNam2_1_,

        customer0_.LastName as LastName3_1_,

        customer0_.City as City4_1_ 

    from

        customers customer0_



Hibernate: 

    select

        addresses0_.CUSTOMER_ID as CUSTOMER5_0_1_,

        addresses0_.Id as Id1_0_1_,

        addresses0_.idx as idx6_1_,

        addresses0_.Id as Id1_0_0_,

        addresses0_.Street as Street2_0_0_,

        addresses0_.Landmark as Landmark3_0_0_,

        addresses0_.PostalCode as PostalCo4_0_0_,

        addresses0_.CUSTOMER_ID as CUSTOMER5_0_0_ 

    from

        address addresses0_ 

    where

        addresses0_.CUSTOMER_ID in (
            ?, ?, ?,?,?,?,?,?,?,?
        )


Now Hibernate has generated 2 queries

First Select query to retrieve all the customer records

Second Select query with “IN” to retrieve its address collection for 10 customers(we can see 10 ‘?’ symbols ).

If we don’t use batch size in this case, we will end up with N+1 queries where “N” corresponds to the number of Customer records in the session which is 10 in this example.

So we will end up 10+1 = 11 queries.

1 query to retrieve all the 10 customers
1 query to retrieve its addresses for 1st customer
1 query to retrieve its address for 2nd customer
And so on up to 10 customers

In case,If we have 20 Customers in the session and batch size is 10

Then Hibernate will generate 3 queries

1.Select query to retrieve all the customer records
2.select query with “IN” to retrieve its address collection for first 10 customers.
3.select query with “IN” to retrieve its address collection for last 10 customers

Note:The batch-size fetching strategy is not specifying how many records in the collections are loaded. Instead, it specifies how many collections should be loaded at a time.

FetchMode SUBSELECT


Modify above xml with fetch=”subselect”

(or)

Modify above annotated class with @Fetch(FetchMode.SUBSELECT)

Group the collection of an entity into a Sub-Select query.

This strategy loads the collections using a sub query

Suppose we have 10 users and we load all of them using below query

1
2
3
4
5
6
7
8
List<Customer> customers = session.createQuery("from Customer").getResultList();
    for (Customer customer : customers) {
                       List<Address> addresses = customer.getAddresses();
                    for (Address address : addresses) {
                        System.out.println(address);
                    }   
                    
                }
List<Customer> customers = session.createQuery("from Customer").getResultList();
	for (Customer customer : customers) {
	        		   List<Address> addresses = customer.getAddresses();
	   				for (Address address : addresses) {
	   					System.out.println(address);
	   				}	
					
				}


Hibernate generates 2 queries in this case

Hibernate: 

    select

        customer0_.Id as Id1_1_,

        customer0_.FirstName as FirstNam2_1_,

        customer0_.LastName as LastName3_1_,

        customer0_.City as City4_1_ 

    from

        customers customer0_


Hibernate: 

    select

        addresses0_.CUSTOMER_ID as CUSTOMER5_0_1_,

        addresses0_.Id as Id1_0_1_,

        addresses0_.idx as idx6_1_,

        addresses0_.Id as Id1_0_0_,

        addresses0_.Street as Street2_0_0_,

        addresses0_.Landmark as Landmark3_0_0_,

        addresses0_.PostalCode as PostalCo4_0_0_,

        addresses0_.CUSTOMER_ID as CUSTOMER5_0_0_ 

    from

        address addresses0_ 

    where

        addresses0_.CUSTOMER_ID in (

            select

                customer0_.Id 

            from

                customers customer0_
        )


First Select query is to retrieve all the Customers
Second Select query is to Select the address collections for all the Customers using a sub query.

Even if we have 30 customers , the same 2 queries will be generated.

Which Fetch mode should I Use?


The most common question is “What is the best fetching strategy to use in my application

Answer to this question depends on so many factors like application , entity relationship,etc.

Lets see some of the general guidelines to choose the best one

FetchMode JOIN

In this case, we know that data will be loaded even before we use it but it creates a least number of queries.

Sometime Single Join is faster than multiple Selects but Joining will not be good choice if it involves too much of data.

We can prefer this mode whenever we have less data in the collections.

It will be faster while accessing the collections as its already loaded everything in one shot.

FetchMode SELECT

This is best to use when we want faster response on accessing single entity.

It loads additional data only when its required.

Example:
User with list of address

In many places of our application,we may need only User details not the address details

In this case, loading only User details will get the faster response.

Whenever we need addresses of an user in other parts of an application, It will be loaded only at that moment.

FetchMode SELECT with BatchSize

BatchSize is useful when have a fixed set of data.

Example :
When we have a batch processing of say 10 Users at a time.

BatchSize of 10 will drastically reduces the number of queries required.

Most important point to note here is, If batch size is small then its performance will be good otherwise it leads to more time and affect the performance.

We have to prefer this mode when we have small Batch size and have the requirement of processing the data batch wise.

FetchMode SUBSELECT

This mode will fetch all the related collections in a Sub query.

This should be used when we have an entity where most of them are loaded in the session.

Most important point to note here is that all the collections are fetched even if the parent is not in the session.

Example:
If we have 100 users in the database and only few users are in the session then using SubSelect is not a good choice as it loads the addresses of all the 100 users using sub query.

We should prefer it when we have one entity whose most of the records are already loaded in the session.

Note:
Using the right fetching strategy based on our requirement is important to optimize the Hibernate query performance.
If we use these strategies in a wrong way, then it will badly affect the performance of the query.
So use the right strategy with thorough understanding of the concept and the requirement.

About the Author

Karibasappa G C (KB)
Founder of javainsimpleway.com
I love Java and open source technologies and very much passionate about software development.
I like to share my knowledge with others especially on technology 🙂
I have given all the examples as simple as possible to understand for the beginners.
All the code posted on my blog is developed,compiled and tested in my development environment.
If you find any mistakes or bugs, Please drop an email to kb.knowledge.sharing@gmail.com

Connect with me on Facebook for more updates

Share this article on