One-to-One Relationship in JPA

One-to-One Relationship in JPA

1 Comment

Last Updated on September 6, 2021 by jt

For a One-to-One relationship in JPA, each entity instance is related to a single instance of another entity. It means each row of one entity is referred to one and only one row of another entity.

In this post,  you’ll learn how to create a One-to-One relationship between two entities using JPA in a Spring Boot application.

Maven Dependency for One-to-One Relationship

The Maven POM for this example is this.

You will require spring-boot-starter-web, spring-boot-starter-data-jpa, h2, spring-boot-starter-test, and lombok dependencies in pom.xml file.

pom.xml

               
                <dependency>
		       <groupId>org.springframework.boot</groupId>
		       <artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
		</dependency>
	        <dependency>
		       <groupId>org.projectlombok</groupId>
		       <artifactId>lombok</artifactId>
		       <optional>true</optional>
	        </dependency>

One to One Relationship Example

A one-to-one mapping refers to the relationship between two entities/database tables A and B in which only one element/row of A may only be linked to one element/row of B, and vice versa.

I am taking an example of a Person entity having a one-to-one relationship with an Address entity. Although, in the real world, a person can have multiple addresses, for the sake of learning relationships, let’s assume a person has only one home address.

The code of the Person class is this.

Person.java

@Getter
@Setter
@Builder
@Entity
@Table(name = "persons")
public class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;
    private String password;
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id")
    private Address address;
}

The preceding Person class uses Lombok to reduce boilerplate code. For more information on Lombok, I suggest you go through my series on Lombok.

Person class is annotated with @Entity to indicate that it is JPA entity.

Line 5 uses the @Table annotation to specify the database table to which this entity instance will be saved.

On-Line 8 – Line 9 the id field is annotated with both @Id and @GeneratedValue annotations.

@Id annotation indicates that the value of the id field will be mapped as a primary key in the corresponding table.

Annotation @GeneratedValue indicates that the ID value will be automatically generated by Spring Data JPA using the underlying Hibernate ORM.

The @OneToOne annotation declares that a relationship between two entities is one-to-one. It accepts the cascade  parameter. Various cascading operations are applied at the time of updation and deletion of parent entity. Here we have used  CascadeType.ALL . Therefore, it will apply all cascading operations to the related entity.

The @JoinColumn annotation on Line 15 is used to specify the foreign key column as address_id in the Person table.

Let’s now create the Address entity.

Address.java

@Getter
@Setter
@Builder
@Entity
@Table(name = "addresses")
public class Address {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String street;
    private String city;
    private String state;
    private String zipCode;
    private String country;
    @OneToOne(mappedBy = "address")
    private Person person;
}

In the preceding Address class, the @OneToOne annotation uses the mappedBy property.

Creating the JPA Repositories

This is the code for PersonRepository Interface.

PersonRepository

package org.springframework.guru.repository;

import org.springframework.data.repository.CrudRepository;
import org.springframework.guru.domain.Person;

public interface PersonRepository extends CrudRepository<Person,Long> {
}

The code for AddressRepository interface is this.

AddressRepository

package org.springframework.guru.repository;

import org.springframework.data.repository.CrudRepository;
import org.springframework.guru.domain.Address;

public interface AddressRepository extends CrudRepository<Address,Long> {
}

Unit Test the One-to-One Relationship

Let’s write few test cases to test the bidirectional one-to-one association between Person and Address.

In the PersonRepositoryTest you will write the setUp() method that sets up the test cases.

PersonRepositoryTest

@BeforeEach
        public void setUp(){
                person = Person.builder()
                    .name("Martin")
                    .email("[email protected]")
                    .password("1234abcd")
                    .build();
    

             address = Address.builder()
                    .street("Lake victoria")
                    .city("Berlin")
                    .state("Berlin")
                    .country("Germany")
.zipCode("10115")
                    .build();
    

            address.setPerson(person);
            person.setAddress(address);
            Address savedAddress = addressRepository.save(address);
            Person savedPerson= personRepository.save(person);
            addressId=savedAddress.getId();
            personId=savedPerson.getId();
        }

This method creates a Person and Address objects and set’s their association. As a result, it saves both the objects and retrieves their IDs.

The following test case asserts that a Person retrieved with an ID is associated with a valid address.

 @Test
        public void testGetPersonWithAddress(){
             Person person= personRepository.findById(personId).orElse(null);
             assertNotNull(person.getAddress());
             assertEquals("Berlin",person.getAddress().getCity());
             }

This test case asserts that  Address retrieved with an ID is associated with a valid Person.

  
       @Test
        public void testGetAddressWithPerson(){
            Address address= addressRepository.findById(addressId).orElse(null);
            assertNotNull(address.getPerson());
            assertEquals("Martin",address.getPerson().getName());
        }
    

The testDeleteCascade() asserts that on deleting a Person, the associated Address also gets deleted.

 @Test
        public void testDeleteCascade() {
            personRepository.delete(person);
            Address address = addressRepository.findById(addressId).orElse(null);
            assertNull(address);
        }

On running these tests, you will find all the test cases running successfully.

Summary

In this post, you’ve learned how to map and use a one-to-one relationship in Spring Data JPA and Hibernate.
The other Mapping relationships in Spring Data JPA like one-to-many and many-to-many will be explained in further posts.

You can find the source code of this post on Github.

About SFG Contributor

Staff writer account for Spring Framework Guru

    You May Also Like

    One comment

    1. November 14, 2023 at 1:59 pm

      How to do rest call with this type of mapping?

      Reply

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    This site uses Akismet to reduce spam. Learn how your comment data is processed.