One-to-One Bidirectional Mapping with MapsId

Last updated on March 14th, 2024

This topic will teach us how to implement one-to-one(@OneToOne annotation) bidirectional mapping with a MapsId(@MapsId) annotation using Spring Boot,  Hibernate,  Spring Data JPA, H2 database and Lombok.

one-to-one_bidirectional_mapping

In the one-to-one bidirectional with the MapsId, it shares one primary key between two tables(employee and address). In a Bidirectional relationship, it is possible to navigate in both directions. We are taking an example of an Employee and Address relationship to establish bidirectional mapping with the help of @MapsId annotation. We will create a restful web service to implement one-to-one bidirectional mapping with this annotation using the Spring Boot. Let’s begin to implement this 

Table of Contents
    1. Create a Spring Boot Starter Project(one-to-one bidirectional mapping with MapsIds)
    2. Maven Dependency
    3. Defining H2 Database configuration
    4. Creating Entity
    5. Create Repository
    6. Create Service
    7. Create Model
    8. Create Controller
    9. Run the Spring Boot application
   10. Conclusion

1. Create a Spring Boot Starter Project(one-to-one bidirectional mapping with MapsIds)

       Add the following dependencies: 
          • Spring Web 
          • Lombok
          • H2 Database
          • Spring Data JPA

Project Structure

one-to-one_bidirectional_mapping

2. Maven Dependency

       pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.3</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.springjava</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </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>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

3. Defining H2 Database configuration

We are defining the H2 database configuration for this Spring Boot Application in the application.properties file.

spring.datasource.url=jdbc:h2:mem:test
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
spring.h2.console.enabled=true

4. Create Entity

We are creating two Java Bean classes(Employee and Address) and we are using JPA annotations to map these classes to the database(H2) tables. To learn how to map a Java Bean class to a database table in Spring Boot then click here.

Employee.java

package com.springjava.entity;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import lombok.Data;
@Entity
@Data
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private String emailId;
private String mobNo;
private String design;
@OneToOne(mappedBy="employee", cascade = CascadeType.ALL)
private Address address;
}

Address.java

package com.springjava.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MapsId;
import javax.persistence.OneToOne;
import lombok.Data;
@Entity
@Data
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String street;
private String city;
private String state;
@OneToOne
@MapsId
private Employee employee;
}

→ We have an employee property in the Address entity class and an address property in the Employee entity class, which means we can now navigate in both directions.
→ This @MapsId annotation is used to share the primary key between these tables. 
→ This annotation will automatically pull the id from the parent(Employee) entity and assign it to the child(Address) entity.

5. Create Repository

EmployeeRepository.java

package com.springjava.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.springjava.entity.Employee;
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
}

AddressRepository.java

package com.springjava.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.springjava.entity.Address;
public interface AddressRepository extends JpaRepository<Address, Integer> {
}

6. Create Service

EmployeeService.java

package com.springjava.service;
import java.util.List;
import com.springjava.entity.Employee;
public interface EmployeeService {
void save(Employee emp);
List<Employee> findAll();
}

Copy

EmployeeServiceImpl.java

package com.springjava.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.springjava.entity.Employee;
import com.springjava.repository.EmployeeRepository;
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
EmployeeRepository employeeRepository;
@Override
public void save(Employee emp) {
employeeRepository.save(emp);    
}
@Override
public List<Employee> findAll() {
return employeeRepository.findAll()
    }
}

AddressService.java

package com.springjava.service;
import java.util.List;
import com.springjava.entity.Address;
public interface AddressService {
List<Address> findAll();
}
AddressServiceImpl.java
package com.springjava.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.springjava.entity.Address;
import com.springjava.repository.AddressRepository;
@Service
public class AddressServiceImpl implements AddressService {
@Autowired
AddressRepository addressRepository;
@Override
public List<Address> findAll() {
return addressRepository.findAll();
    }
}

7. Create Model

We are creating the Java Bean class to take employee details to save them into the database.
EmployeeModel.java

package com.springjava.model;
import lombok.Data;
@Data
public class EmployeeModel {
private Integer id;
private String name;
private String emailId;
private String mobNo;
private String design;
private String street;
private String city;
private String state;
}

8. Create Controller

EmployeeController.java

