JPA - Detached Entities and Lazy Loading

[Updated: Nov 27, 2017, Created: Nov 26, 2017]

Lazy associations cannot be fetched for the detached entities. Lazy fetching is desired when associated entities are not likely to be accessed right after the loading of the parent entity. They are usually accessed conditionally and may be after a long time of their parent entity loading. To access lazy relationships of the detached entities, we have to attach them back to the persistence context by using EntityManager.merge() method. Let's understand that with an example.

Example

The Entities

@Entity
public class Person {
  @Id
  @GeneratedValue
  private long id;
  private String name;
  @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
  private List<Phone> phoneList;
    .............
}
@Entity
public class Phone {
  @Id
  @GeneratedValue
  private long id;
  private String number;
  private String type;
    .............
}

Fetching detached entity's lazy relationship

public class ExampleMain {

  static EntityManagerFactory emf =
          Persistence.createEntityManagerFactory("example-unit");

  public static void main(String[] args) throws Exception {
      try {
          long id = persistPerson();
          Person person = loadPersonById(id);
          person.getPhoneList().forEach(System.out::println);
      } finally {
          emf.close();
      }
  }

  private static Person loadPersonById(long id) {
      EntityManager em = emf.createEntityManager();
      Person person = em.find(Person.class, id);
      em.close();
      return person;
  }


  private static long persistPerson() {
      Person person = new Person();
      person.setName("Jackie");
      person.addPhone("111-11-1111", "cell");
      person.addPhone("22-222-2222", "work");

      EntityManager em = emf.createEntityManager();
      em.getTransaction().begin();
      em.persist(person);
      em.getTransaction().commit();
      em.close();
      return person.getId();
  }
}
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.logicbig.example.Person.phoneList, could not initialize proxy - no Session
	at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:582)
	at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:201)
	at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:561)
	at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:132)
	at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:277)
	at java.lang.Iterable.forEach(Iterable.java:74)
	at com.logicbig.example.ExampleMain.main(ExampleMain.java:17)

Since lazy fetching is only a hint and can be ignored by a JPA provider, this behavior might not be consistent for different providers.

To fix above exception, we need to use merge() method and fetch the lazy data within the EntityManager context (before calling close()).

public class ExampleMain2 {
  static EntityManagerFactory emf =
          Persistence.createEntityManagerFactory("example-unit");

  public static void main(String[] args) throws Exception {
      try {
          long id = persistPerson();
          Person person = loadPersonById(id);
          List<Phone> phoneList = mergeAndGetPhoneList(person);
          phoneList.forEach(System.out::println);

      } finally {
          emf.close();
      }
  }

  private static List<Phone> mergeAndGetPhoneList(Person person) {
      EntityManager em = emf.createEntityManager();
      person = em.merge(person);
      List<Phone> phoneList = person.getPhoneList();
      em.close();
      return phoneList;
  }
    .............
}

Output

Phone{id=2, number='111-11-1111', type='cell'}
Phone{id=3, number='22-222-2222', type='work'}

Example Project

Dependencies and Technologies Used :

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

Detached Entities and Lazy Loading Select All Download
  • detach-entities-and-lazy-loading
    • src
      • main
        • java
          • com
            • logicbig
              • example
        • resources
          • META-INF

See Also