Contents

One-to-One Relationship in JPA

In this JPA (Java Persistence API) article, we will discuss different ways of achieving one-to-one mappings in JPA.

1 What is a One-to-One Relationship?

A one-to-one relationship in JPA is a type of association where each instance of an entity is directly linked to a single, unique instance of another entity. Formally, for every row in the first table, there is at most one corresponding row in the second table, and vice versa. This ensures that the relationship between the two entities is exclusive and bidirectional or unidirectional, depending on the mapping configuration.

To make it even clearer, consider a more naive and relatable example: think about a Person and their Passport. In most countries, each person can have only one valid passport at a time, and each passport is issued to only one person. This means there is a strict one-to-one relationship between the Person and Passport entities.

Here’s how this relationship might look in a real-world scenario:

  • Person: Has attributes like id, name, dateOfBirth, etc.
  • Passport: Has attributes like passportNumber, issueDate, expiryDate, etc.

Each Person is linked to exactly one Passport, and each Passport is linked to exactly one Person. In a database, this can be represented by having a foreign key in the Passport table that references the Person table, or vice versa.

In summary, a one-to-one relationship in JPA is used to model scenarios where two entities are uniquely associated with each other, ensuring data consistency and integrity.

2 Achieving One-to-One Mapping in JPA

There are three different ways of achieving one-to-one mapping in JPA:

  1. Foreign Key Method In this approach, one entity contains a foreign key that references the primary key of the other entity.
  2. Shared Primary Key Method Here, both entities share the same primary key value, establishing a direct one-to-one correspondence.
  3. Join Table Method This method uses a separate join table to maintain the association between the two entities.

We will explore each of these methods in detail in the following sections. For better understanding, we will use the example of a Student and their Marksheet. In this scenario, each student has exactly one marksheet, and each marksheet belongs to exactly one student. This forms a classic one-to-one relationship, where the Student entity is uniquely associated with the Marksheet entity. Throughout the following sections, we will demonstrate how to implement this relationship using different mapping strategies in JPA.

2.1 Foreign Key Method

This method involves using a foreign key in one table that references the primary key in another table.

/posts/one-to-one-relationship-in-jpa/images/foreignkey-method/erdiagram.png
fig : ER diagram for foreign key-based one-to-one mapping.

In our example, the student table will have a column named marksheet_id, which acts as a foreign key referencing the id column in the mark_sheet table. The mark_sheet table itself contains columns such as id, english, and physics to store the marks for each subject.

/posts/one-to-one-relationship-in-jpa/images/foreignkey-method/student-table.png /posts/one-to-one-relationship-in-jpa/images/foreignkey-method/marksheet-table.png
Student Table Marksheet Table

Here is how you can implement the one-to-one relationship using the Foreign Key Method in JPA:

@Data
@Entity
public class MarkSheet {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private Integer physics;
    private Integer english;

    @OneToOne(mappedBy = "markSheet")
    private Student student;
}

@Getter
@Setter
@Entity
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "marksheet_id", referencedColumnName = "id")
    private MarkSheet markSheet;
}

In this example:

  • The Student entity has a markSheet field annotated with @OneToOne and @JoinColumn, indicating that the marksheet_id column in the student table is a foreign key referencing the id column in the mark_sheet table.
  • The MarkSheet entity also uses the @OneToOne annotation, with the mappedBy attribute to indicate that the relationship is managed by the markSheet field in the Student entity.
  • This setup creates a bidirectional relationship, meaning both entities (Student and MarkSheet) are aware of each other and can access each other’s data directly through their respective fields. In a bidirectional one-to-one relationship, you can navigate from Student to MarkSheet and also from MarkSheet to Student, providing flexibility when querying or manipulating the associated entities.

This setup ensures that each student is associated with exactly one marksheet, and each marksheet belongs to exactly one student.

2.2 Shared Primary Key Method

In the Shared Primary Key Method, both entities use the same primary key value to establish a one-to-one relationship. This means the primary key of one entity is also a foreign key referencing the primary key of the other entity. This approach tightly couples the lifecycle of the two entities, ensuring that the existence of one directly depends on the other.

