Understanding Relationships in JPA: One-to-One with Shared Primary Key
Discussing “One-to-One with Shared Primary Key” and examining how JPA/Hibernate generates the corresponding tables
As mentioned in the introductory article of this series, I’ve faced difficulties in implementing JPA relationships correctly in my applications.
To enhance my understanding of them, I developed a GitHub repository called spring-data-jpa-relationships, containing simple examples for each relationship type.
In this series of articles, we will demonstrate each JPA relationship type by presenting the necessary code to map the entities and examining how JPA/Hibernate generates the tables.
I hope you find these articles and the GitHub repository helpful.
Today, we will talk about “One-to-One with Shared Primary Key”. So, let’s get started!
One-to-One with Shared Primary Key
In this example, we associate the Person and PersonDetail entities. A person can have only one person detail, and a person detail belongs to only one person. Additionally, we intend to utilize the Person’s ID as the primary key for the PersonDetail entity.
Below is the desired database model we aim to achieve.

This is the complete and final code. Next, we will explain it in detail.
@Entity
@Table(name = "persons")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@OneToOne(mappedBy = "person", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private PersonDetail personDetail;
@Column(nullable = false)
private String name;
// getters and setters
}
@Entity
@Table(name = "person_details")
public class PersonDetail {
@Id
private Long id;
@OneToOne(fetch = FetchType.LAZY)
@MapsId
private Person person;
@Column(nullable = false)
private String description;
// getters and setters
}We create two classes, Person and PersonDetail, and use the @Entity annotation to indicate that these classes correspond to database tables.
By default, the table name would be the same as the entity name, but we want them to be in plural form. We can achieve this by using the @Table annotation, where we specify the desired table name. For the Person entity, we use “persons”, and for the PersonDetail entity, we use “person_details”.
Next, we add the following fields to the Person class: id of type Long and name of type String. Similarly, we add the following fields to the PersonDetail class: id of type Long and description of type String.
To ensure that the name field in the Person entity and the description field in the PersonDetail entity cannot have null values, we add the @Column(nullable = false) annotation to it. This annotation tells JPA that the corresponding database column should be defined as NOT NULL.
For the id field in the Person entity, we add the annotations: @Id and @GeneratedValue(strategy = GenerationType.SEQUENCE). The @Id annotation indicates that the field is the primary key for the entity, while the @GeneratedValue annotation specifies the strategy for generating the ID.
In order to set the primary key for the PersonDetail entity, we use the @Id annotation on the id field and the @MapsId annotation on the person field. The @MapsId annotation is used to map the primary key of the associated entity (Person) as the foreign key in the current entity (PersonDetail). This means that the primary key values of Person and PersonDetail will be the same, which allows for a simpler and more efficient database schema.
To establish the one-to-one relationship, we add the personDetail field to the Person class and annotate it with the @OneToOne annotation. Similar to the person field in the PersonDetail class.
The mappedBy used in the @OneToOne annotation on the personDetail field in the Person class indicates that the relationship between Person and PersonDetail is bidirectional, and that the owning side of the relationship is the PersonDetail entity. The mappedBy attribute specifies the name of the field in the PersonDetail class that owns the relationship, which in this case is the person field.
The fetch = FetchType.LAZY is used to specify that the PersonDetail object (in Person class) or the Person object (in PersonDetail class) should be loaded lazily, which means it won’t be loaded from the database until it’s actually accessed.
The cascade = CascadeType.ALL is used to specify that all cascading operations should be applied to the PersonDetail entity when the Person entity is persisted, updated, or deleted. This means that if a Person is deleted, the associated PersonDetail will also be deleted.
Lastly, let’s describe the tables in Postgres terminal.
jparelationshipsdb=# \d persons
Table "public.persons"
Column | Type | Collation | Nullable | Default
--------+------------------------+-----------+----------+---------
id | bigint | | not null |
name | character varying(255) | | not null |
Indexes:
"persons_pkey" PRIMARY KEY, btree (id)
Referenced by:
TABLE "person_details" CONSTRAINT "fk57xp5a9x3kwiu38878hgs67dw" FOREIGN KEY (person_id) REFERENCES persons(id)
jparelationshipsdb=# \d person_details
Table "public.person_details"
Column | Type | Collation | Nullable | Default
-------------+------------------------+-----------+----------+---------
description | character varying(255) | | not null |
person_id | bigint | | not null |
Indexes:
"person_details_pkey" PRIMARY KEY, btree (person_id)
Foreign-key constraints:
"fk57xp5a9x3kwiu38878hgs67dw" FOREIGN KEY (person_id) REFERENCES persons(id)The persons table has the id column as primary key and the person_details table has the person_id column as Primary Key. The same column is a Foreign Key to the id column in the persons table.
Conclusion
In this series of articles, we are exploring the four different types of relationships in JPA: one-to-one, one-to-many / many-to-one, and many-to-many. By presenting simple examples for each type of relationship and providing necessary code to map the entities, we hope to help developers to gain a better understanding of the JPA relationships.
Support and Engagement
If you enjoyed this article and would like to show your support, please consider taking the following actions:
- 👏 Engage by clapping, highlighting, and replying to my story. I’ll be happy to answer any of your questions;
- 🌐 Share my story on Social Media;
- 🔔 Follow me on: Medium | LinkedIn | Twitter | GitHub;
- ✉️ Subscribe to my newsletter, so you don’t miss out on my latest posts.




