How to use Hibernate Lazy Fetch and Eager Fetch Type – Spring Boot + MySQL

How to use Hibernate Lazy Fetch and Eager Fetch Type – Spring Boot + MySQL

Many case asking that “Spring JPA lazy loading not working!”. So it is very hard to start with Hibernate and Spring JPA. In the tutorial, I will help you understand SpringJPA Hibernate Lazy Fetch Type and Eager Fetch Type by example code with Spring Boot & MySql database using with Spring JPA annotations’ @onetoone and @onetomany

– Lazy Loading is a design pattern which is used to defer initialization of an object as long as it’s possible. The FetchType.LAZY tells Hibernate to only fetch the related entities from the database when you use the relationship
(This does not load the relationships unless you invoke it via the getter method)
– Eager Loading is a design pattern in which data initialization occurs on the spot. Eager initialization takes more memory consumption and processing speed is slow. FetchType.EAGER = This loads all the relationships.

Related articles:
How to configure Spring JPA One to Many Relationship – SpringBoot
How to start development with Hibernate – XML Mapping File Hibernate

I. Technologies

– Java 1.8
– Maven 3.3.9
– Spring Tool Suite – Version 3.8.1.RELEASE
– Spring Boot: 1.5.1.RELEASE
– MySql database

II. Hibernate Lazy Fetch & Eager Fetch Type

How to load all entities of relationships in RDBMS Database?
-> Hibernate provides 2 strategies to retrieve entities(records) of all relationships: Lazy Fetch & Eager Fetch. So Hibernate Fetch Types are always associated with relationship annotations: @OneToOne, @OneToMany, @ManyToOne, @ManyToMany.

1. Eager Fetch Type

Hibernate Eager Fetch Type comes with setting: fetch = FetchType.EAGER.
Details:


@Entity
@Table(name="company")
public class Company{
	@Id
    @GeneratedValue(strategy = GenerationType.AUTO)
	private int id;
    private String name;
    
    @OneToMany(mappedBy = "company", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Set products;
    
    public Company(){
    }
    ...
}

The meaning of CascadeType.ALL is that the persistence will propagate (cascade) all EntityManager operations (PERSIST, REMOVE, REFRESH, MERGE, DETACH) to the relating entities.

Hibernate Eager Fetch Type will load all the relationship entities at the initial time.

– Example, when calling companyRepository.findAll(), all the records of product tables will be loaded by Hibernate and stored in Set products of a Company object.

Details:
CompanyServices.java:


@Service
public class CompanyServices {
    ...
    public void showData(){
        List companyLst = companyRepository.findAll();
        companyLst.forEach(System.out::println);;
    }
    ...	
}

Logs:


=====================Retrieve Companies from Database:====================
Hibernate: select company0_.id as id1_0_, company0_.name as name2_0_ from company company0_
Hibernate: select products0_.company_id as company_3_1_0_, products0_.id as id1_1_0_, products0_.id as id1_1_1_, products0_.company_id as company_3_1_1_, products0_.name as name2_1_1_ from product products0_ where products0_.company_id=?
Hibernate: select products0_.company_id as company_3_1_0_, products0_.id as id1_1_0_, products0_.id as id1_1_1_, products0_.company_id as company_3_1_1_, products0_.name as name2_1_1_ from product products0_ where products0_.company_id=?
=====================Show All Companies on console:====================
{"name":"Apple","products":[{"name":"Iphone 7"},{"name":"IPadPro"}]}
{"name":"Samsung","products":[{"name":"GalaxyJ7"},{"name":"GalaxyTabA"}]}

From the logs, having three select statements when calling function: companyRepository.findAll() to load company entities and all relationship entities: products at initial time. Then for next processing step(showing them on console), all info of product entities are ready in memory for getting.

*** Note: should consider about memory & performance issues when working with Eager fetching strategy.

2. Lazy Fetch Type

Hibernate Lazy Fetch Type comes with setting: fetch = FetchType.LAZY.

Details:


@Entity
@Table(name="company")
public class Company{
	@Id
    @GeneratedValue(strategy = GenerationType.AUTO)
	private int id;
    private String name;
    
