JPA supports great support for entity relationships. In the previous article, we learned how to map related entity classes using one to one mapping in JPA.

In this article, we will learn how to map the related entities which are having one to many(or many to one) relationships.

A one to many (or many to one) relationships is quite normal while developing any applications. A simple example would be a department having several employees.

In this case, each database record for the employee will also have a foreign key reference to the department he belongs to.

JPA supports @OneToMany and @ManyToOne annotation for entity mapping to map these related physical tables. This type of mapping represents the collection of objects related to another object on the “many” side.

Let’s get start with the one to many jpa mapping example application. 🙂

Create a spring boot application

Since we are using spring boot to demonstrate the JPA one to many mapping, create a spring boot application with required dependencies.

For this application, we need to add spring-boot-starter-web and spring-boot-starter-data-jpa dependencies.

We are going to use the PostgreSQL database and also Lombok library to avoid boilerplate code. We have added these dependencies in our pom.xml file.

The pom.xml file will look like as shown below.

<?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.1.7.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.asb.example</groupId>
	<artifactId>jpa-one-to-many-example</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>jpa-one-to-many-example</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
		<maven-jar-plugin.version>3.1.1</maven-jar-plugin.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.postgresql</groupId>
			<artifactId>postgresql</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</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>

Create database tables and setup database connection configuration

Create EMPLOYEE_DTLS and DEPARTMENT tables and database sequences for the tables.

CREATE TABLE public.employee_dtls
(
  id bigint NOT NULL,
  name varchar(30),
  dept_id bigint NOT NULL,
  CONSTRAINT employee_primery_key PRIMARY KEY (id),
  CONSTRAINT dept_id FOREIGN KEY (dept_id) REFERENCES public.department (id)
)

CREATE TABLE public.department
(
  id bigint NOT NULL,
  deptname varchar(50),
  CONSTRAINT dept_primery_key PRIMARY KEY (id)
)

CREATE SEQUENCE public.employee_sequence 
  INCREMENT 500
  MINVALUE 1;

CREATE SEQUENCE public.dept_sequence
  INCREMENT 500
  MINVALUE 1

Add the below spring boot JPA configuration properties to the application.properties configuration file.

spring.datasource.url=jdbc:postgresql://localhost/postgres
spring.datasource.username=postgres
spring.datasource.password=asbnotebook

#Enable physcical naming strategy.
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=true

We are configuring the data source URL, username, and password.

spring.jpa.hibernate.naming.physical-strategy: By setting this property value, we are stating that the database table and column values defined should strictly follow hibernate naming strategy conventions. Default spring’s naming convention replaces camel case entity names with underscore(“_”).

Create entity classes and required mapping

To demonstrate one to many JPA mapping, let us create Employee and Department entity classes. A department can have many employees and every employee belongs to a particular department.

Here, Department has one to many relationship with Employee entity. Every Employee entity will contain a foreign key reference to Department entity.

Below is the Employee.java entity class.

package com.asb.example.model;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Entity
@Table(name = "Employee_Dtls")
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class Employee {

	@Id
	@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "emp_seq")
	@SequenceGenerator(initialValue = 1, name = "emp_seq", sequenceName = "employee_sequence")
	@Column(name = "id")
	private Integer id;

	@Column(name = "name")
	private String name;

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(foreignKey = @ForeignKey(name = "dept_id"), name = "dept_id")
	private Department department;
}

This class has an “id” field, with database sequence we created earlier as Id generation strategy.

A “name” field to store employee name and also the department the employee belongs to.

@ManyToOne: This annotation is used to map the relationship of Employee entity with the Department entity. We can have multiple employee entities under a particular department.

@JoinColumn: We have a foreign key reference to the department id for every employee entity. We use this column to join the employee entity with the department entity class. 

This Entity is also considered as the owning side of the mapping, as we have foreign key reference in this entity class. The other side(Department entity) is considered as inverse side of the relationship.


Create a Department.java entity class as shown below.

package com.asb.example.model;

import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

import com.fasterxml.jackson.annotation.JsonIgnore;

