JPA - Getting Entity's Lazy Proxy with EntityManager.getReference()

[Updated: Dec 3, 2017, Created: Dec 2, 2017]

EntityManager.getReference(entityClass, primaryKey) can be used to get an entity instance whose state may be lazily initialized. The returned entity instance is a proxy whose field values are only initialized/loaded from database upon first access.

Example

In this example, we have enabled hibernate.show_sql property in persistence.xml so that we will know how our entity is loaded via getReference() method call.

The Entity

@Entity
public class Employee {
  @Id
  @GeneratedValue
  private Integer id;
  private String name;
  private String department;
    .............
}

Using getReference() method

public class GetReferenceExample {

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

  private static Employee persistEntity(EntityManagerFactory emf) {
      Employee employee = new Employee("Sara Dorsey", "Admin");
      System.out.println("-- persisting employee --");
      EntityManager em = emf.createEntityManager();
      em.getTransaction().begin();
      em.persist(employee);
      em.getTransaction().commit();
      em.close();
      System.out.println("Employee persisted: " + employee);
      return employee;
  }

  private static void loadEntity(EntityManagerFactory emf) {
      System.out.println("-- loading employee by id --");
      EntityManager em = emf.createEntityManager();
      System.out.println("calling getReference().");
      Employee employee = em.getReference(Employee.class, 1);
      System.out.println("getReference() already called");
      System.out.println("loaded employee name: " + employee.getName());
      em.close();
  }
}
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: create table Employee (id integer not null, department varchar(255), name varchar(255), primary key (id))
-- persisting employee --
Hibernate: call next value for hibernate_sequence
Hibernate: insert into Employee (department, name, id) values (?, ?, ?)
Employee persisted: Employee{id=1, name='Sara Dorsey', department='Admin'}
-- loading employee by id --
calling getReference().
getReference() already called
Hibernate: select employee0_.id as id1_0_0_, employee0_.department as departme2_0_0_, employee0_.name as name3_0_0_ from Employee employee0_ where employee0_.id=?
loaded employee name: Sara Dorsey

As seen in above output, the select statement is only fired when we first call Employee.getName().

getReference() vs find()

EntityManager.find() returns the fully initialized entity instance (except for the association configured with lazy fetch strategy), whereas EntityManager.getReference() methods returns uninitialized proxy of the entity.

Following example shows the difference between getReference() and find() methods when updating a field of the entity.

public class GetReferenceVsFindExample {

  public static void main(String[] args) {
      EntityManagerFactory emf =
              Persistence.createEntityManagerFactory("example-unit");
      try {
          persistEntity(emf);
          updateByReference(emf);
          updateByFinding(emf);
          allEmployeesQuery(emf);
      } finally {
          emf.close();
      }
  }
    .............
  private static void updateByReference(EntityManagerFactory emf) {
      System.out.println("-- updating employee by reference --");
      EntityManager em = emf.createEntityManager();
      Employee employee = em.getReference(Employee.class, 1);
      em.getTransaction().begin();
      System.out.println("-- setting new department --");
      employee.setDepartment("IT");
      System.out.println("-- commit --");
      em.getTransaction().commit();
      em.close();
  }

  private static void updateByFinding(EntityManagerFactory emf) {
      System.out.println("-- updating employee by finding --");
      EntityManager em = emf.createEntityManager();
      Employee employee = em.find(Employee.class, 1);
      em.getTransaction().begin();
      System.out.println("-- setting new name -- ");
      employee.setName("Diana");
      System.out.println("-- committing --");
      em.getTransaction().commit();
      em.close();
  }

