Learn to get the proxy reference of an entity using the EntityManager#getReference() method and it’s difference with find() method in runtime.
1. EntityManager.getReference() Method
We know that in hibernate lazy loading can be done by specifying “fetch= FetchType.LAZY
” in hibernate mapping annotations. In given example, competition will loaded lazily when the getCompetition() accessor will be invoked by the application code.
@Entity public class Nomination { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @ManyToOne(fetch = FetchType.LAZY) private Competition competition; //Getters and setters are hidden for brevity }
@Entity
public class Competition {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
//Getters and setters are hidden for brevity
}
This kind of lazy loading applies to cases when we are defining the mapping between two entities where we apply one of the association mapping annotations.
But, what if we want to lazy load the enclosing entity itself we can use the getReference() API. It returns only returns an entity Proxy which only has the identifier set. If we access the other fields in the Proxy, the associated SQL statement will be triggered as long as the EntityManager
is still open.
For example, if we want to persist a new Nomination for an existing Competition then we don’t need to fetch the Competition entity from the database. We only need the reference of an existing Competition.
Competition competitionRef = em.getReference(Competition.class, 1L);
Nomination nomination = new Nomination();
nomination.setName("Test");
nomination.setCompetition(competitionRef);
em.persist(nomination);
Now check the SQL queries executed for above example. No SQL SELECT query has been executed.
Hibernate: insert into Nomination (id, competition_id, name) values (default, ?, ?)
The getReference() is an excellent choice where we don’t need to load the entity and only need the entity reference with the primary id field set to it. This helps in situations where we need the entity just for setting the foreign key while executing the persist() operation.
Be careful in cases where the primary key may not exist. In these cases, persistence context will not throw any exception until we access one of the fields in the entity. If primary key is absent from database while accessing a field from reference entity, we will get the ConstraintViolationException
exception in runtime.
For example, if Competition id 2 does not exist in the database then while persisting the nomination we will get Referential integrity constraint violation error.
//No error in this statement
Competition competitionRef = em.getReference(Competition.class, 2L);
Nomination nomination = new Nomination();
nomination.setName("Test");
nomination.setCompetition(competitionRef);
//Error comes while accessing competition
em.persist(nomination);
Caused by: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException:
Referential integrity constraint violation: "FK7F1QFQ1Y14WSAGUOTXQRFO9JK:
PUBLIC.NOMINATION FOREIGN KEY(COMPETITION_ID)
REFERENCES PUBLIC.COMPETITION(ID) (CAST(2 AS BIGINT))"; SQL statement:
insert into Nomination (id, competition_id, name) values (default, ?, ?) [23506-212]
2. Difference with find() Method
The find() method works just opposite to getReference() method and when we fetch an entity the associated SQL
select query is executed at the same time. In other words, the find() method returns a fully initialized entity.
To understand the difference, we are using the previous example and replacing the getReference() call with find().
Competition competitionRef = em.find(Competition.class, 1L);
Nomination nomination = new Nomination();
nomination.setName("Test");
nomination.setCompetition(competitionRef);
em.persist(nomination);
Notice that the SELECT query has also been executed.
Hibernate: select c1_0.id,c1_0.title from Competition c1_0 where c1_0.id=?
Hibernate: insert into Nomination (id, competition_id, name) values (default, ?, ?)
Also the find() API returns null when the primary key does not exist in the database. It does not throw any exception.
3. Conclusion
In this Jakarta persistence tutorial, we learned to use the EntityManager‘s getReference() and find() APIs and their differences. We also learned about their behavior when the primary identifies does not exist in the database.
Happy Learning !!