import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Entity
@Table(name = "Department")
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class Department {

	@Id
	@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "dept_seq")
	@SequenceGenerator(initialValue = 1, name = "dept_seq", sequenceName = "dept_sequence")
	@Column(name = "id")
	private Integer id;

	@Column(name = "deptName")
	private String deptName;

	@OneToMany(mappedBy = "department", cascade = CascadeType.ALL, orphanRemoval = true)
	@JsonIgnore
	private List<Employee> employees;
}

Here, we have the same Id generation strategy as we used for the Employee entity.

Department entity has one to many relationship with employee entity. The mappedBy property is set with the value “department“. This is the previously declared Many to one mapping field name of the Employee entity class.

Next, create a EmployeeRepository.java interface as shown below.

package com.asb.example.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.asb.example.model.Employee;

public interface EmployeeRepository extends JpaRepository<Employee, Integer> {

}

Also add DepartmentRepository.java interface as shown below. We are going to use this repository to check if the department is already present or not, before persisting in database.

package com.asb.example.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.asb.example.model.Department;

public interface DepartmentRepository extends JpaRepository<Department, Integer> {

}

Create a service layer and add required methods.

package com.asb.example.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.asb.example.model.Department;
import com.asb.example.model.Employee;
import com.asb.example.repository.DepartmentRepository;
import com.asb.example.repository.EmployeeRepository;

@Service
public class EmployeeService {

	@Autowired
	private EmployeeRepository employeeRepository;

	@Autowired
	private DepartmentRepository departmentRepository;

	public List<Employee> getAllEmployees() {

		return employeeRepository.findAll();
	}

	public Employee addEmployee(Employee employee) {

		Department dept = departmentRepository.findById(employee.getDepartment().getId()).orElse(null);
		if (null == dept) {
			dept = new Department();
		}
		dept.setDeptName(employee.getDepartment().getDeptName());
		employee.setDepartment(dept);
		return employeeRepository.save(employee);
	}

	public Employee editEmployees(Employee entity) {

		return employeeRepository.save(entity);
	}

	public void deleteEmployees(Integer id) {

		employeeRepository.deleteById(id);
	}
}

In this class, we have methods to get all employee entities, create, update and delete employee entities.

We are also using department repository to check if department details passed in the request is already present or not in the database.

Create REST end points for CRUD operation

We will expose REST end points for CRUD operation of Employee and Department entities.

Create a EmployeeController.java as shown below.

package com.asb.example.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.asb.example.model.Employee;
import com.asb.example.service.EmployeeService;

@RestController
public class EmployeeController {

	@Autowired
	private EmployeeService employeeService;

	@GetMapping("/get-employees")
	public ResponseEntity<List<Employee>> getAllEmployees() {

		List<Employee> employees = employeeService.getAllEmployees();
		return new ResponseEntity<>(employees, HttpStatus.OK);
	}

	@PostMapping("/employee")
	public ResponseEntity<Employee> saveEmployee(@RequestBody Employee employee) {

		Employee emp = employeeService.addEmployee(employee);
		return new ResponseEntity<>(emp, HttpStatus.OK);
	}

	@PutMapping("/employee")
	public ResponseEntity<Employee> updateEmployee(@RequestBody Employee employee) {

		Employee emp = employeeService.editEmployees(employee);
		return new ResponseEntity<>(emp, HttpStatus.OK);
	}

	@DeleteMapping("/employee")
	public ResponseEntity<String> deleteEmployee(@RequestParam(name = "employeeId") Integer employeeId) {

		employeeService.deleteEmployees(employeeId);
		return new ResponseEntity<>("Employee with ID :" + employeeId + " deleted successfully", HttpStatus.OK);
	}
}

Testing the application

We will use Postman to test the REST end points.

Create operation

Below image shows the creation of employee entity along with department details using postman.

jpa many to one save

Update operation

Update employee entity request and response details are shown below. HTTP PUT operation for update. Make sure to pass the proper Id fields in the update request.

jpa one to many update

Retrieve operation

Let’s retrieve the saved entities. 🙂

jpa one to many get all

Delete operation

Finally, delete operation to delete the employee entity. We are passing the id of employee to delete it from database.

jpa one to many delete

Conclusion

In this article we learned how to use one to many(or many to one) JPA mapping entity mapping. We also learned bidirectional by using the mapping from both owning and inverse side of related entities.

You may also interested in