package com.springjava.controller;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.springjava.entity.Address;
import com.springjava.entity.Employee;
import com.springjava.model.EmployeeModel;
import com.springjava.service.AddressService;
import com.springjava.service.EmployeeService;
@RestController
@RequestMapping("/api")
public class EmployeeController {
@Autowired
AddressService addressService;
@Autowired
EmployeeService employeeService;

@PostMapping("/save")
public ResponseEntity < ? > saveEmp(@RequestBody EmployeeModel empModel) {
Map <String, Object > respData = new LinkedHashMap < String, Object > ();
//creating address object and setting properties
Address address = new Address();
address.setStreet(empModel.getStreet());
address.setCity(empModel.getCity());
address.setState(empModel.getState());
//creating employee object and setting properties
Employee emp = new Employee();
emp.setName(empModel.getName());
emp.setEmailId(empModel.getEmailId());
emp.setMobNo(empModel.getMobNo());
emp.setDesign(empModel.getDesign());
emp.setAddress(null);
//saving employee into db
employeeService.save(emp);
address.setEmployee(emp);
addressService.save(address);
respData.put("status", 1);
respData.put("message", "Record is Saved Successfully!");
return new ResponseEntity < > (respData, HttpStatus.CREATED);
}

@GetMapping("/employees")
public ResponseEntity < ? > getEmployees() {
Map <String, Object> respData = new LinkedHashMap < String, Object > ();
List <Employee> empList = employeeService.findAll();
List <EmployeeModel> empListData= new ArrayList < > ();
if (!empList.isEmpty()) {
for (Employee emp: empList) {
EmployeeModel empModel = new EmployeeModel();
empModel.setId(emp.getId());
empModel.setName(emp.getName());
empModel.setEmailId(emp.getEmailId());
empModel.setMobNo(emp.getMobNo());
empModel.setDesign(emp.getDesign());
empModel.setStreet(emp.getAddress().getStreet());
empModel.setCity(emp.getAddress().getCity());
empModel.setState(emp.getAddress().getState());
empListData.add(empModel);
}
respData.put("status", 1);
respData.put("data", empListData);
return new ResponseEntity < > (respData, HttpStatus.OK);
}else{
respData.clear();
respData.put("status", 0);
respData.put("message", "Data is not found");
return new ResponseEntity < > (respData, HttpStatus.NOT_FOUND);
    }
}

@GetMapping("/address-list")
public ResponseEntity < ? > getAddressList() {
Map <String, Object> respData = new LinkedHashMap < String, Object > ();
List < Address > addList = addressService.findAll();
List < EmployeeModel > empListData = new ArrayList < > ();
if (!addList.isEmpty()) {
for (Address address: addList) {
EmployeeModel empModel = new EmployeeModel();
empModel.setId(address.getEmployee().getId());
empModel.setName(address.getEmployee().getName());
empModel.setEmailId(address.getEmployee().getEmailId());
empModel.setMobNo(address.getEmployee().getMobNo());
empModel.setDesign(address.getEmployee().getDesign());
empModel.setStreet(address.getStreet());
empModel.setCity(address.getCity());
empModel.setState(address.getState());
empListData.add(empModel);
}
respData.put("status", 1);
respData.put("data", empListData);
return new ResponseEntity < > (respData, HttpStatus.OK);
}else{
respData.clear();
respData.put("status", 0);
respData.put("message", "Data is not found");
return new ResponseEntity < > (respData, HttpStatus.NOT_FOUND);
    }
  }
}

9. Run the Spring Boot application

To run this application one-to-one bidirectional mapping with MapsId Right-click on the DemoApplication.java then click on Run As after that select Java Application.
To test the API on the Postman
Url: http://localhost:8080/api/save

one-to-one_bidirectional_mapping

Url: http://localhost:8080/api/employees

one-to-one_bidirectional_mapping

Url: http://localhost:8080/api/address-list

one-to-one_bidirectional_mapping

To check the H2 database we can browse this URL “http://localhost:8080/h2-console” on the browser to view the tables in the database which are created by this application(one-to-one bidirectional mapping with MapsId).

one-to-one_bidirectional_mapping

See both tables below here:

one-to-one_bidirectional_mapping
one-to-one_bidirectional_mapping

10. Conclusion

In this topic, we learnt about how to implement one-to-one bidirectional mapping with @MapsId annotation in spring boot, spring data JPA, Lombok and H2 database with rest API example.

Leave a Comment