    @OneToMany(mappedBy = "company", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private Set products;
    
    public Company(){
    }

    public String toString(){
    	String info = "";
        JSONObject jsonInfo = new JSONObject();
        jsonInfo.put("name",this.name);
        
        JSONArray productArray = new JSONArray();
        if(this.products != null){
            this.products.forEach(product->{
                JSONObject subJson = new JSONObject();
                subJson.put("name", product.getName());
                productArray.put(subJson);
            });
        }
        jsonInfo.put("products", productArray);
        info = jsonInfo.toString();
        return info;
    }
    ...
}

Hibernate Eager Fetch Type will NOT load any entities (records) of relationships at the initial time.
Need @Transaction annotation for Lazy Fetch to associate relationship loaded entities with a Hibernate session. If NOT, an exception will be thrown:


Caused by: org.hibernate.LazyInitializationException: could not initialize proxy - no Session
	at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:148) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:266) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:73) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
	at com.javasampleapproach.jpa.one2many.model.Company_$$_jvstdfb_0.getName(Company_$$_jvstdfb_0.java) ~[classes/:na]

Details company loading service:
CompanyServices.java


@Service
public class CompanyServices {
    ...
    @Transactional
    public void showData(){
        List companyLst = companyRepository.findAll();
        companyLst.forEach(System.out::println);;
    }
    ...	
}

Logs when calling the function: companyRepository.findAll():


=====================Retrieve Companies from Database:====================
Hibernate: select company0_.id as id1_0_, company0_.name as name2_0_ from company company0_
=====================Show All Companies on console:====================
Hibernate: select products0_.company_id as company_3_1_0_, products0_.id as id1_1_0_, products0_.id as id1_1_1_, products0_.company_id as company_3_1_1_, products0_.name as name2_1_1_ from product products0_ where products0_.company_id=?
{"name":"Apple","products":[{"name":"IPadPro"},{"name":"Iphone 7"}]}
Hibernate: select products0_.company_id as company_3_1_0_, products0_.id as id1_1_0_, products0_.id as id1_1_1_, products0_.company_id as company_3_1_1_, products0_.name as name2_1_1_ from product products0_ where products0_.company_id=?
{"name":"Samsung","products":[{"name":"GalaxyTabA"},{"name":"GalaxyJ7"}]}

Difference with Eager Fetch, at retrieving Company entities step, just having one select statement to load company entities while invoking the function: companyRepository.findAll().
When showing relationship entities(products) info, having 2 others select statements to load product records from database.

III. Practice

Step to do:
– Create SpringBoot project
– Create Models
– Create JPA Repositories
– Implement Services
– Configure Datasource & Spring JPA
– Implement a test Client
– Run & Check results

1. Create SpringBoot project

– Using SpringToolSuite, create a SpringBoot project. Then add needed dependencies:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
	<groupId>org.json</groupId>
	<artifactId>json</artifactId>
</dependency>

2. Create Models

2 Entities Company and Product that having One-to-Many relationship:

Spring Jpa One To Many Relationship

2.1 Company entity


package com.javasampleapproach.hibernate.fetchtype.model;

import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.json.JSONArray;
import org.json.JSONObject;

@Entity
@Table(name="company")
public class Company {
	@Id
    @GeneratedValue(strategy = GenerationType.AUTO)
	private int id;
    private String name;
    
    @OneToMany(mappedBy = "company", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Set products;
    
    public Company(){
    }
    
    public Company(String name){
    	this.name = name;
    }
    
    public Company(String name, Set products){
    	this.name = name;
    	this.products = products;
    }
    
    // name
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    // products
    public void setProducts(Set products){
    	this.products = products;
    }
    
    public Set getProducts(){
    	return this.products;
    }
    
    public String toString(){
    	String info = "";
        JSONObject jsonInfo = new JSONObject();
        jsonInfo.put("name",this.name);
        
        JSONArray productArray = new JSONArray();
        if(this.products != null){
            this.products.forEach(product->{
                JSONObject subJson = new JSONObject();
                subJson.put("name", product.getName());
                productArray.put(subJson);
            });
        }
        jsonInfo.put("products", productArray);
        info = jsonInfo.toString();
        return info;
    }
}

2.2 Product entity


package com.javasampleapproach.hibernate.fetchtype.model;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import org.json.JSONObject;

@Entity
@Table(name="product")
public class Product {
	
	@Id
    @GeneratedValue(strategy = GenerationType.AUTO)
	private int id;
    private String name;
    
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "company_id")
    private Company company;
    
    public Product(){
    }
    
    public Product(String name){
    	this.name = name;
    }
    
    public Product(String name, Company company){
    	this.name = name;
    	this.company = company;
    }
    
