JPA One To Many Example – Spring Boot

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.

The JPA supports the @OneToMany and the @ManyToOne annotations 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 started with the one to many jpa mapping example application.

Table of Contents

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 the Lombok library to avoid boilerplate code. We have added these dependencies in our pom.xml file.

Finally, the complete pom.xml file is 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 the database tables with the name EMPLOYEE_DTLS and DEPARTMENT. Also, create 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

Also, 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 an underscore(“_”).

Create entity classes and required mapping

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

Here, Department has one to many relationships with the Employee entity. Also, every Employee entity will contain a foreign key reference to the Department entity.

Create the Employee entity class

Below is the Employee entity java 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 the database sequence we created earlier as the 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 the Employee entity with the Department entity. We can also 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 the owning side of the mapping, as we have the foreign key reference in this entity class. We call the other side(Department entity) the inverse side of the relationship.

Create the department entity class

Create a java entity class with the name Department 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 relationships 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 an interface with the name EmployeeRepository, 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 the DepartmentRepository interface as shown below. We are going to use this repository to check if the department is already present, before persisting in the 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 the department repository to check if department details passed in the request are already present or not in the database.

Create REST end points for CRUD operation

We will expose REST endpoints for the CRUD operation of Employee and Department entities.

Create a java class with the name EmployeeController, 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 the Postman tool to test the REST endpoints.

Create operation

The below image shows the creation of an employee entity along with department details using postman.

jpa many to one save

Update operation

The below are the employee entity update requests and response details. HTTP PUT operation for the 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 the operation to delete the employee entity. We are passing the id of the employee to delete it from the 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 sides of related entities.