One-to-One Relationship in JPA
1 CommentLast 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.
Shakil Ahmed
How to do rest call with this type of mapping?