    // name
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    // products
    public void setCompany(Company company){
    	this.company = company;
    }
    
    public Company getCompany(){
    	return this.company;
    }
    
    public String toString(){
    	String info = "";
    	
        JSONObject jsonInfo = new JSONObject();
        jsonInfo.put("name",this.name);
        
        JSONObject companyObj = new JSONObject();
        companyObj.put("name", this.company.getName());
        jsonInfo.put("company", companyObj);
        
        info = jsonInfo.toString();
        return info;
    }
}

@Entity: Specifies that the class is an entity. This annotation is applied to the entity class.
@Id: Specifies the primary key of an entity.
@OneToMany: Defines a many-valued association with one-to-many multiplicity.
@ManyToOne: Defines a single-valued association to another entity class that has many-to-one multiplicity
@JoinColumn: Specifies a column for joining an entity association or element collection. If the JoinColumn annotation itself is defaulted, a single join column is assumed and the default values apply.

3. Create JPA Repositories

Create 2 interface repositories by extends JpaRepository:
CompanyRepository.java


package com.javasampleapproach.hibernate.fetchtype.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.javasampleapproach.jpa.one2many.model.Company;

public interface CompanyRepository extends JpaRepository{
}

ProductRepository.java


package com.javasampleapproach.hibernate.fetchtype.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.javasampleapproach.jpa.one2many.model.Product;

public interface ProductRepository extends JpaRepository{
}

4. Implement Services

CompanyServices.java:


package com.javasampleapproach.hibernate.fetchtype.services;

import java.util.List;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.javasampleapproach.jpa.one2many.model.Company;
import com.javasampleapproach.jpa.one2many.repository.CompanyRepository;


@Service
public class CompanyServices {
	@Autowired
    CompanyRepository companyRepository;

	public void save(Company company){
		companyRepository.save(company);
	}
	
	@Transactional
	public void showData(){
		System.out.println("=====================Retrieve Companies from Database:====================");
		List companyLst = companyRepository.findAll();
		System.out.println("=====================Show All Companies on console:====================");
		companyLst.forEach(System.out::println);;
	}
		
	public void deleteAll(){
		companyRepository.deleteAll();
	}
}

ProductServices.java:


package com.javasampleapproach.hibernate.fetchtype.services;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.javasampleapproach.jpa.one2many.model.Product;
import com.javasampleapproach.jpa.one2many.repository.ProductRepository;

@Service
public class ProductServices {
	@Autowired
    ProductRepository productRepository;
	
	public void save(Product product){
		productRepository.save(product);
	}
	
	@Transactional
	public void showData(){
		System.out.println("=====================Retrieve Products from Database:====================");
		List productLst = productRepository.findAll();
		System.out.println("=====================Show All Products on console:====================");
        productLst.forEach(System.out::println);
	}
	
	
	public void deleteAll(){
		productRepository.deleteAll();
	}
}

About showData() function, we must implement with @Transactional for Lazy Fetch Type. But @Transactional is optional with Eager FetchType.

5. Configure Datasource & Spring JPA

Open application.properties, configure spring.datasource & spring.jpa:


spring.datasource.url=jdbc:mysql://localhost:3306/testdb
spring.datasource.username=root
spring.datasource.password=12345
 
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect

6. Implement a test Client

Use 2 repository: CompanyRepository & ProductRepository


@Autowired
CompanyServices companyService;

@Autowired
ProductServices productService;

Implement 3 functions:
clearData() is used to empty 2 tables company & product
saveData() is used to persist entities (Company & Product) to database
showData() is used to load all records (Company & Product) and show all on console.

Full SourceCode:


package com.javasampleapproach.hibernate.fetchtype;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.javasampleapproach.jpa.one2many.model.Company;
import com.javasampleapproach.jpa.one2many.model.Product;
import com.javasampleapproach.jpa.one2many.services.CompanyServices;
import com.javasampleapproach.jpa.one2many.services.ProductServices;


@SpringBootApplication
public class SpringJpaOneToManyRelationshipApplication implements CommandLineRunner{
	@Autowired
	CompanyServices companyService;
	
	@Autowired
	ProductServices productService;

 
    public static void main(String[] args) {
    	SpringApplication.run(SpringJpaOneToManyRelationshipApplication.class, args);
    }
 
    
    @Override
    public void run(String... arg0) throws Exception {
    	clearData();
    	saveData();
    	showData();
    }
    
