Hibernate Tip: How to lazily load one-to-one associations
Take your skills to the next level!
The Persistence Hub is the place to be for every Java developer. It gives you access to all my premium video courses, monthly Java Persistence News, monthly coding problems, and regular expert sessions.
Hibernate Tips is a series of posts in which I describe a quick and easy solution for common Hibernate questions. If you have a question for a future Hibernate Tip, please post a comment below.
Question:
I modeled a one-to-one association and activated lazy loading for it. But it doesn’t work. How do I tell Hibernate to fetch a one-to-one association lazily?
Solution:
Configuring lazy loading for one-to-one associations is not as easy as it is for other associations. For all other association types, you just need to set the FetchType to FetchType.LAZY. Hibernate will then wait for you to use the relationship before it loads the associated entities.
Unfortunately, that’s not the case for one-to-one associations. It also depends on the mapping of the relationship and the Hibernate version you are using.
Most common mapping and its problems
A lot of developers model a one-to-one association using independent primary key columns for both tables and an additional foreign key column in one of the tables. That not only requires an additional database column; it also negatively affects your entity mapping. I will show you a more efficient way to model the association at the end of this article.
The entity that represents the table containing the foreign key column is called the owning side of the association. On this entity, Hibernate supports lazy loading as expected. You just have to set the fetch attribute of the @OneToOne association to FetchType.LAZY.
@Entity public class Manuscript { @Id @GeneratedValue private Long id; private byte[] file; @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "fk_book") private Book book; ... }
But if you model this as a bidirectional association, you will quickly recognize that Hibernate always fetches the other end of the association eagerly.
@Entity public class Book { @Id @GeneratedValue private Long id; @OneToOne(mappedBy = "book", fetch = FetchType.LAZY) private Manuscript manuscript; ... }
That’s because Hibernate needs to know if it shall initialize the manuscript attribute with null or a proxy class. It can only find that out, by querying the manuscript table to find a record that references this Book entity. The Hibernate team decided that if they have to query the manuscript table anyways, it’s best to fetch the associated entity eagerly.
With some Hibernate versions, you can set the optional attribute of the @OneToOne annotation to false to avoid the eager fetching. Hibernate then always initializes the manuscript attribute with a proxy object. Unfortunately, this doesn’t work for all Hibernate version. If it works with your Hibernate version, you need to be prepared that it might change with future updates.
@Entity public class Book { @Id @GeneratedValue private Long id; @OneToOne(mappedBy = "book", fetch = FetchType.LAZY, optional = false) private Manuscript manuscript; ... }
Most efficient mapping of a one-to-one association
You can avoid all these problems and get rid of the foreign key column by using the same primary key value for both associated entities. You can do that by annotating the owning side of the association with @MapsId.
@Entity public class Manuscript { @Id private Long id; @OneToOne @MapsId @JoinColumn(name = "id") private Book book; ... }
The shared primary key allows you to model the relationship as a unidirectional one. You no longer need the referencing side of the association. If you have a Book entity object, you also know the primary key value of the associated Manuscript entity. So, you can simply use the find method on your EntityManager to fetch the Manuscript.
Book b = em.find(Book.class, 100L); Manuscript m = em.find(Manuscript.class, b.getId());
Learn more:
If you are interested in one-to-one association mappings, you should also read the following articles:
- Ultimate Guide – Association Mappings with JPA and Hibernate
- Hibernate Tips: How to Share the Primary Key in a One-to-One Association
- Hibernate Tip: Map a bidirectional one-to-one association with a shared composite primary key
Hibernate Tips Book
Get more recipes like this one in my new book Hibernate Tips: More than 70 solutions to common Hibernate problems.
It gives you more than 70 ready-to-use recipes for topics like basic and advanced mappings, logging, Java 8 support, caching, and statically and dynamically defined queries.