In the rapidly evolving landscape of software development, managing database access and operations has seen significant advancements. One such innovation within the Spring Framework ecosystem is Spring Data REST, a powerful tool that automates the creation of RESTful services. With Spring Data REST, even a simple entity class and a repository interface can effortlessly generate complex REST APIs. In this article, we will delve into the fundamental concepts and usage of Spring Data REST. You will discover how to efficiently manage database operations with Spring Data REST, empowering you to swiftly navigate the intricacies of building robust APIs. Join us on this exciting journey as we unravel the capabilities of Spring Data REST, offering a streamlined approach to handling RESTful services with unparalleled ease and efficiency.
In a typical Spring-based application, we create three-layer architecture as below:
Spring Data JPA is a powerful data access layer technology used to streamline and simplify database operations in Java-based applications. JPA (Java Persistence API) provides an interface for performing fundamental tasks like storing, retrieving, updating, and deleting Java objects in relational databases. Spring Data JPA leverages the features offered by JPA to create a convenience layer used in Spring projects, enabling more efficient and meaningful database operations.
One of the notable advantages of Spring Data JPA is its ability to automatically manage CRUD (Create, Read, Update, Delete) operations. Without the need for writing custom queries, operations can be performed using simple method naming conventions. For instance, methods like findAll and findById allow effortless retrieval of all records from the database or a specific record based on its ID. This approach eliminates unnecessary code complexity and repetition, fostering the development of a more readable and maintainable codebase.
Hmm…Can this apply to REST API’s?
Problem and Solution
What is the Problem?
If we want to create a CRUD REST APIs for an entity then we need to create a controller class and need to manually write REST APIs ( create, update, delete, get, pagination, sorting etc).
If we want to create a CRUD REST APIs for another entity: For example, Department, Project, Company, User, etc. Then, we need to create a controller for all these entities and create REST APIs.
We are repeating the same code again….
Think about the solution : Spring can Create CRUD REST APIs for entity automatically for us.
Solution
Spring Data REST module is the solution. Spring Data REST uses interfaces that extend JpaRepository and provides CRUD REST APIs for entities for FREE — Helps to minimize the boiler-plate controller layer code.
1. Create Spring Boot Project
Spring Boot provides a web tool called https://start.spring.io to bootstrap an application quickly. Just go to https://start.spring.io and generate a new spring boot project.
Use the below details in the Spring boot creation:
Project Name: springDataRest
Project Type: Maven
Choose dependencies: Spring Web, Spring Data JPA, H2 database, Lombok
Package name: com.codingrave.springDataRest
2. Add Spring Data REST to your Maven POM file
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
Here is the complete pom.xml for your reference:
<?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>3.1.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.codingrave</groupId>
<artifactId>springDataRest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springDataRest</name>
<description>springDataRest</description>
<properties>
<java.version>17</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>com.h2database</groupId>
<artifactId>h2</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>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Application.properties:
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
3. Create JPA Entity
Let’s create an entity package inside a base package “com.codingrave.springDataRest”. Within the entity package, create a Employee class with the following content:
package com.codingrave.springDataRest.entities;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Entity
@Table(name = "employees")
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "email")
private String email;
}
4. Create Spring Data JPA Repository
The next thing we’re gonna do is create a repository to access a Employee’s data from the database.
The JpaRepository interface defines methods for all the CRUD operations on the entity, and a default implementation of the JpaRepository called SimpleJpaRepository.
Let’s create a repository package inside a base package “com.codingrave.springDataRest”. Within the repository package, create a EmployeeRepository interface with the following content:
package com.codingrave.springDataRest.repository;
import com.codingrave.springDataRest.entities.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
public interface EmployeeRepository extends JpaRepository<Employee,Long> {
}
Thus, Spring Data Rest will make these endpoints available for free!
I have added Swagger documentation to the project for you. Let’s take a look at our endpoints using Swagger. You can access them by opening http://localhost:8080/swagger-ui/.
Spring Data REST — How Does It Work?
· Spring Data Rest will scan your Project for JpaRepository.
· Expose REST APIs for each entity type for your JpaRepository.
REST Endpoints
· By default, Spring Data REST will create endpoints based on entity type.
· Simple pluralized form.
· First character of Entity type is lowercase.
· Then just adds an “s” to the entity.
Before we begin our tests, let’s populate our database with a few records. You can achieve this by navigating to http://localhost:8080/h2-console and logging in with the following credentials:
- JDBC Url: jdbc:h2:mem:testdb
- Username: sa
- Password: password
Once logged in, execute the provided SQL script:
INSERT INTO EMPLOYEES (ID, FIRST_NAME, LAST_NAME, EMAIL)
VALUES
(1, 'Gregory', 'Hughes', 'GregoryHughes@armyspy.com'),
(2, 'Frank', 'Clarke', 'FrankClarke@armyspy.com'),
(3, 'Susan', 'Moore', 'SusanMoore@armyspy.com');
5. Test REST APIs using Postman
Test GET All Employees:
URL: http://localhost:8080/employees
HTTP Method: GET
Test GET Employee By ID:
URL: http://localhost:8080/employees/1
HTTP Method: GET
Test POST Employee:
URL: http://localhost:8080/employees
HTTP Method: POST
Test PUT Employee:
URL: http://localhost:8080/employees/1
HTTP Method: PUT
Test DELETE Employee:
URL: http://localhost:8080/employees/4
HTTP Method: DELETE
6. Change REST API Path
package com.codingrave.springDataRest.repository;
import com.codingrave.springDataRest.entities.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource(path="members")
public interface EmployeeRepository extends JpaRepository<Employee,Long> {
}
7. Pagination and Sorting Support
Pagination:
Sorting:
8. REST API for Query Methods
Let’s create a query method in the below repository and test using the postman client:
package com.codingrave.springDataRest.repository;
import com.codingrave.springDataRest.entities.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import java.util.List;
@RepositoryRestResource(path="members")
public interface EmployeeRepository extends JpaRepository<Employee,Long> {
List<Employee> findByFirstName(@Param("firstName") String firstName);
}
In this tutorial, we have explored the revolutionary simplicity offered by Spring Data REST in creating RESTful APIs. By eliminating the need for traditional controller and service layers, Spring Data REST automates the API creation process, allowing developers to focus on business logic rather than boilerplate code.