    private void clearData(){
    	System.out.println("=================== Clear DATA =======================");
    	companyService.deleteAll();
        productService.deleteAll();
    }
    
    private void saveData(){
    	System.out.println("=================== Save DATA =======================");
    	Product iphone7 = new Product("Iphone 7");
        Product iPadPro = new Product("IPadPro");
        
        Product galaxyJ7 = new Product("GalaxyJ7");
        Product galaxyTabA = new Product("GalaxyTabA");
         
        Company apple = new Company("Apple");
        Company samsung = new Company("Samsung");
        
        // set company for products
        iphone7.setCompany(apple);
        iPadPro.setCompany(apple);
        
        galaxyJ7.setCompany(samsung);
        galaxyTabA.setCompany(samsung);
        
        // save companies
        companyService.save(apple);
        companyService.save(samsung);
         
        // save products
        productService.save(iphone7);
        productService.save(iPadPro);
        
        productService.save(galaxyJ7);
        productService.save(galaxyTabA);
    }
    
    private void showData(){
    	System.out.println("=================== Show ALL Data =======================");
        companyService.showData();
        productService.showData();
    }
    
}

7. Run & Check results

7.1 Run with Eager Fetch Type

Build & Run the project with SpringBoot App mode.
The sourcecode is ready with Eager Fetch Type config for both Company & Product.
Logs:


=====================Retrieve Companies from Database:====================
Hibernate: select company0_.id as id1_0_, company0_.name as name2_0_ from company company0_
Hibernate: select products0_.company_id as company_3_1_0_, products0_.id as id1_1_0_, products0_.id as id1_1_1_, products0_.company_id as company_3_1_1_, products0_.name as name2_1_1_ from product products0_ where products0_.company_id=?
Hibernate: select products0_.company_id as company_3_1_0_, products0_.id as id1_1_0_, products0_.id as id1_1_1_, products0_.company_id as company_3_1_1_, products0_.name as name2_1_1_ from product products0_ where products0_.company_id=?
=====================Show All Companies on console:====================
{"name":"Apple","products":[{"name":"Iphone 7"},{"name":"IPadPro"}]}
{"name":"Samsung","products":[{"name":"GalaxyJ7"},{"name":"GalaxyTabA"}]}
=====================Retrieve Products from Database:====================
Hibernate: select product0_.id as id1_1_, product0_.company_id as company_3_1_, product0_.name as name2_1_ from product product0_
Hibernate: select company0_.id as id1_0_0_, company0_.name as name2_0_0_, products1_.company_id as company_3_1_1_, products1_.id as id1_1_1_, products1_.id as id1_1_2_, products1_.company_id as company_3_1_2_, products1_.name as name2_1_2_ from company company0_ left outer join product products1_ on company0_.id=products1_.company_id where company0_.id=?
Hibernate: select company0_.id as id1_0_0_, company0_.name as name2_0_0_, products1_.company_id as company_3_1_1_, products1_.id as id1_1_1_, products1_.id as id1_1_2_, products1_.company_id as company_3_1_2_, products1_.name as name2_1_2_ from company company0_ left outer join product products1_ on company0_.id=products1_.company_id where company0_.id=?
=====================Show All Products on console:====================
{"name":"Iphone 7","company":{"name":"Apple"}}
{"name":"IPadPro","company":{"name":"Apple"}}
{"name":"GalaxyJ7","company":{"name":"Samsung"}}
{"name":"GalaxyTabA","company":{"name":"Samsung"}}

7.2 Run with Lazy Fetch Type

Change the Fetch Type to LAZY for both: Company & Product.

Company.java:


