JPA - Getting Entity's Load State with PersistenceUtil.isLoaded()

[Updated: Dec 7, 2017, Created: Dec 6, 2017]

javax.persistence.PersistenceUtil.isLoaded() methods can be used to determine the load state of an entity or entity attributes.

From JSR-338:

An entity is considered to be loaded if all attributes with FetchType.EAGER - whether explicitly specified or by default - (including relationship and other collection-valued attributes) have been loaded from the database or assigned by the application. Attributes with FetchType.LAZY may or may not have been loaded.

The instance of the PersistenceUtil interface can be obtained by the javax.persistence.Persistence.getPersistenceUtil() method.

Note that, instead of using PersistenceUtil.isLoaded() we can also use PersistenceUnitUtil.isLoaded(). The instance of PersistenceUnitUtil can be retrieved by EntityManagerFactory.getPersistenceUnitUtil() instance method.

PersistenceUnitUtil is a sub interface of PersistenceUtil.

Examples

The Entities

@Entity
public class Employee {
  @Id
  @GeneratedValue
  private Integer id;
  private String name;
  private String department;
  @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
  private List<Phone> phones;

  public Employee() {
  }

  public Employee(String name, String department, Phone... phones) {
      this.name = name;
      this.department = department;
      this.phones = Arrays.asList(phones);
  }
    .............
  @Override
  public String toString() {
      return "Employee{" +
              "id=" + id +
              ", name='" + name + '\'' +
              ", department='" + department + '\'' +
              '}';
  }
}
@Entity
public class Phone {
  @Id
  @GeneratedValue
  private int id;
  private String number;
  private String type;

  public static Phone cell(String number) {
      Phone p = new Phone();
      p.number = number;
      p.type = "cell";
      return p;
  }

  public static Phone work(String number) {
      Phone p = new Phone();
      p.number = number;
      p.type = "work";
      return p;
  }
    .............
  @Override
  public String toString() {
      return "Phone{" +
              "id=" + id +
              ", number='" + number + '\'' +
              ", type='" + type + '\'' +
              '}';
  }
}

Using PersistenceUtil.isLoaded() methods

Before and after Persisting

public class LoadStateWithPersist {
  private static EntityManagerFactory entityManagerFactory =
          Persistence.createEntityManagerFactory("example-unit");

  public static void main(String[] args) {
      try {
          persistEmployee();
      } finally {
          entityManagerFactory.close();
      }
  }

  public static void persistEmployee() {
      Employee employee = new Employee("Joe", "IT",
              Phone.cell("111-111-1111"), Phone.work("222-222-2222"));
      printLoadState("New Entity", employee);
      EntityManager em = entityManagerFactory.createEntityManager();
      em.getTransaction().begin();
      em.persist(employee);
      printLoadState("After persisting", employee);
      em.getTransaction().commit();
      em.close();
      printLoadState("Detach entity after EntityManger.close()", employee);
  }

  private static void printLoadState(String msg, Employee employee) {
      System.out.printf("-- %s --%n", msg);
      PersistenceUtil pu = Persistence.getPersistenceUtil();
      boolean employeeLoaded = pu.isLoaded(employee);
      System.out.printf("employee loaded: %s%n", employeeLoaded);
      boolean phonesLoaded = pu.isLoaded(employee, "phones");
      System.out.printf("  phones loaded: %s%n", phonesLoaded);
  }
}
-- New Entity --
employee loaded: true
phones loaded: true
-- After persisting --
employee loaded: true
phones loaded: true
-- Detach entity after EntityManger.close() --
employee loaded: true
phones loaded: true

Retrieving entity by find()

   EntityManager em = entityManagerFactory.createEntityManager();
   Employee employee = em.find(Employee.class, 1);
   printLoadState("After find()", employee);
   //accessing phones via Phone.toString()
   System.out.println("Accessing phones: " + employee.getPhones());
   printLoadState("After accessing phones", employee);
   em.close();
-- After find() --
employee loaded: true
phones loaded: false
---------------
Accessing phones:[Phone{id=2, number='111-111-1111', type='cell'}, Phone{id=3, number='222-222-2222', type='work'}]
-- After accessing phones --
employee loaded: true
phones loaded: true
---------------

As seen in above output, just calling find() method marks employee entity as loaded, that's because find() method will initialize all eager fields. As 'phones' field is declared to be lazy, it is only loaded upon first access. This behavior is same when the entity is loaded via JPQL query 'select t from Employee t' (an example is included in project browser below).

Retrieving entity by getReference()

   EntityManager em = entityManagerFactory.createEntityManager();
   Employee employee = em.getReference(Employee.class, 1);
   printLoadState("After finding by getReference()", employee);
   //accessing department field
   System.out.println("Accessing employee.department: " + employee.getDepartment());
   printLoadState("After accessing employee.department field", employee);
   //accessing  phones field via Phones.toString() method
   System.out.println("Accessing employee.phones: " + employee.getPhones());
   printLoadState("After accessing employee.phones field", employee);
   em.close();
-- After finding by getReference() --
employee loaded: false
phones loaded: false
---------------
Accessing employee.department: IT
-- After accessing employee.department field --
employee loaded: true
phones loaded: false
---------------
Accessing employee.phones: [Phone{id=2, number='111-111-1111', type='cell'}, Phone{id=3, number='222-222-2222', type='work'}]
-- After accessing employee.phones field --
employee loaded: true
phones loaded: true
---------------

As seen in above output, both the parent and it's lazy field (phones) are unloaded. That's because getReference() returns an uninitialized proxy of the entity (tutorial here). After accessing department field, the state of employee entity became 'loaded' that's because all non lazy fields (eager fields) had been loaded at that point. As 'phones' field is declared to be fetched lazily, it is only loaded upon first access (just like last find() example)

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

PersistenceUtil.isLoaded() Examples Select All Download
  • entity-load-state-examples
    • src
      • main
        • java
          • com
            • logicbig
              • example
        • resources
          • META-INF

See Also