JPA - Lazy Fetching

[Updated: Sep 8, 2017, Created: Sep 4, 2017]

The LAZY strategy specifies that data should only be fetched when it is first accessed. According to the JPA specification, this is only a hint and a particular JPA implementation is free to eagerly fetch data for which the LAZY strategy has been specified.

FetchType enum

package javax.persistence;

public enum FetchType {
    /** Defines that data can be lazily fetched. */
    LAZY,
    /** Defines that data must be eagerly fetched. */
    EAGER
}

Annotations with element fetch()

  • @Basic
  • @OneToOne
  • @ManyToOne
  • @OneToMany
  • @ManyToMany
  • @ElementCollection

Example

In the following example, we are going to use @OneToOne relationship with fetch=FetchType.LAZY. We also going to enable hibernate logging to see the sql statements, so that we will know when a particular entity is loaded.

Entities

@Entity
public class Customer {
  @Id
  @GeneratedValue
  private int id;
  private String name;
  private String address;
  @OneToOne(fetch = FetchType.LAZY)
  private OrderItem orderItem;
    .............
}
@Entity
public class OrderItem {
  @Id
  @GeneratedValue
  private int id;
  private String itemName;
  private int quantity;
    .............
}

src/main/resources/META-INF/persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
             xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">

    <persistence-unit name="example-unit" transaction-type="RESOURCE_LOCAL">
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
            <property name="javax.persistence.schema-generation.database.action" value="create"/>
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"/>
            <property name="hibernate.show_sql" value="true"/>
        </properties>
    </persistence-unit>

</persistence>

Persisting and loading data

public class ExampleMain {

  public static void main(String[] args) {
      EntityManagerFactory emf =
              Persistence.createEntityManagerFactory("example-unit");
      try {
          persistCustomerEntity(emf);
          loadEntities(emf);
      } finally {
          emf.close();
      }
  }

  private static void persistCustomerEntity(EntityManagerFactory emf) {
      System.out.println("-- Persisting Customer entity --");
      EntityManager em = emf.createEntityManager();

      Customer customer = new Customer();
      customer.setName("Maria Dorsey");
      customer.setAddress("1719 Shumaker Boulevard, Sylvania");
      OrderItem orderItem = new OrderItem();
      orderItem.setItemName("GEN SSD");
      orderItem.setQuantity(3);
      customer.setOrderItem(orderItem);

      em.getTransaction().begin();
      em.persist(orderItem);
      em.persist(customer);
      em.getTransaction().commit();
      em.close();
  }

  private static void loadEntities(EntityManagerFactory emf) {
      System.out.println("-- Loading Customer --");
      EntityManager em = emf.createEntityManager();
      List<Customer> customers = em.createQuery("Select t from Customer t")
                                   .getResultList();
      customers.forEach(ExampleMain::printObject);
      for (Customer customer : customers) {
          System.out.println("-- accessing customer#OrderItem --");
          OrderItem orderItem = customer.getOrderItem();
          printObject(orderItem);
      }
      em.close();
  }

  private static void printObject(Object obj) {
      System.out.println("Object: " + obj);
  }
}

Output

Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: create table Customer (id integer not null, address varchar(255), name varchar(255), orderItem_id integer, primary key (id))
Hibernate: create table OrderItem (id integer not null, itemName varchar(255), quantity integer not null, primary key (id))
Hibernate: alter table Customer add constraint FK1ybrwg8u38ltbsp0b49w4dpjy foreign key (orderItem_id) references OrderItem
-- Persisting Customer entity --
Hibernate: call next value for hibernate_sequence
Hibernate: call next value for hibernate_sequence
Hibernate: insert into OrderItem (itemName, quantity, id) values (?, ?, ?)
Hibernate: insert into Customer (address, name, orderItem_id, id) values (?, ?, ?, ?)
-- Loading Customer --
Hibernate: select customer0_.id as id1_0_, customer0_.address as address2_0_, customer0_.name as name3_0_, customer0_.orderItem_id as orderIte4_0_ from Customer customer0_
Object: Customer{id=2, name='Maria Dorsey', address='1719 Shumaker Boulevard, Sylvania'}
-- accessing customer#OrderItem --
Hibernate: select orderitem0_.id as id1_1_0_, orderitem0_.itemName as itemName2_1_0_, orderitem0_.quantity as quantity3_1_0_ from OrderItem orderitem0_ where orderitem0_.id=?
Object: OrderItem{id=1, itemName='GEN SSD', quantity=3}

When Lazy objects are fetched

As seen in above sql statements, the OrderItem entity is only loaded when first accessed via customer.getOrderItem(). Actually methods like getOrderItem() still might not load the underlying object (getOrderItem() will only return a proxy). It will only be loaded when we first access its fields/methods. In above example, it is loaded when we called printObject(orderItem) and System.out.println used (toString() method is invoked).

Eager loading

In above example, if we specify fetch=FetchType.EAGER (or just remove the fetch element, as EAGER is default for @OneToOne) then both entities will be loaded at the time when we first load Customer. Following shows the last part logs in that case:

......
-- Loading Customer --
Sep 04, 2017 9:50:05 PM org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService
INFO: HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select customer0_.id as id1_0_, customer0_.address as address2_0_, customer0_.name as name3_0_, customer0_.orderItem_id as orderIte4_0_ from Customer customer0_
Sep 04, 2017 9:50:05 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
Hibernate: select orderitem0_.id as id1_1_0_, orderitem0_.itemName as itemName2_1_0_, orderitem0_.quantity as quantity3_1_0_ from OrderItem orderitem0_ where orderitem0_.id=?
INFO: HHH10001008: Cleaning up connection pool [jdbc:h2:mem:test;DB_CLOSE_DELAY=-1]
Object: Customer{id=2, name='Maria Dorsey', address='1719 Shumaker Boulevard, Sylvania'}
-- accessing customer#OrderItem --
Object: OrderItem{id=1, itemName='GEN SSD', quantity=3}

Example Project

Dependencies and Technologies Used :

  • h2 1.4.196: H2 Database Engine.
  • hibernate-core 5.2.10.Final: The core O/RM functionality as provided by Hibernate.
  • JDK 1.8
  • Maven 3.3.9

Lazy Fetching Example Select All Download
  • jpa-lazy-fetching
    • src
      • main
        • java
          • com
            • logicbig
              • example
        • resources
          • META-INF

See Also