Spring Boot: JPA One-to-One Mapping

Introduction

When working with relational databases in Java, JPA (Java Persistence API) makes it easier to map relationships between entities. One of the most fundamental relationships is One-to-One mapping. In this blog, we’ll break it down in a way that makes it simple and enjoyable to learn!

By the end of this article, you’ll understand:

  • What One-to-One mapping is.
  • How to implement it in Spring Boot with JPA and Hibernate.
  • The different ways to set up the relationship.
  • Practical use cases for real-world applications.

Let’s dive in!


What is One-to-One Mapping?

A One-to-One relationship means that one entity is associated with exactly one other entity. For example:

  • A User can have exactly one Profile.
  • A Car can have exactly one Engine.

In database terms, this means that the primary key of one table is linked to the primary key of another table.

How Does One-to-One Mapping Work in JPA?

JPA provides the @OneToOne annotation to define this relationship. There are different ways to implement it, depending on how you design your database:

  1. Using a Foreign Key (Recommended)
  2. Using a Shared Primary Key
  3. Using a Join Table

Let’s see how we can implement these approaches in Spring Boot!


Implementing One-to-One Mapping in Spring Boot

1️⃣ One-to-One Mapping Using a Foreign Key (Recommended)

The most common way to set up a One-to-One relationship is by using a foreign key. Here’s how we do it:

Step 1: Create the User Entity

<span>import</span> <span>jakarta.persistence.*</span><span>;</span>
<span>@Entity</span>
<span>public</span> <span>class</span> <span>User</span> <span>{</span>
<span>@Id</span>
<span>@GeneratedValue</span><span>(</span><span>strategy</span> <span>=</span> <span>GenerationType</span><span>.</span><span>IDENTITY</span><span>)</span>
<span>private</span> <span>Long</span> <span>id</span><span>;</span>
<span>private</span> <span>String</span> <span>name</span><span>;</span>
<span>@OneToOne</span><span>(</span><span>cascade</span> <span>=</span> <span>CascadeType</span><span>.</span><span>ALL</span><span>)</span>
<span>@JoinColumn</span><span>(</span><span>name</span> <span>=</span> <span>"profile_id"</span><span>,</span> <span>referencedColumnName</span> <span>=</span> <span>"id"</span><span>)</span>
<span>private</span> <span>Profile</span> <span>profile</span><span>;</span>
<span>// Getters and Setters</span>
<span>}</span>
<span>import</span> <span>jakarta.persistence.*</span><span>;</span>

<span>@Entity</span>
<span>public</span> <span>class</span> <span>User</span> <span>{</span>
    <span>@Id</span>
    <span>@GeneratedValue</span><span>(</span><span>strategy</span> <span>=</span> <span>GenerationType</span><span>.</span><span>IDENTITY</span><span>)</span>
    <span>private</span> <span>Long</span> <span>id</span><span>;</span>
    <span>private</span> <span>String</span> <span>name</span><span>;</span>

    <span>@OneToOne</span><span>(</span><span>cascade</span> <span>=</span> <span>CascadeType</span><span>.</span><span>ALL</span><span>)</span>
    <span>@JoinColumn</span><span>(</span><span>name</span> <span>=</span> <span>"profile_id"</span><span>,</span> <span>referencedColumnName</span> <span>=</span> <span>"id"</span><span>)</span>
    <span>private</span> <span>Profile</span> <span>profile</span><span>;</span>

    <span>// Getters and Setters</span>
<span>}</span>
import jakarta.persistence.*; @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "profile_id", referencedColumnName = "id") private Profile profile; // Getters and Setters }

Enter fullscreen mode Exit fullscreen mode

Step 2: Create the Profile Entity

<span>import</span> <span>jakarta.persistence.*</span><span>;</span>
<span>@Entity</span>
<span>public</span> <span>class</span> <span>Profile</span> <span>{</span>
<span>@Id</span>
<span>@GeneratedValue</span><span>(</span><span>strategy</span> <span>=</span> <span>GenerationType</span><span>.</span><span>IDENTITY</span><span>)</span>
<span>private</span> <span>Long</span> <span>id</span><span>;</span>
<span>private</span> <span>String</span> <span>bio</span><span>;</span>
<span>private</span> <span>String</span> <span>website</span><span>;</span>
<span>// Getters and Setters</span>
<span>}</span>
<span>import</span> <span>jakarta.persistence.*</span><span>;</span>

<span>@Entity</span>
<span>public</span> <span>class</span> <span>Profile</span> <span>{</span>
    <span>@Id</span>
    <span>@GeneratedValue</span><span>(</span><span>strategy</span> <span>=</span> <span>GenerationType</span><span>.</span><span>IDENTITY</span><span>)</span>
    <span>private</span> <span>Long</span> <span>id</span><span>;</span>
    <span>private</span> <span>String</span> <span>bio</span><span>;</span>
    <span>private</span> <span>String</span> <span>website</span><span>;</span>

    <span>// Getters and Setters</span>
<span>}</span>
import jakarta.persistence.*; @Entity public class Profile { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String bio; private String website; // Getters and Setters }

Enter fullscreen mode Exit fullscreen mode

In the User entity, we used @OneToOne and @JoinColumn(name = "profile_id") to specify that the profile_id column in the User table acts as a foreign key pointing to the id column in the Profile table.


2️⃣ One-to-One Mapping Using a Shared Primary Key

In this approach, the primary key of one table is also used as the primary key of another table.

Update the Profile Entity

<span>@Entity</span>
<span>public</span> <span>class</span> <span>Profile</span> <span>{</span>
<span>@Id</span>
<span>private</span> <span>Long</span> <span>id</span><span>;</span>
<span>private</span> <span>String</span> <span>bio</span><span>;</span>
<span>private</span> <span>String</span> <span>website</span><span>;</span>
<span>@OneToOne</span>
<span>@MapsId</span>
<span>@JoinColumn</span><span>(</span><span>name</span> <span>=</span> <span>"id"</span><span>)</span>
<span>private</span> <span>User</span> <span>user</span><span>;</span>
<span>}</span>
<span>@Entity</span>
<span>public</span> <span>class</span> <span>Profile</span> <span>{</span>
    <span>@Id</span>
    <span>private</span> <span>Long</span> <span>id</span><span>;</span>
    <span>private</span> <span>String</span> <span>bio</span><span>;</span>
    <span>private</span> <span>String</span> <span>website</span><span>;</span>

    <span>@OneToOne</span>
    <span>@MapsId</span>
    <span>@JoinColumn</span><span>(</span><span>name</span> <span>=</span> <span>"id"</span><span>)</span>
    <span>private</span> <span>User</span> <span>user</span><span>;</span>
<span>}</span>
@Entity public class Profile { @Id private Long id; private String bio; private String website; @OneToOne @MapsId @JoinColumn(name = "id") private User user; }

Enter fullscreen mode Exit fullscreen mode

The @MapsId annotation ensures that the Profile entity shares the same primary key as User.


3️⃣ One-to-One Mapping Using a Join Table

Sometimes, you may want a third table to manage the relationship. In this case, JPA allows us to use @JoinTable.

Modify the User Entity

<span>@OneToOne</span><span>(</span><span>cascade</span> <span>=</span> <span>CascadeType</span><span>.</span><span>ALL</span><span>)</span>
<span>@JoinTable</span><span>(</span><span>name</span> <span>=</span> <span>"user_profile"</span><span>,</span>
<span>joinColumns</span> <span>=</span> <span>@JoinColumn</span><span>(</span><span>name</span> <span>=</span> <span>"user_id"</span><span>),</span>
<span>inverseJoinColumns</span> <span>=</span> <span>@JoinColumn</span><span>(</span><span>name</span> <span>=</span> <span>"profile_id"</span><span>))</span>
<span>private</span> <span>Profile</span> <span>profile</span><span>;</span>
<span>@OneToOne</span><span>(</span><span>cascade</span> <span>=</span> <span>CascadeType</span><span>.</span><span>ALL</span><span>)</span>
<span>@JoinTable</span><span>(</span><span>name</span> <span>=</span> <span>"user_profile"</span><span>,</span>
           <span>joinColumns</span> <span>=</span> <span>@JoinColumn</span><span>(</span><span>name</span> <span>=</span> <span>"user_id"</span><span>),</span>
           <span>inverseJoinColumns</span> <span>=</span> <span>@JoinColumn</span><span>(</span><span>name</span> <span>=</span> <span>"profile_id"</span><span>))</span>
<span>private</span> <span>Profile</span> <span>profile</span><span>;</span>
@OneToOne(cascade = CascadeType.ALL) @JoinTable(name = "user_profile", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "profile_id")) private Profile profile;

Enter fullscreen mode Exit fullscreen mode

This approach creates a user_profile table that holds the user_id and profile_id columns, acting as a bridge between User and Profile.


When to Use One-to-One Mapping?

One-to-One relationships are useful in scenarios where:
The related data is rarely accessed separately (e.g., user profile information).
You need data integrity with clear ownership (e.g., a unique identity document per user).
You want to simplify queries and avoid data duplication.


Conclusion

In this article, we explored:

  • What One-to-One mapping is.
  • Three ways to implement it in Spring Boot with JPA.
  • When to use each approach.

One-to-One mapping is a powerful way to structure your database relationships effectively. Mastering it will help you build better, more scalable applications!

Got questions or feedback? Drop a comment below! Happy coding!

原文链接:Spring Boot: JPA One-to-One Mapping

© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享
Misery can be caused by someone being just weak and indecisive.
一个人仅仅因为软弱无能或优柔寡断就完全可能招致痛苦
评论 抢沙发

请登录后发表评论

    暂无评论内容