How to change the @OneToOne shared primary key column name with JPA and Hibernate
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, we are going to see how you can change the @OneToOne
shared primary key column name when using JPA and Hibernate. This has been a recurrent theme when answering questions about Hibernate or during my High-Performance Java Persistence training.
As previously explained, the one-to-one database table relationship requires the Primary Key to be shared among the parent and the child tables.
Unfortunately, just adding the JPA @OneToOne
annotation in the child entity does not render a true one-to-one table relationship since a separate Foreign Key column will be used. Only when adding the @MapsId
annotation will the JPA one-to-one association map to a real one-to-one table relationship.
How to change the @OneToOne shared primary key column name with JPA and Hibernate@vlad_mihalceahttps://t.co/BoQsxJ6Lvm pic.twitter.com/kP5hgBRw1b
— Java (@java) April 10, 2019
Domain Model
Let’s assume we are using the following Post
and PostDetails
entities:
The Post
entity is the parent while the PostDetails
is the child entity and its associated Primary Key is also a Foreign Key to the parent table Primary Key.
The Post
entity is rather straightforward to map since it does not contain any association:
@Entity(name = "Post") @Table(name = "post") public class Post { @Id @GeneratedValue private Long id; private String title; //Getters and setters omitted for brevity }
The PostDetails
can be mapped as follows:
@Entity(name = "PostDetails") @Table(name = "post_details") public class PostDetails { @Id private Long id; @Column(name = "created_on") private Date createdOn; @Column(name = "created_by") private String createdBy; @OneToOne(fetch = FetchType.LAZY) @MapsId private Post post; public PostDetails() {} public PostDetails(String createdBy) { createdOn = new Date(); this.createdBy = createdBy; } //Getters and setters omitted for brevity }
Notice that we are using
FetchType.LAZY
explicitly since, by default, JPA usesFetchType.EAGER
for@OneToOne
and@ManyToOne
associations, and that’s very bad for performance.
When generating the schema with the hbm2ddl tool or if we create the schema manually and managing it with Flyway, Hibernate expects the following database table structure:
Notice that the Primary Key column name is called post_id
in the post_details
table, and this is not very nice since the Primary Key column name is called id
in the post
table.
@JoinColumn to the rescue
To fix this issue, we just have to add a @JoinColumn
annotation to the @OneToOne
association in the PostDetails
entity:
@OneToOne @MapsId @JoinColumn(name = "id") private Post post;
This way, Hibernate will either generate or expect the following database tables:
Awesome, right?
If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.
Conclusion
It’s a little bit unfortunate that, by default, the @MapsId
annotation does not use the entity identifier name to match the underlying table Primary Key column. However, using the @JoinColumn
annotation a straightforward way to fix this issue.