...
public class Company{
    ...
    @OneToMany(mappedBy = "company", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    ...

Product.java:


...
public class Product{
    ...
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "company_id")
    private Company company;
    ...
}

– Build & Run again the project with SpringBoot App mode.
Logs:


=====================Retrieve Companies from Database:====================
Hibernate: select company0_.id as id1_0_, company0_.name as name2_0_ from company company0_
=====================Show All Companies on console:====================
Hibernate: select products0_.company_id as company_3_1_0_, products0_.id as id1_1_0_, products0_.id as id1_1_1_, products0_.company_id as company_3_1_1_, products0_.name as name2_1_1_ from product products0_ where products0_.company_id=?
{"name":"Apple","products":[{"name":"Iphone 7"},{"name":"IPadPro"}]}
Hibernate: select products0_.company_id as company_3_1_0_, products0_.id as id1_1_0_, products0_.id as id1_1_1_, products0_.company_id as company_3_1_1_, products0_.name as name2_1_1_ from product products0_ where products0_.company_id=?
{"name":"Samsung","products":[{"name":"GalaxyJ7"},{"name":"GalaxyTabA"}]}
=====================Retrieve Products from Database:====================
Hibernate: select product0_.id as id1_1_, product0_.company_id as company_3_1_, product0_.name as name2_1_ from product product0_
=====================Show All Products on console:====================
Hibernate: select company0_.id as id1_0_0_, company0_.name as name2_0_0_ from company company0_ where company0_.id=?
{"name":"Iphone 7","company":{"name":"Apple"}}
{"name":"IPadPro","company":{"name":"Apple"}}
Hibernate: select company0_.id as id1_0_0_, company0_.name as name2_0_0_ from company company0_ where company0_.id=?
{"name":"GalaxyJ7","company":{"name":"Samsung"}}
{"name":"GalaxyTabA","company":{"name":"Samsung"}}

7.3 Mix Lazy & Eager Fetch Types

Change fetch type: Company with FetchType.LAZY BUT Product with FetchType.EAGER.

Company.java:


...
public class Company{
    ...
    @OneToMany(mappedBy = "company", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    ...

Product.java:


...
public class Product{
    ...
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "company_id")
    private Company company;
    ...
}

Build & Run again the project with SpringBoot App mode.

Logs:


=====================Retrieve Companies from Database:====================
Hibernate: select company0_.id as id1_0_, company0_.name as name2_0_ from company company0_
=====================Show All Companies on console:====================
Hibernate: select products0_.company_id as company_3_1_0_, products0_.id as id1_1_0_, products0_.id as id1_1_1_, products0_.company_id as company_3_1_1_, products0_.name as name2_1_1_ from product products0_ where products0_.company_id=?
{"name":"Apple","products":[{"name":"IPadPro"},{"name":"Iphone 7"}]}
Hibernate: select products0_.company_id as company_3_1_0_, products0_.id as id1_1_0_, products0_.id as id1_1_1_, products0_.company_id as company_3_1_1_, products0_.name as name2_1_1_ from product products0_ where products0_.company_id=?
{"name":"Samsung","products":[{"name":"GalaxyTabA"},{"name":"GalaxyJ7"}]}
=====================Retrieve Products from Database:====================
Hibernate: select product0_.id as id1_1_, product0_.company_id as company_3_1_, product0_.name as name2_1_ from product product0_
Hibernate: select company0_.id as id1_0_0_, company0_.name as name2_0_0_ from company company0_ where company0_.id=?
Hibernate: select company0_.id as id1_0_0_, company0_.name as name2_0_0_ from company company0_ where company0_.id=?
=====================Show All Products on console:====================
{"name":"Iphone 7","company":{"name":"Apple"}}
{"name":"IPadPro","company":{"name":"Apple"}}
{"name":"GalaxyJ7","company":{"name":"Samsung"}}
{"name":"GalaxyTabA","company":{"name":"Samsung"}}

IV. Sourcecode

SpringHibernateFetchType

Related post:

How to use Spring JDBC Template with Spring Boot for Postgres DataBase
Spring JPA/Hibernate One-to-Many Association + PostgreSQL | SpringBoot CRUD RestAPIs Post/Get/Put/Delete example
Spring MongoOperations to access MongoDB
How to start Spring Data Cassandra with SpringBoot



By grokonez | April 27, 2017.

Last updated on May 10, 2021.



Related Posts


2 thoughts on “How to use Hibernate Lazy Fetch and Eager Fetch Type – Spring Boot + MySQL”

  1. Thanks for this article. But what if I just want to return a company with products data to my controller for example. How would I do this?

    @Transactional
    public Company showData(){
    Company company = companyRepository.findById(1).get();
    // logger.info(company.getProducts); –> I must print in order to get all products
    return company
    }

    Then I tried

    @Transactional
    public Company showData(){
    Company company = companyRepository.findById(1).get();
    company.getProducts(); // My thinking is if company still retain session with db if I do this it just trigger a join fetch but it doesn’t… I must implement System.print or logger… to get products.
    return company;
    }

    Maybe is just my logic problem but if you could kindly explain why printing the object or toString the object triggers the fetch. Thank you

Got Something To Say:

Your email address will not be published. Required fields are marked *

*