ManyToOne JPA and Hibernate association best practices - Vlad Mihalcea (2024)

Last modified:

Follow @vlad_mihalcea

Imagine having a tool that can automatically detect JPA and Hibernate performance issues. Wouldn’t that be just awesome?

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

So, enjoy spending your time on the things you love rather than fixing performance issues in your production system on a Saturday night!

Introduction

In this article, I’m going to show you what is the best way to map a ManyToOne association when using JPA and Hibernate.

Since the @ManyToOne association is the most common relationship, knowing how to map it properly will have a significant impact on application performance.

A one-to-many is the most common relationship in database tables@vlad_mihalcea explains the best way to map ManyToOne associations when using JPA and Hibernatehttps://t.co/M1U9fwdTdo pic.twitter.com/sQ4yt8aMQ7

— SQL Daily (@sqldaily) May 14, 2020

Table relationships

As explained in this article, there are three table relationship types:

  • one-to-many
  • one-to-one
  • many-to-many

The one-to-many table relationship looks as follows:

ManyToOne JPA and Hibernate association best practices - Vlad Mihalcea (1)

The post_comment table has a post_id column that has a Foreign Key relationship with the id column in the parent post table. The post_id Foreign Key column drives the one-to-many table relationship.

The @ManyToOne JPA and Hibernate association

When using JPA and Hibernate, the @ManyToOne annotation allows you to map a Foreign Key column:

@Entity(name = "PostComment")@Table(name = "post_comment")public class PostComment { @Id @GeneratedValue private Long id; private String review; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "post_id") private Post post; //Getters and setters omitted for brevity}

The @JoinColumn annotation allows you to specify the Foreign Key column name. In our example, we can omit the @JoinColumn annotation since, by default, the Foreign Key column name is assumed to be formed by joining the @ManyToOne property and the parent entity identifier via the _ character.

Also, it’s very important to set the fetch strategy explicitly to FetchType.LAZY. By default, @ManyToOne associations use the FetchType.EAGER strategy, which can lead to N+1 query issues or fetching more data than necessary.

For more details about why you should avoid using FetchType.EAGER, check out this article.

Persisting a ManyToOne association with JPA and Hibernate

Let’s assume we have previously persisted a parent Post entity:

entityManager.persist( new Post() .setId(1L) .setTitle("High-Performance Java Persistence"));

A common mistake developers do when persisting child entities is to fetch the parent entity using find:

Post post = entityManager.find(Post.class, 1L);entityManager.persist( new PostComment() .setId(1L) .setReview("Amazing book!") .setPost(post));

Or, if you’re using Spring Data JPA, the same issue happens when using the findById method of the JpaRepository:

Post post = postRepository.findById(1L);commentRepository.save( new PostComment() .setId(1L) .setReview("Amazing book!") .setPost(post));

When persisting the PostComment entity while using the find method, Hibernate will execute the following SQL statements:

SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id=1INSERT INTO post_comment ( post_id, review, id) VALUES ( 1, 'Amazing book!', 1)

The SELECT query is not needed since we are not interested in fetching the Post entity. All we want is to set the post_id Foreign Key column.

So, instead of find, you need to use getReference:

Post post = entityManager.getReference(Post.class, 1L);entityManager.persist( new PostComment() .setId(1L) .setReview("Amazing book!") .setPost(post));

Or the getReferenceById method if you’re using Spring Data JPA:

Post post = postRepository.getReferenceById(1L);commentRepository.save( new PostComment() .setId(1L) .setReview("Amazing book!") .setPost(post));

It’s unfortunate that the JpaRepository method is called getReferenceById and not getProxyById, as it would be much easier for developers to guess its purpose.

For more details about the findById Spring Data JPA Anti-Pattern, check out this article.

Now, Hibernate doesn’t need to execute the SELECT statement:

INSERT INTO post_comment ( post_id, review, id) VALUES ( 1, 'Amazing book!', 1)

YouTube Video

I also published a YouTube video about the @ManyToOne association, so enjoy watching it if you’re interested in this topic.

Fetching a ManyToOne association with JPA and Hibernate

Assuming you are using the FetchType.LAZY strategy, when fetching the PostComment entity and accessing the post @ManyToOne association:

PostComment comment = entityManager.find(PostComment.class, 1L);LOGGER.info( "The post '{}' got the following comment '{}'", comment.getPost().getTitle(), comment.getReview());

Hibernate is going to trigger a secondary SELECT statement:

SELECT pc.id AS id1_1_0_, pc.post_id AS post_id3_1_0_, pc.review AS review2_1_0_ FROM post_comment pc WHERE pc.id = 1SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 1The post 'High-Performance Java Persistence' got the following comment 'Amazing book!'

