Spring Boot Hibernate One-to-Many Unidirectional Example with Complete CRUD Operations

In this tutorial, we will create a one-to-many unidirectional relationship between Department and Employee entities using Spring Boot and Hibernate. We will cover CRUD operations and demonstrate how to manage this relationship through RESTful APIs.

Prerequisites

  1. Java Development Kit (JDK) 11 or higher: Ensure JDK is installed and configured on your system.
  2. Integrated Development Environment (IDE): IntelliJ IDEA, Eclipse, or any other IDE.
  3. Maven: Ensure Maven is installed and configured on your system.

Step 1: Create a Spring Boot Project

  1. Open your IDE and create a new Spring Boot project.
  2. Use Spring Initializr or manually create the pom.xml file to include Spring Boot and other required dependencies.
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>spring-boot-one-to-many-example</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.0</version>
        <relativePath/>
    </parent>

    <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>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Step 2: Configure the Application Properties

Configure the application.properties file to set up the H2 database.

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

Explanation

  • spring.datasource.url: JDBC URL for the H2 database.
  • spring.datasource.driverClassName: JDBC driver class for the H2 database.
  • spring.datasource.username: Username for the H2 database.
  • spring.datasource.password: Password for the H2 database.
  • spring.jpa.database-platform: Hibernate dialect for H2.
  • spring.h2.console.enabled: Enables the H2 console.
  • spring.jpa.hibernate.ddl-auto: Automatically updates the database schema.
  • spring.jpa.show-sql: Prints SQL statements to the console.
  • spring.jpa.properties.hibernate.format_sql: Formats SQL statements.

Step 3: Create the Department Entity Class

Create a package named com.example.entity and a class named Department.

package com.example.entity;

import jakarta.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
public class Department {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "department_id")
    private Set<Employee> employees = new HashSet<>();

    public Department() {}

    public Department(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Set<Employee> getEmployees() {
        return employees;
    }

    public void setEmployees(Set<Employee> employees) {
        this.employees = employees;
    }

    public void addEmployee(Employee employee) {
        employees.add(employee);
    }

    public void removeEmployee(Employee employee) {
        employees.remove(employee);
    }

    @Override
    public String toString() {
        return "Department{id=" + id + ", name='" + name + '\'' + '}';
    }
}

Explanation

  • @Entity: Marks the class as an entity.
  • @Id: Marks the field as the primary key.
  • @GeneratedValue: Specifies the strategy for generating values for the primary key.
  • @OneToMany: Defines a one-to-many relationship with the Employee entity.
  • @JoinColumn: Specifies the foreign key column.

Step 4: Create the Employee Entity Class

Create a class named Employee in the same package.

package com.example.entity;

import jakarta.persistence.*;

@Entity
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    public Employee() {}

    public Employee(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Employee{id=" + id + ", name='" + name + '\'' + '}';
    }
}

Explanation

  • @Entity: Marks the class as an entity.
  • @Id: Marks the field as the primary key.
  • @GeneratedValue: Specifies the strategy for generating values for the primary key.

Step 5: Create Repository Interfaces

Create a package named com.example.repository and interfaces for Department and Employee.

package com.example.repository;

import com.example.entity.Department;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface DepartmentRepository extends JpaRepository<Department, Long> {}
package com.example.repository;

import com.example.entity.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {}

Explanation

  • JpaRepository: Provides CRUD operations for the entity.

Step 6: Create Service Classes

Create a package named com.example.service and service classes for Department and Employee.

package com.example.service;

import com.example.entity.Department;
import com.example.entity.Employee;
import com.example.repository.DepartmentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class DepartmentService {
    @Autowired
    private DepartmentRepository departmentRepository;

    public Department save(Department department) {
        return departmentRepository.save(department);
    }

    public List<Department> findAll() {
        return departmentRepository.findAll();
    }

    public Department findById(Long id) {
        return departmentRepository.findById(id).orElse(null);
    }

    public void deleteById(Long id) {
        departmentRepository.deleteById(id);
    }

    public Department addEmployee(Long departmentId, Employee employee) {
        Department department = findById(departmentId);
        if (department != null) {
            department.addEmployee(employee);
            return save(department);
        }
        return null;
    }

    public Department removeEmployee(Long departmentId, Long employeeId) {
        Department department = findById(departmentId);
        if (department != null) {
            Employee employee = department.getEmployees().stream().filter(e -> e.getId().equals(employeeId)).findFirst().orElse(null);
            if (employee != null) {
                department.removeEmployee(employee);
                return save(department);
            }
        }
        return null;
    }
}
package com.example.service;

