1. Overview
We are going to create CRUD API connecting through JPA to H2 Database with Department Example.
We are going to design in layer architecture as shown below, but first we need some maven dependencies as covered in point 2.
Rest layer architecture:

You can download the example below or follow below step:
OR
Lets create Department example. Please create folder as shown below:

2. Maven dependencies
Please make sure to have below dependencies:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-actuator</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> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-data-jpa</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-validation</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-test</artifactid> <scope>test</scope> </dependency> <dependency> <groupid>org.mockito</groupid> <artifactid>mockito-core</artifactid> </dependency> <dependency> <groupid>org.projectlombok</groupid> <artifactid>lombok</artifactid> </dependency> |
these dependencies in a Spring Boot project:
spring-boot-starter-actuator
: provides production-ready features to help you monitor and manage your application. This includes endpoint for health check, metrics, info, etc.spring-boot-starter-web
: provides everything you need to create a web application, including a web server (Tomcat by default), the Spring MVC framework, and the Jackson JSON library.H2
: is an in-memory database that can be used for development and testing purposes. It provides a fast and easy way to store data without having to set up a separate database server.spring-boot-starter-data-jpa
: provides a way to easily connect to a database using the Java Persistence API (JPA) and Hibernate. It also provides support for database migrations using Flyway or Liquibase.spring-boot-starter-validation
: provides support for validation of user input in a web application. It includes the Bean Validation API and Hibernate Validator implementation.spring-boot-starter-test
: provides a range of test support classes, including JUnit, Mockito, and Spring Test. It makes it easier to write and run tests for your application.Lombok
: is a library that can reduce the amount of boilerplate code you have to write. It provides annotations for automatically generating getters, setters, constructors, and more.
3. Creating entity class
As we are taking Departments example, lets create Department entity class
Department.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
package com.example.demo.entity; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.validation.constraints.NotBlank; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Entity @Data @NoArgsConstructor @AllArgsConstructor @Builder public class Department { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long departmentId; @NotBlank(message = "Please Always add department name !") private String departmentName; private String address; private String code; } |
This is an example of a Java class in a Spring Boot application, specifically an entity class. An entity class represents a table in a database and maps to a database table. In this example, the class is called “Department” and it represents a table of departments in a database.
The entity class is annotated with the @Entity annotation, which indicates that this class is a JPA entity. JPA (Java Persistence API) is a specification for managing data between Java objects and a relational database.
The @Data, @NoArgsConstructor, @AllArgsConstructor, and @Builder annotations are from the Lombok library. They provide boilerplate code for getters, setters, and constructors, as well as other helpful features, saving you from having to write this code yourself.
The class has four fields:
- departmentId: This field is annotated with @Id and @GeneratedValue, which indicates that it is the primary key of the department table and that its value will be generated automatically by the database. The GenerationType.AUTO strategy means that the database will choose the appropriate strategy for generating the primary key.
- departmentName: This field is annotated with @NotBlank, which indicates that it cannot be empty. The message “Please Always add department name !” will be displayed if the field is empty.
- address: This is a simple string field to store the address of the department.
- code: This is a simple string field to store the code of the department.
Note: The “jakarta.persistence” and “lombok” imports are used in this example because it is using Jakarta EE (a Java EE 8 compatible platform) instead of Java EE.
4. Implementing a REST Controller With CRUD operations
DepartmentController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
package com.example.demo.controller; import java.util.List; import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; 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.RestController; import com.example.demo.entity.Department; import com.example.demo.error.DepartmentNotFoundException; import com.example.demo.service.DepartmentService; import jakarta.validation.Valid; @RestController public class DepartmentController { @Autowired private DepartmentService departmentService; private final Logger LOGGER = LoggerFactory.getLogger(DepartmentController.class); @PostMapping("/storeDepartment") public Department saveDepartment(@Valid @RequestBody Department department) { LOGGER.info("inside saveDepartment>>"); return departmentService.saveDepartment(department); } @GetMapping("/departments") public List<Department> fetchDepartmentList() { return departmentService.fetchDepartmentsList(); } @GetMapping("/departments/{id}") public Optional<Department> fetchById(@PathVariable("id") Long id) throws DepartmentNotFoundException { Optional<Department> dOptional= departmentService.getDepartmentByID(id); if(!dOptional.isPresent()) { LOGGER.info("DepartmentNotFoundException>>"); throw new DepartmentNotFoundException("Department not available"); } return dOptional; } @DeleteMapping("/departments/{id}") public String deleteDepartmentById(@PathVariable("id") Long id) { departmentService.deleteDepartment(id); return "successfully deleted"; } @PutMapping("/departments/{id}") public Department updateDepartment(@PathVariable("id") Long id, @RequestBody Department department) { Optional<Department> departmentDB = departmentService.getDepartmentByID(id); if (departmentDB.isPresent() && !department.getDepartmentName().isBlank()) { departmentDB.get().setDepartmentName(department.getDepartmentName()); return departmentService.saveDepartment(departmentDB.get()); } return null; } @GetMapping("departments/name/{name}") public Department getDepartmentByDepartName(@PathVariable("name") String name) { return departmentService.getDepartmentByDepartmentName(name); } } |
This code defines a DepartmentController
in a Spring Boot application that provides REST endpoints for managing departments. The DepartmentController
is annotated with @RestController
, which means it is a controller class and will handle HTTP requests.
The DepartmentService
is autowired into the controller using the @Autowired
annotation. This service provides the business logic for managing departments.
The following methods are defined in the controller:
saveDepartment
: This method maps to the/storeDepartment
endpoint and is responsible for saving a new department. The department data is passed in the request body, and the@Valid
annotation is used to indicate that the data should be validated.fetchDepartmentList
: This method maps to the/departments
endpoint and returns a list of all departments.fetchById
: This method maps to the/departments/{id}
endpoint and returns a single department with the specified id. If the department is not found, aDepartmentNotFoundException
is thrown.deleteDepartmentById
: This method maps to the/departments/{id}
endpoint and deletes a department with the specified id.updateDepartment
: This method maps to the/departments/{id}
endpoint and updates a department with the specified id.getDepartmentByDepartName
: This method maps to the/departments/name/{name}
endpoint and returns a department with the specified name.
The LOGGER
instance is used to log information about the method calls.
This code demonstrates how to create a REST controller in a Spring Boot application using standard annotations such as @RestController
, @GetMapping
, @PostMapping
, @PutMapping
, @DeleteMapping
, and @PathVariable
.
5. Service layer :
Service layer
- creating interface service.java
1 2 3 4 5 6 7 8 9 10 11 12 |
package com.example.demo.service; import java.util.List; import java.util.Optional; import com.example.demo.entity.Department; public interface DepartmentService { Department saveDepartment(Department department); List<Department> fetchDepartmentsList(); Optional<Department> getDepartmentByID(Long id); void deleteDepartment(Long id); Department getDepartmentByDepartmentName(String name); } |
2. created DepartmentServiceImpl class implementing above interface:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
package com.example.demo.service; import java.util.List; import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.example.demo.entity.Department; import com.example.demo.repository.DepartmentRepository; @Service public class DepartmentServiceImpl implements DepartmentService{ @Autowired DepartmentRepository departmentRepository; @Override public Department saveDepartment(Department department) { return departmentRepository.save(department); } @Override public List<Department> fetchDepartmentsList() { return departmentRepository.findAll(); } @Override public Optional<Department> getDepartmentByID(Long id) { return departmentRepository.findById(id); } @Override public void deleteDepartment(Long id) { departmentRepository.deleteById(id); } @Override public Department getDepartmentByDepartmentName(String name) { return departmentRepository.findByDepartmentNameIgnoreCase(name); } } |
This is an example of a service class in a Spring Boot application. The service class is responsible for encapsulating the business logic for a particular feature, in this case, managing departments. The service class is called “DepartmentServiceImpl”.
The service class implements the “DepartmentService” interface, which defines the methods that the service class must implement. The interface defines methods such as saveDepartment(), fetchDepartmentsList(), getDepartmentByID(), deleteDepartment(), and getDepartmentByDepartmentName().
The service class is annotated with the @Service annotation, which indicates that this class is a Spring service bean. This annotation is used to mark classes that perform business logic.
The service class has a member variable “departmentRepository”, which is annotated with @Autowired. This annotation tells Spring to automatically inject an instance of the DepartmentRepository class into this variable when the service class is instantiated. The DepartmentRepository class is a repository class that provides access to the department table in the database.
The methods in the service class use the DepartmentRepository class to perform operations on the department table, such as saving a department, retrieving a list of departments, retrieving a department by ID, deleting a department, and retrieving a department by name. These methods are defined in the DepartmentService interface and are implemented by the DepartmentServiceImpl class.
6. Creating Repository
1 2 3 4 5 6 7 8 9 10 |
package com.example.demo.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import com.example.demo.entity.Department; @Repository public interface DepartmentRepository extends JpaRepository<Department,Long>{ public Department findByDepartmentName(String departmentName); public Department findByDepartmentNameIgnoreCase(String departmentName); } |
This is an example of a repository class in a Spring Boot application. The repository class is responsible for providing access to the database to retrieve and store data related to the Department entity.
The repository class is called “DepartmentRepository”. The repository class extends the JpaRepository interface, which provides a set of common methods for accessing data in a JPA-based (Java Persistence API) application. The JpaRepository interface is part of the Spring Data JPA project, which provides a convenient way to interact with a database in a Spring application.
The JpaRepository interface is parameterized with the Department entity and the type of the entity’s primary key, which is Long. This means that the DepartmentRepository interface provides methods for performing CRUD (Create, Read, Update, and Delete) operations on Department entities in the database.
The repository class is annotated with the @Repository annotation, which indicates that this class is a Spring repository bean. This annotation is used to mark classes that provide access to the database.
The repository class defines two methods, findByDepartmentName() and findByDepartmentNameIgnoreCase(), which allow you to retrieve a Department entity from the database based on the name of the department. The findByDepartmentName() method returns the first Department entity that matches the specified name exactly, while the findByDepartmentNameIgnoreCase() method returns the first Department entity that matches the specified name ignoring the case of the characters in the name. These methods are defined using the Spring Data JPA conventions for method naming, which allow you to write less code to perform common database operations.
7. Creating & Handling Exception
create ErrorMessage.java in entity folder:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.example.demo.entity; import org.springframework.http.HttpStatus; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class ErrorMessage { HttpStatus code; String message; } |
create DepartmentNotFoundException and RestResponseEntityExceptionHandlers class to handle the exception:
DepartmentNotFoundException.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
package com.example.demo.error; public class DepartmentNotFoundException extends Exception{ public DepartmentNotFoundException() { } public DepartmentNotFoundException(String message) { super(message); } public DepartmentNotFoundException(Throwable cause) { super(cause); } public DepartmentNotFoundException(String message, Throwable cause) { super(message, cause); } public DepartmentNotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } } |
This is an example of a custom exception class in Java. The DepartmentNotFoundException
class extends the Exception
class and is used to indicate that a specific department could not be found.
It provides multiple constructors to accommodate different ways of creating an instance of the exception, for example with a message, with a message and a cause, or with just a cause. The exception message can be used to provide more detail about the error.
This custom exception can be used to handle specific error scenarios in your application, such as when a request to retrieve a department by ID returns no results.
RestResponseEntityExceptionHandlers.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package com.example.demo.error; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; import com.example.demo.entity.ErrorMessage; @ControllerAdvice @ResponseStatus public class RestResponseEntityExceptionHandlers extends ResponseEntityExceptionHandler{ @ExceptionHandler(DepartmentNotFoundException.class) public ResponseEntity<ErrorMessage> departmentNotFoundExceptionHandler(DepartmentNotFoundException exception, WebRequest request) { ErrorMessage errorMessage= new ErrorMessage(HttpStatus.NOT_FOUND,exception.getMessage()); return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorMessage); } } |
This code defines a class called RestResponseEntityExceptionHandlers
that extends ResponseEntityExceptionHandler
from Spring framework. This class is annotated with @ControllerAdvice
which indicates that it provides advice for handling exceptions thrown from controllers. The purpose of this class is to handle specific exceptions and return appropriate error responses.
It has a single @ExceptionHandler
method, which is responsible for handling DepartmentNotFoundException
exceptions. This method takes in the exception instance and the current request instance. It creates an instance of the ErrorMessage
class, which is used to return error information to the client. The method returns a ResponseEntity
with an HTTP status code of HttpStatus.NOT_FOUND
(404), and the error message in the body of the response.
8. Application Properties : setting up H2 DB
1 2 3 4 5 6 7 8 9 |
server.port= 8082 spring.h2.console.enabled=true spring.datasource.platform=h2 spring.datasource.url=jdbc:h2:mem:school4tech spring.datasource.driverClassName=org.h2.Driver spring.datasource.username= test spring.datasource.password= test spring.jpa.database-platform= org.hibernate.dialect.H2Dialect |
DemoApplication.java
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } |
started with commasnd on terminal : mvn spring-boot:run
The H2 will be hosted on http://localhost:8082/h2-console
Great !! Now the service is created and up.
Please hit the application and add data by calling api as below:

- You can login into H2 console with below credential and view the data inside Department table
- spring.datasource.username= test
- spring.datasource.password= test