How do find and getReference EntityManager methods work when using JPA and Hibernate

Are you struggling with performance issues in your Spring, Jakarta EE, or Java EE application?

What if there were a tool that could automatically detect what caused performance issues in your JPA and Hibernate data access layer?

Wouldn’t it be awesome to have such a tool to watch your application and prevent performance issues during development, long before they affect production systems?

Well, Hypersistence Optimizer is that tool! And it works with Spring Boot, Spring Framework, Jakarta EE, Java EE, Quarkus, Micronaut, or Play Framework.

So, rather than fixing performance issues in your production system on a Saturday night, you are better off using Hypersistence Optimizer to help you prevent those issues so that you can spend your time on the things that you love!

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.

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.

Transactions and Concurrency Control eBook

2 Comments on “How do find and getReference EntityManager methods work when using JPA and Hibernate

  1. How does the #getReference method compare with simply initializing a new Post instance and setting only the ID, e.g.

    Post post = new Post();
    post.setId(1L);
    comment.setPost(post);
    

    instead of:

    Post post = entityManager.getReference(Post.class, 1L);
    comment.setPost(post);
    

    It seems identical queries are generated in both cases? Are there reasons to prefer one over the other?

    • GetReference gives you a Proxy that can be initialized. A dummy object that has only the id will allow you to set the FK column, but if you then pass it further on and some business logic makes a decision based on the fact that one of properties is null, then you will have a flaw in your logic.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.