Home » Spring Boot » Spring Boot, Spring Data JPA – Rest CRUD API example

Spring Boot, Spring Data JPA – Rest CRUD API example

In this tutorial, we will build a Spring Boot REST CRUD API example with Maven that use Spring Data JPA to interact with MySQL database. We are going to build CRUD RESTFul APIs for a Simple Employee Management System which will expose various endpoints for Employee CRUD operation.

Overview of Project

We will build a CRUD RESTful API for a Employee-Management application using Spring Boot 2 JPA and MySQL. Following are list of REST APIs created for Employee resource.

Spring Boot CRUD

Technology

  • Java 8
  • Spring Boot 2.2.1 (with Spring Web MVC, Spring Data JPA)
  • Spring Framework - 5.0.8 RELEASE
  • MySQL
  • Maven 3.6.1

Creating Spring Boot Project and Importing it to IDE

There are many ways to create a Spring Boot application. The simplest way is to use Spring Initializr page by opening URL(http://start.spring.io/,) in your browser. Spring Initializer is an online Spring Boot application generator tool. Open the Spring initializer page and fill the details as shown below.

Look at the above diagram, we have specified following details:

Spring Boot CRUD


  • Generate: Maven Project
  • Java Version: 1.8 (Default)
  • Boot:2.2.6
  • Group: com.jwt.spring.boot.demo
  • Artifact: employee-managment-service
  • Name: employee-managment-service
  • Description: Rest API for a Simple Employee Management Application
  • Package Name : com.jwt.spring.boot.demo
  • Packaging: jar (This is the default value
  • Dependencies: Web, JPA, MySQL, DevTools

Once, all the details are entered, click on Generate Project button will generate a spring boot project and downloads it. Next, Unzip the downloaded zip file and import it into your favorite IDE.

Directory Struture Of Project

Following is the directory structure of our Employee Management Application.

Spring Boot CRUD

The pom.xml File

In the pom.xml we define this project as a Spring Boot project along with web dependency and dependencies to connect to the database.

<?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.2.6.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.jwt.spring.boot.demo</groupId>
	<artifactId>employee-management-service</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>employee-management-service</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.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.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

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

</project>

Define Entity – Employee.java

package com.jwt.spring.boot.demo.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "employee")
public class Employee {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private long id;
	private String firstName;
	private String lastName;
	private String emailId;

	public long getId() {
		return id;
	}

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

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public String getEmailId() {
		return emailId;
	}

	public void setEmailId(String emailId) {
		this.emailId = emailId;
	}

	public Employee() {
	}

	public Employee(String firstName, String lastName, String emailId) {
		super();
		this.firstName = firstName;
		this.lastName = lastName;
		this.emailId = emailId;
	}

	@Override
	public String toString() {
		return "Employee [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", emailId=" + emailId
				+ "]";
	}

}					

Database Configuration using application.properties or application.yml.

application.properties

## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url = jdbc:mysql://localhost:3306/spring_boot?useSSL=false
spring.datasource.username = root
spring.datasource.password = mukesh

## Hibernate Properties
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect

# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = update

application.yml

spring:
    datasource:
        password: mukesh
        url: jdbc:mysql://localhost:3306/spring_boot?useSSL=false
        username: root
    jpa:
        hibernate:
            ddl-auto: update
        properties:
            hibernate:
                dialect: org.hibernate.dialect.MySQL5InnoDBDialect

Define Repository – EmployeeRepository.java

We will create our repository interface extending JpaRepository. Spring Data JPA provides predefined repositories, using that we can create our Custom repository.

package com.jwt.spring.boot.demo.repo;

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

import com.jwt.spring.boot.demo.model.Employee;

public interface EmployeeRepository extends JpaRepository {

}
					

Define Service Interface – EmployeeService.java

package com.jwt.spring.boot.demo.service;

import java.util.List;
import java.util.Map;
import com.jwt.spring.boot.demo.exception.ResourceNotFoundException;
import com.jwt.spring.boot.demo.model.Employee;

public interface EmployeeService {

	public List getAllEmployees();

	public Employee getEmployeeById(Long employeeId) throws ResourceNotFoundException;

	public Employee createEmployee(Employee employee);

	public Employee updateEmployee(long employeeId, Employee employeeDetails) throws ResourceNotFoundException;

	public Map deleteEmployee(Long employeeId) throws ResourceNotFoundException;

}

Implement Service Interface – EmployeeServiceImpl.java

package com.jwt.spring.boot.demo.service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

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

import com.jwt.spring.boot.demo.exception.ResourceNotFoundException;
import com.jwt.spring.boot.demo.model.Employee;
import com.jwt.spring.boot.demo.repo.EmployeeRepository;

@Service
public class EmployeeServiceImpl implements EmployeeService {

	@Autowired
	private EmployeeRepository employeeRepository;

	@Override
	public List getAllEmployees() {
		return employeeRepository.findAll();
	}

	@Override
	public Employee getEmployeeById(Long employeeId) throws ResourceNotFoundException {
		Employee employee = employeeRepository.findById(employeeId)
				.orElseThrow(() -> new ResourceNotFoundException("Employee Not found for this id :: " + employeeId));
		return employee;
	}

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

	@Override
	public Employee updateEmployee(long employeeId, Employee employeeDetails) throws ResourceNotFoundException {
		Employee employee = employeeRepository.findById(employeeId)
				.orElseThrow(() -> new ResourceNotFoundException("Employee Not found for this id " + employeeId));
		employee.setFirstName(employeeDetails.getFirstName());
		employee.setLastName(employeeDetails.getLastName());
		employee.setEmailId(employeeDetails.getEmailId());
		Employee updatedemployee = employeeRepository.save(employee);
		return updatedemployee;
	}

	@Override
	public Map deleteEmployee(Long employeeId) throws ResourceNotFoundException {
		employeeRepository.findById(employeeId)
				.orElseThrow(() -> new ResourceNotFoundException("Employee Not found for this id " + employeeId));
		employeeRepository.deleteById(employeeId);
		Map map = new HashMap<>();
		map.put("Deleted", true);
		return map;
	}

}					

@Service annotation used with Service class where our business logic exists. See more about @Service, @Component, @Repository here.

Define controller class – Rest APIs

package com.jwt.spring.boot.demo.controller;

import java.util.List;
import java.util.Map;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
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.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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.jwt.spring.boot.demo.exception.ResourceNotFoundException;
import com.jwt.spring.boot.demo.model.Employee;
import com.jwt.spring.boot.demo.service.EmployeeService;

@RestController
@RequestMapping("/api/v1")
public class EmployeeController {

	@Autowired
	private EmployeeService employeeService;

	@GetMapping("/employees")
	public List getAllEmployees() {
		return employeeService.getAllEmployees();
	}

	@GetMapping("/employee/{id}")
	public ResponseEntity getEmployeeById(@PathVariable("id") long employeeId)
			throws ResourceNotFoundException {
		Employee employee = employeeService.getEmployeeById(employeeId);
		return ResponseEntity.ok().body(employee);

	}

	@PostMapping("/employee")
	public Employee createEmployee(@Valid @RequestBody Employee employee) {
		return employeeService.createEmployee(employee);
	}

	@PutMapping("/employee/{id}")
	public ResponseEntity updateEmployee(@PathVariable("id") long employeeId,
			@Valid @RequestBody Employee employeeDetails) throws ResourceNotFoundException {
		Employee employee = employeeService.updateEmployee(employeeId, employeeDetails);
		return ResponseEntity.ok(employee);

	}

	@DeleteMapping("/employee/{id}")
	public Map deleteEmployee(@PathVariable("id") Long employeeId) throws ResourceNotFoundException {
		return employeeService.deleteEmployee(employeeId);

	}

}

Exception(Error) Handling for RESTful Services

Spring Boot provides default implementation for exception handling for RESTful Services. Let’s quickly look at the default Exception Handling features provided by Spring Boot.

Resource Not Found

Heres what happens when you fire a request to a resource which is not available: http://localhost:8080/dummy-url


{
    "timestamp": "2020-10-24T14:39:38.315+0000",
    "status": 404,
    "error": "Not Found",
    "message": "No message available",
    "path": "/api/v1/"
}

That's a good error response and It contains all the details that are typically needed. However, one of the things that you would always need to remember is, it's important to have consistent exception message which is returned back for all the services inside your enterprise. So if you have a big organization and each of the services returns the exception message in a different way that's not really good.It would be really good if you can actually define a standard exception structure and that is followed across all you were restful services.In an organization you need to define a standard structure of your error response.

Creating Custom Business Exception

We defined the Rest APIs for creating, retrieving, updating, and deleting an Employee.The APIs will throw a ResourceNotFoundException whenever a Employee with a given id is not found in the database.Following is the definition of ResourceNotFoundException.

package com.jwt.spring.boot.demo.exception;

public class ResourceNotFoundException extends Exception {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	public ResourceNotFoundException(String message) {
		super(message);
	}

}

Customizing Error Response Structure

Default error response provided by Spring Boot contains all the details that are typically needed. However, as discussed above you need to create a framework independent response structure for your organization. In that case, you can define a specific error response structure.Let’s define a simple error response bean.

ErrorResponse.java

package com.jwt.spring.boot.demo.exception;

import java.util.Date;

public class ErrorResponse {

	private Date timestamp;

	private String message;

	private String details;

	public Date getTimestamp() {
		return timestamp;
	}

	public void setTimestamp(Date timestamp) {
		this.timestamp = timestamp;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public String getDetails() {
		return details;
	}

	public void setDetails(String details) {
		this.details = details;
	}

	public ErrorResponse(Date timestamp, String message, String details) {
		this.timestamp = timestamp;
		this.message = message;
		this.details = details;
	}

}

Implement GlobalExceptionHandler – GlobalExceptionHandler.java

To use ErrorDetails to return the error response, let’s create a GlobalExceptionHandler class annotated with @ControllerAdvice annotation. This class handles exception specific and global exception in a single place.


package com.jwt.spring.boot.demo.exception;

import java.util.Date;

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.context.request.WebRequest;

@ControllerAdvice
public class GlobalExceptionHandler {

	@ExceptionHandler(ResourceNotFoundException.class)
	public ResponseEntity<?> resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
		ErrorResponse errorDetails = new ErrorResponse(new Date(), ex.getMessage(), request.getDescription(false));
		return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
	}

	@ExceptionHandler(Exception.class)
	public ResponseEntity<?> globleExcpetionHandler(Exception ex, WebRequest request) {
		ErrorResponse errorDetails = new ErrorResponse(new Date(), ex.getMessage(), request.getDescription(false));
		return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
	}

}

Running the Application

We’ve successfully built all the apis for our application. Let’s now run the app and test the apis. This spring boot application has an entry point Java class called EmployeeManagementServiceApplication.java with the public static void main(String[] args) method, which you can run to start the application.

package com.jwt.spring.boot.demo;

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

@SpringBootApplication
public class EmployeeManagementServiceApplication {

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

}					

Let’s run the EmployeeManagementServiceApplication.java class.

Spring Boot CRUD

The application will start at Spring Boot’s default tomcat port 8080. Great! Now, It’s time to test our apis using postman.

Testing the APIs


1. Create Employee

HTTP Method: POST
Request URL: http://localhost:8080/api/v1/employee

Spring Boot CRUD


Response

Spring Boot CRUD

The response contains database auto generated id.

2. Get Employee by ID

HTTP Method: GET
Request URL: http://localhost:8080/api/v1/employee/1

Spring Boot CRUD


3. Get all Employees

HTTP Method: GET
Request URL: http://localhost:8080/api/v1/employees

Spring Boot CRUD


4. Update Employee

HTTP Method: PUT
Request URL: http://localhost:8080/api/v1/employee/1

Spring Boot CRUD



5. Delete Employee

HTTP Method: DELETE
Request URL: http://localhost:8080/api/v1/employee/1

Spring Boot CRUD



You can get the whole code used in this post from our GitHub repository:

GitHub

Previous Next Article