  private static void allEmployeesQuery(EntityManagerFactory emf) {
      System.out.println("-- select all query --");
      EntityManager em = emf.createEntityManager();
      Query query = em.createQuery("select t from Employee t");
      List resultList = query.getResultList();
      resultList.forEach(System.out::println);
  }
}
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: create table Employee (id integer not null, department varchar(255), name varchar(255), primary key (id))
-- persisting employee --
Hibernate: call next value for hibernate_sequence
Hibernate: insert into Employee (department, name, id) values (?, ?, ?)
Employee persisted: Employee{id=1, name='Sara Dorsey', department='Admin'}
-- updating employee by reference --
-- setting new department --
Hibernate: select employee0_.id as id1_0_0_, employee0_.department as departme2_0_0_, employee0_.name as name3_0_0_ from Employee employee0_ where employee0_.id=?
-- commit --
Hibernate: update Employee set department=?, name=? where id=?
-- updating employee by finding --
Hibernate: select employee0_.id as id1_0_0_, employee0_.department as departme2_0_0_, employee0_.name as name3_0_0_ from Employee employee0_ where employee0_.id=?
-- setting new name --
-- committing --
Hibernate: update Employee set department=?, name=? where id=?
-- select all query --
Hibernate: select employee0_.id as id1_0_, employee0_.department as departme2_0_, employee0_.name as name3_0_ from Employee employee0_
Employee{id=1, name='Diana', department='IT'}

As seen in above output, in case of getReference() 'select' statement is fired when setting a new property value, whereas in case of find() method 'select' statement is fired during initialization.

Accessing detached proxy returned by getReference()

The properties of uninitialized proxy returned by getReference() method, must be access within EntityManager context.

public class GetReferenceAndDetachedEntityExample {

  public static void main(String[] args) {
      EntityManagerFactory emf = Persistence.createEntityManagerFactory("example-unit");
      try {
          persistEntity(emf);
          Employee employee = getEntityReference(emf);
          System.out.println("employee name " + employee.getName());
      } finally {
          emf.close();
      }
  }
    .............
  private static Employee getEntityReference(EntityManagerFactory emf) {
      EntityManager em = emf.createEntityManager();
      Employee employee = em.getReference(Employee.class, 1);
      em.close();
      return employee;
  }
}
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: create table Employee (id integer not null, department varchar(255), name varchar(255), primary key (id))
-- persisting employee --
Hibernate: call next value for hibernate_sequence
Hibernate: insert into Employee (department, name, id) values (?, ?, ?)
Employee persisted: Employee{id=1, name='Sara Dorsey', department='Admin'}
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:146)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:259)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:73)
at com.logicbig.example.Employee_$$_jvstd8d_0.getName(Employee_$$_jvstd8d_0.java)
at com.logicbig.example.GetReferenceAndDetachedEntityExample.main(GetReferenceAndDetachedEntityExample.java:15)

To fix above exception we need to merge the detached poxy entity:

public class MergingDetachedProxyEntity {

  public static void main(String[] args) {
      EntityManagerFactory emf = Persistence.createEntityManagerFactory("example-unit");
      try {
          persistEntity(emf);
          Employee employee = getEntityReference(emf);
          EntityManager em = emf.createEntityManager();
          employee = em.merge(employee);
          System.out.println("employee name " + employee.getName());
          em.close();
      } finally {
          emf.close();
      }
  }
    .............
  private static Employee getEntityReference(EntityManagerFactory emf) {
      EntityManager em = emf.createEntityManager();
      Employee employee = em.getReference(Employee.class, 1);
      em.close();
      return employee;
  }
}
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: create table Employee (id integer not null, department varchar(255), name varchar(255), primary key (id))
-- persisting employee --
Hibernate: call next value for hibernate_sequence
Hibernate: insert into Employee (department, name, id) values (?, ?, ?)
Employee persisted: Employee{id=1, name='Sara Dorsey', department='Admin'}
Hibernate: select employee0_.id as id1_0_0_, employee0_.department as departme2_0_0_, employee0_.name as name3_0_0_ from Employee employee0_ where employee0_.id=?
employee name Sara Dorsey

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

EntityManager.getReference() Example Select All Download
  • get-reference-example
    • src
      • main
        • java
          • com
            • logicbig
              • example
        • resources
          • META-INF

See Also