avatarIvan Franchin

Summary

The provided content discusses the implementation of a one-to-one relationship with a shared primary key in JPA/Hibernate, illustrating the concept with code examples and explaining the database schema generation.

Abstract

The article delves into the complexities of JPA relationships, focusing on the one-to-one association with a shared primary key. It uses a GitHub repository, spring-data-jpa-relationships, to provide concrete examples through the Person and PersonDetail entities. The author explains the annotations and strategies used to map the entities in a bidirectional relationship where the Person entity's ID is also the primary key for the PersonDetail entity. The article also demonstrates how JPA/Hibernate generates the corresponding tables in the database, ensuring that the primary key of PersonDetail is both a primary key and a foreign key referencing the Person table. The goal is to enhance the reader's understanding of JPA relationships and to offer a resource that can be beneficial to developers.

Opinions

  • The author acknowledges personal difficulties in correctly implementing JPA relationships and aims to clarify these concepts for others.
  • The use of the @MapsId annotation is emphasized as a key feature for mapping the primary key of the associated entity as the foreign key in the current entity, leading to a more efficient database schema.
  • Lazy loading is recommended for the associated entities to optimize performance by deferring the loading of related objects until they are explicitly accessed.
  • The author expresses hope that both the articles in the series and the accompanying GitHub repository will be found helpful by developers working with JPA/Hibernate.
  • The article encourages reader engagement through clapping, sharing, following on social media, and subscribing to a newsletter, indicating a desire for community interaction and feedback.

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

Photo by Kirill Balobanov on Unsplash

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.
Jpa
Spring Data Jpa
Technology
Database
Relational Databases
Recommended from ReadMedium