To avoid the secondary SELECT query, you need to fetch the post @ManyToOne association using the JOIN FETCH directive:

PostComment comment = entityManager.createQuery(""" select pc from PostComment pc join fetch pc.post where pc.id = :id """, PostComment.class).setParameter("id", 1L).getSingleResult();LOGGER.info( "The post '{}' got the following comment '{}'", comment.getPost().getTitle(), comment.getReview());

Now, Hibernate executes a single SQL query to fetch both the child and parent entities:

SELECT pc.id AS id1_1_0_, p.id AS id1_0_1_, pc.post_id AS post_id3_1_0_, pc.review AS review2_1_0_, p.title AS title2_0_1_ FROM post_comment pcINNER JOIN post p ON pc.post_id = p.id WHERE pc.id = 1The post 'High-Performance Java Persistence' got the following comment 'Amazing book!'

The JOIN FETCH directive can help you avoid getting a LazyInitializationException if you try to access a lazy @ManyToOne association after the Persistence Context is closed.

If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.

ManyToOne JPA and Hibernate association best practices - Vlad Mihalcea (2)ManyToOne JPA and Hibernate association best practices - Vlad Mihalcea (3)ManyToOne JPA and Hibernate association best practices - Vlad Mihalcea (4)

Conclusion

When using JPA and Hibernate, it’s very important to know how to map and use the ManyToOne association since it’s the most common relationship.

Using FetchType.LAZY, by default, is a very useful practice, as the fetching strategy should be set on a per-use case basis, not globally.

Follow @vlad_mihalcea

ManyToOne JPA and Hibernate association best practices - Vlad Mihalcea (5)

Related

ManyToOne JPA and Hibernate association best practices - Vlad Mihalcea (2024)

References

Top Articles
Where to Stay in Louisville, Kentucky (and Where NOT to): a Local's Guide
Ranni Isn't In Her Tower
Funny Roblox Id Codes 2023
Golden Abyss - Chapter 5 - Lunar_Angel
Www.paystubportal.com/7-11 Login
Joi Databas
DPhil Research - List of thesis titles
Shs Games 1V1 Lol
Evil Dead Rise Showtimes Near Massena Movieplex
Steamy Afternoon With Handsome Fernando
Which aspects are important in sales |#1 Prospection
Detroit Lions 50 50
18443168434
Newgate Honda
Zürich Stadion Letzigrund detailed interactive seating plan with seat & row numbers | Sitzplan Saalplan with Sitzplatz & Reihen Nummerierung
Grace Caroline Deepfake
978-0137606801
Nwi Arrests Lake County
Justified Official Series Trailer
London Ups Store
Committees Of Correspondence | Encyclopedia.com
Pizza Hut In Dinuba
Jinx Chapter 24: Release Date, Spoilers & Where To Read - OtakuKart
How Much You Should Be Tipping For Beauty Services - American Beauty Institute
Free Online Games on CrazyGames | Play Now!
Sizewise Stat Login
VERHUURD: Barentszstraat 12 in 'S-Gravenhage 2518 XG: Woonhuis.
Jet Ski Rental Conneaut Lake Pa
Unforeseen Drama: The Tower of Terror’s Mysterious Closure at Walt Disney World
Ups Print Store Near Me
C&T Wok Menu - Morrisville, NC Restaurant
How Taraswrld Leaks Exposed the Dark Side of TikTok Fame
University Of Michigan Paging System
Random Bibleizer
10 Best Places to Go and Things to Know for a Trip to the Hickory M...
Black Lion Backpack And Glider Voucher
Gopher Carts Pensacola Beach
Duke University Transcript Request
Lincoln Financial Field, section 110, row 4, home of Philadelphia Eagles, Temple Owls, page 1
Jambus - Definition, Beispiele, Merkmale, Wirkung
Ark Unlock All Skins Command
Craigslist Red Wing Mn
D3 Boards
Jail View Sumter
Nancy Pazelt Obituary
Birmingham City Schools Clever Login
Thotsbook Com
Funkin' on the Heights
Vci Classified Paducah
Www Pig11 Net
Ty Glass Sentenced
Latest Posts
Article information

Author: Delena Feil

Last Updated:

Views: 5514

Rating: 4.4 / 5 (65 voted)

Reviews: 88% of readers found this page helpful

Author information

Name: Delena Feil

Birthday: 1998-08-29

Address: 747 Lubowitz Run, Sidmouth, HI 90646-5543

Phone: +99513241752844

Job: Design Supervisor

Hobby: Digital arts, Lacemaking, Air sports, Running, Scouting, Shooting, Puzzles

Introduction: My name is Delena Feil, I am a clean, splendid, calm, fancy, jolly, bright, faithful person who loves writing and wants to share my knowledge and understanding with you.