import com.example.entity.Employee;
import com.example.repository.EmployeeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class EmployeeService {
    @Autowired
    private EmployeeRepository employeeRepository;

    public Employee save(Employee employee) {
        return employeeRepository.save(employee);
    }

    public List<Employee> findAll() {
        return employeeRepository.findAll();
    }

    public Employee findById(Long id) {
        return employeeRepository.findById(id).orElse(null);
    }

    public void deleteById(Long id) {
        employeeRepository.deleteById(id);
    }
}

Explanation

  • @Service: Marks the class as a service.
  • Autowired: Injects the repository dependency.
  • CRUD methods: Provides methods for saving, finding, and deleting entities.

Step 7: Create Controller Classes

Create a package named com.example.controller and controller classes for Department and Employee.

package com.example.controller;

import com.example.entity.Department;
import com.example.entity.Employee;
import com.example.service.DepartmentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/departments")
public class DepartmentController {
    @Autowired
    private DepartmentService departmentService;

    @PostMapping
    public Department createDepartment(@RequestBody Department department) {
        return departmentService.save(department);
    }

    @GetMapping
    public List<Department> getAllDepartments() {
        return departmentService.findAll();
    }

    @GetMapping("/{id}")
    public Department getDepartmentById(@PathVariable Long id) {
        return departmentService.findById(id);
    }

    @DeleteMapping("/{id}")
    public void deleteDepartment(@PathVariable Long id) {
        departmentService.deleteById(id);
    }

    @PostMapping("/{departmentId}/employees")
    public Department addEmployee(@PathVariable Long departmentId, @RequestBody Employee employee) {
        return departmentService.addEmployee(departmentId, employee);
    }

    @DeleteMapping("/{departmentId}/employees/{employeeId}")
    public Department removeEmployee(@PathVariable Long departmentId, @PathVariable Long employeeId) {
        return departmentService.removeEmployee(departmentId, employeeId);
    }
}
package com.example.controller;

import com.example.entity.Employee;
import com.example.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;


import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/employees")
public class EmployeeController {
    @Autowired
    private EmployeeService employeeService;

    @PostMapping
    public Employee createEmployee(@RequestBody Employee employee) {
        return employeeService.save(employee);
    }

    @GetMapping
    public List<Employee> getAllEmployees() {
        return employeeService.findAll();
    }

    @GetMapping("/{id}")
    public Employee getEmployeeById(@PathVariable Long id) {
        return employeeService.findById(id);
    }

    @DeleteMapping("/{id}")
    public void deleteEmployee(@PathVariable Long id) {
        employeeService.deleteById(id);
    }
}

Explanation

  • @RestController: Marks the class as a REST controller.
  • @RequestMapping: Maps HTTP requests to handler methods.
  • CRUD methods: Provides endpoints for creating, reading, and deleting entities.

Step 8: Create Main Application Class

Create a package named com.example and a class named SpringBootOneToManyExampleApplication.

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootOneToManyExampleApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootOneToManyExampleApplication.class, args);
    }
}

Explanation

  • @SpringBootApplication: Marks the class as a Spring Boot application.
  • SpringApplication.run: Launches the application.

Step 9: Run the Application

  1. Run the SpringBootOneToManyExampleApplication class.
  2. Use an API client (e.g., Postman) or a web browser to test the endpoints.

Testing the Endpoints

  1. Create a Department:

    • URL: POST /departments
    • Body:
      {
        "name": "IT"
      }
      
  2. Create Employees:

    • URL: POST /employees
    • Body:
      {
        "name": "John Doe"
      }
      
    • Body:
      {
        "name": "Jane Doe"
      }
      
  3. Add Employees to Department:

    • URL: POST /departments/{departmentId}/employees
    • Body:
      {
        "name": "John Doe"
      }
      
  4. Get All Departments:

    • URL: GET /departments
  5. Get Department by ID:

    • URL: GET /departments/{id}
  6. Get All Employees:

    • URL: GET /employees
  7. Get Employee by ID:

    • URL: GET /employees/{id}
  8. Delete Department by ID:

    • URL: DELETE /departments/{id}
  9. Delete Employee by ID:

    • URL: DELETE /employees/{id}

Conclusion

You have successfully created an example using Spring Boot and Hibernate to demonstrate a one-to-many unidirectional relationship. This tutorial covered setting up a Spring Boot project, configuring Hibernate, creating entity classes with a one-to-many relationship, and performing CRUD operations through RESTful endpoints.


Comments