How do find and getReference EntityManager methods work when using JPA and Hibernate
Imagine having a tool that can automatically detect JPA and Hibernate performance issues. Hypersistence Optimizer is that tool!
Introduction
While doing my High-Performance Java Persistence training, I realized that not all developers are familiar with the getReference
method of the JPA EntityManager
and most of them use find
almost exclusively.
In this article, we are going to see the difference between the find
and getReference
method so that it’s clear when to apply them depending on the underlying use case.
See the difference between the find and getReference method in JPA and #Hibernate - @vlad_mihalcea https://t.co/vfnBexGpOt pic.twitter.com/xMqeh8Lf2f
— Java (@java) July 3, 2018
Domain Model
For the next examples, consider we have a parent Post
entity and a child PostComment
which has a @ManyToOne
association back to its parent:
The Post
entity is mapped as follows:
@Entity(name = "Post") @Table(name = "post") public class Post { @Id private Long id; private String title; //Getters and setters omitted for brevity }
And the PostComment
like this:
@Entity(name = "PostComment") @Table(name = "post_comment") public class PostComment { @Id @GeneratedValue private Long id; private String review; @ManyToOne(fetch = FetchType.LAZY) private Post post; //Getters and setters omitted for brevity }
The reason why the @ManyToOne
association uses the FetchType.LAZY
attribute is because the default FetchType.EAGER
is very bad for performance.
Now, let’s assume we have the following Post
entity in our database:
Post post = new Post(); post.setId(1L); post.setTitle("High-Performance Java Persistence"); entityManager.persist(post);
The EntityManager find method
If you want to fetch an entity so that the associated SQL
select query is executed right away, you need to use the find
method of the current EntityManager
:
Post post = entityManager.find(Post.class, 1L); assertEquals("High-Performance Java Persistence", post.getTitle());
If you inspect the query log, you can see the SQL query being executed by the find
method call:
SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 1
If you need the Post
entity to be fetched because you need to access its properties, then the find
method is the right one to call.
However, let’s assume we want to create a PostComment
and need the Post
entity in order to set the underlying post_id
Foreign Key.
If we use the find
method like in the following example:
PostComment comment = new PostComment(); comment.setReview("Just awesome!"); Post post = entityManager.find(Post.class, 1L); comment.setPost(post); entityManager.persist(comment);
Hibernate will generate the following SQL statements:
SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 1 INSERT INTO post_comment (post_id, review, id) VALUES (1, 'Just awesome!', 1)
The SELECT query is useless this time because we don’t need the Post
entity to be fetched. We only want to set the underlying post_id
Foreign Key column.
The EntityManager getReference method
To fix the previous issue, we can use the getReference
method:
PostComment comment = new PostComment(); comment.setReview("Just awesome!"); Post post = entityManager.getReference(Post.class, 1L); comment.setPost(post); entityManager.persist(comment);
And, this time, Hibernate will issue just the INSERT statement:
INSERT INTO post_comment (post_id, review, id) VALUES (1, 'Just awesome!', 1)
Unlike find
, the getReference
only returns an entity Proxy which only has the identifier set. If you access the Proxy, the associated SQL statement will be triggered as long as the EntityManager
is still open.
However, in this case, we don’t need to access the entity Proxy. We only want to propagate the Foreign Key to the underlying table record so loading a Proxy is sufficient for this use case.
When loading a Proxy, you need to be aware that a LazyInitializationException
can be thrown if you try to access the Proxy reference after the EntityManager is closed. For more details about handling the LazyInitializationException
, check out this article.
If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.
Conclusion
Understanding what operations are associated with every entity state transition is of paramount importance when using JPA and Hibernate.
Therefore, when you need to set @ManyToOne
or @OneToOne
relationships, the getReference
method is much more efficient if you don’t need to fetch the associated parent record.

Thank you for the great article, I have a question regarding the getReference behavior when used to update the entity.
Does getReference actually loads the entity if I update the reference field value within the transaction while it is being managed and then commit the transaction, for what I have read it should, but, I tried writing unit tests and seeing results for myself, and it only has gotten me more confused – since I did not see any select queries performed by hibernate, but if update the field value to the same one the field has had before, it does not log the update query either – and the only way for hibernate to know this is to load the entity from the db.
The
getReference
method is only needed if you want to set a parent association without initializing it.You should never fetch a Proxy so that you later update it. The moment you call a setter on the Proxy, the Proxy will be initialized using a SELECT query.
Ok, thank you. This is a bit disappointing though, do you know is there a way to actually implement the behaviour like this – lazy load the entity from db and update the fields of the entity without ever fetching it from the db.
I know I could just write the hql queries but it seems like a bad solution from the code maintainability standpoint, since it would require new query for each field.
It’s not dissapointing at all. The way it works ensures that an UPDATE is only executed if necessary. Also, this design allows you to prevent lost updates when using optimistic locking as well.
There is a solution to your problem. Use the Hibernate
Session
update
while also enabling@DynamicUpdate
on the entity so that you can provide a new entity Java Object that requires only the properties to be updated to be set.I’m Using Hibernate as JPA implementation, but this approach
comment.setPost(new Post(1L));
doesn’t work for me. It’s sets post field as null after calling the merge() method of the entity manager.
Is there any option or documentation about this behavior?
Most likely, there’s no Post with the id value of 1 in the DB. That’s why merge sets it to null.
I wonder what is the benefit of using getReference() over manually setting the id in a new Post entity:
comment.setPost(new Post(1L));
entityManager.persist(comment);
JPA standard compliance is the reason. Only Hibernate supports that trick.