One-to-One Foreign Key Relationship in JPA Spring Boot

In Java development, Java Persistence API (JPA) is a widely used specification for object-relational mapping (ORM), which allows developers to map Java objects to relational database tables. When dealing with database relationships, one-to-one relationships are essential and commonly encountered scenarios. In this blog, we will explore one-to-one relationships in JPA with Spring Boot and discuss three different approaches to implement them: using Foreign Key, using Shared Primary Key, and using Join Table.

Spring Boot JPA One to One Relationship Entity Diagram

JPA OneToOne Relationships

In a one-to-one relationship, one entity in a database table is related to exactly one record in another table. For instance, consider a scenario where we have two entities: User and Address. Each user has only one address, and each address is associated with only one user. This forms a one-to-one relationship between the User and Address entities. In this blog, we will implement foreign key relationship in three different ways.

Which approach will be best for your spring boot project:

  1. Foreign Key Approach:
    • Ideal when there is a clear owner and dependent entity (parent-child relationship).
    • Suitable for scenarios where the relationship is mostly navigated from the parent entity to the child entity.
    • Useful when the child entity can exist independently of the parent entity.
  2. Shared Primary Key Approach:
    • Best for ensuring a strong one-to-one relationship where the primary key values of both entities must be identical.
    • Useful when data consistency and integrity are crucial, and a direct connection between entities is needed.
    • Recommended when bidirectional navigation between the entities is essential.
  3. Join Table Approach:
    • Recommended when the one-to-one relationship involves complex mapping rules or additional attributes.
    • Useful when multiple attributes need to be associated with the relationship.
    • Suitable when the relationship is not strictly one-to-one and allows multiple instances to be related.

Now, let’s dive into the three approaches of implementing one-to-one relationships in JPA with SpringBoot.

1. Using Foreign Key

In this approach, we will use a foreign key in one of the entities to establish the one-to-one relationship. In our example, we will add a foreign key column user_id in the Address table that references the primary key of the User table.

@Entity
@Table(name="users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
    private Address address;
    
    // Other attributes and getters/setters
}

@Entity
@Table(name="address")
public class Address {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id; // Primary key of the Address table

    private String street;
    private String city;
    
    @OneToOne
    @JoinColumn(name = "user_id")
    private User user; // Foreign key referencing the User table
    
    // Other attributes and getters/setters
}

In the Address entity, we use the @OneToOne annotation to specify the one-to-one relationship. The @JoinColumn annotation allows us to specify the name of the foreign key column, which in this case is user_id.

By having the primary key in the User table, it means that the User entity is the owner of the relationship, and the Address entity is the dependent side. The Address entity references the User entity through the user field, which represents the bidirectional relationship.

Find this method in our Github.

2. Using Shared Primary Key

In this approach, both entities share the same primary key value. We will introduce a bidirectional relationship between User and Address.

@Entity
@Table(name="users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    private Address address;
    
    // Other attributes and getters/setters
}

@Entity
@Table(name="address")
public class Address {
    @Id
    private Long id; // Using the same primary key as User

    private String street;
    private String city;
    
    @OneToOne
    @MapsId
    private User user;
    
    // Other attributes and getters/setters
}

In this approach, both User and Address entities share the same primary key value. The @OneToOne annotation in the User entity with the mappedBy attribute indicates that the User entity is the owner of the relationship, and the address field in the User entity maps to the user field in the Address entity.

Find this method in our Github.

3. Using Join Table

In some cases, it might be necessary to use a separate join table to establish a one-to-one relationship. The join table will have foreign keys to both entities.

@Entity
@Table(name="users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id", referencedColumnName = "id")
    private Address address;
    
    // Other attributes and getters/setters
}

@Entity
@Table(name="address")
public class Address {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String street;
    private String city;
    
    @OneToOne(mappedBy = "address")
    private User user;
    
    // Other attributes and getters/setters
}

We have added the @OneToOne annotation in the User entity with the cascade attribute set to CascadeType.ALL. This ensures that when you persist or update a User entity, it cascades the operation to the associated Address entity as well.

Additionally, we have used the @JoinColumn annotation in the User entity to specify the foreign key column name address_id, which references the id column in the Address table. The referencedColumnName attribute is used to specify the column in the Address table that the foreign key references, which, in this case, is id.

The @OneToOne annotation in the Address entity with the mappedBy attribute indicates that the Address entity is the inverse side of the relationship, and the user field in the Address entity maps to the address field in the User entity.

Find this method in our Github.

How to Access Relationship fields to Get Data

To retrieve a One-to-One relationship in JPA using Spring Boot, we can use the findOne method of the UserRepository interface, as shown below:

User user = userRepository.findOne(1L);
Address address = user.getAddress();

In the above code, we retrieve the User entity with id 1 using the findOne method of the UserRepository interface, and then retrieve its related Address entity using the getAddress method of the User entity. You can use different get methods, to access different entities.

Conclusion

In this blog, we explored the concept of one-to-one relationships in JPA with Spring Boot. We discussed three different approaches to implement them: using Foreign Key, using Shared Primary Key, and using Join Table. Each approach has its advantages and use cases, and the choice depends on the specific requirements of the application.

Blogs You Might Like to Read!