/posts/one-to-one-relationship-in-jpa/images/shared-primarykey-method/erdiagram.png
fig : ER diagram for shared primary key-based one-to-one mapping.

In our example, instead of creating an id primary key column in the mark_sheet table, we are using the primary key column (student_id) which corresponds to the id field in the student table. It is illustrated below:

/posts/one-to-one-relationship-in-jpa/images/shared-primarykey-method/student-table.png /posts/one-to-one-relationship-in-jpa/images/shared-primarykey-method/marksheet-table.png
Student Table Marksheet Table

Here is how you can implement the shared primary key one-to-one relationship in JPA:

@Getter
@Setter
@Entity
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
  
    @OneToOne(mappedBy = "student", cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    private MarkSheet markSheet;
}

@Data
@Entity
public class MarkSheet {
    @Id
    @Column(name = "student_id")
    private Integer id;
    private Integer physics;
    private Integer english;
  
    @OneToOne
    @JoinColumn(name = "student_id")
    @MapsId
    private Student student;
}

Here, we have used @PrimaryKeyJoinColumn which indicates that the primary key of the student entity is used as a foreign key value for the associated mark sheet entity, which acts as a primary key for the mark sheet entity. You can see the @GeneratedValue removed because it takes its value from the student id column. @MapsId indicates that the primary key values will be copied from Student entity.

2.3 Join Table Method

There can be a scenario where a student may not have any mark sheet. If done with previous method, there will be null values as shown in the figure.

/posts/one-to-one-relationship-in-jpa/images/join-table-method/null-values-representation/john-donot-have-marksheet.png /posts/one-to-one-relationship-in-jpa/images/join-table-method/null-values-representation/marksheet-table.png
Student Table Marksheet Table

As there is no marksheet associated with John, so it has a null value in marksheet_id.

Note: In a one-to-one mapping scenario, the Join Table Method establishes the relationship between two entities by introducing a third table, called a join table or associative table. This join table contains foreign keys referencing the primary keys of both related tables, ensuring a unique association between each pair of entities.

/posts/one-to-one-relationship-in-jpa/images/join-table-method/erdiagram.png
fig : ER diagram for join table-based one-to-one mapping.

Generally, a join table is associated with many-to-many relationships, but using a join table in this case can help eliminate null values.

/posts/one-to-one-relationship-in-jpa/images/join-table-method/student-table.png /posts/one-to-one-relationship-in-jpa/images/join-table-method/marksheet-table.png /posts/one-to-one-relationship-in-jpa/images/join-table-method/marksheet-student-table.png
Student Table Marksheet Table MarksheetStudent Join Table

Whenever there is a relation established between student and marksheet there is entry in student_marksheet table with their respective primary keys mapped.

Here is how you can implement the one-to-one relationship using the Join Table Method in JPA:

@Data
@Entity
public class MarkSheet {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private Integer physics;

    private Integer english;

    @OneToOne(mappedBy = "markSheet")
    private Student student;
}

@Getter
@Setter
@Entity
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String name;

    @OneToOne
    @JoinTable(
        name = "student_mark_sheet",
        joinColumns = @JoinColumn(name = "student_id"),
        inverseJoinColumns = @JoinColumn(name = "marksheet_id")
    )
    private MarkSheet markSheet;
}

The @JoinTable annotation instructs Hibernate to use a separate join table (student_mark_sheet) to maintain the one-to-one relationship between Student and MarkSheet. This approach helps avoid null values in either table and keeps the association flexible.

Conclusion

A one-to-one relationship in JPA can be implemented using several strategies, each with its own advantages and trade-offs. The Foreign Key Method is straightforward and commonly used, the Shared Primary Key Method tightly couples the entities, and the Join Table Method offers flexibility and helps avoid null values. Choosing the right approach depends on your application’s requirements and the specific use case. Understanding these mapping techniques will help you design robust and efficient data models in your JPA-based applications.

Further Reading

If you want to dive deeper into one-to-one relationships in JPA, here are some excellent resources: