Spring Boot REST API Implemantation
Project Configuration
Spring Setup
The project is initialized using Spring Initializr at https://start.spring.io/. The following properties are configured in the application.properties file:
spring.datasource.url=jdbc:mysql://localhost:3306/personnel_directory
spring.datasource.username=admin
spring.datasource.password=securepassword
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
Database Configuration
We use MySQL as our database. Here's the database schema:
CREATE DATABASE IF NOT EXISTS `personnel_directory`;
USE `personnel_directory`;
DROP TABLE IF EXISTS `worker`;
CREATE TABLE `worker` (
`id` int NOT NULL AUTO_INCREMENT,
`first_name` varchar(45) DEFAULT NULL,
`last_name` varchar(45) DEFAULT NULL,
`email` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
INSERT INTO `worker` VALUES
(1,'Michael','Johnson','michael@company.com'),
(2,'Sarah','Williams','sarah@company.com'),
(3,'David','Brown','david@company.com'),
(4,'Jennifer','Jones','jennifer@company.com'),
(5,'Robert','Garcia','robert@company.com');
Running MySQL with Docker
To set up MySQL using Docker:
# Pull the latest MySQL image
docker pull mysql:latest
# Run the container
docker run --name mysql-db -e MYSQL_ROOT_PASSWORD=securepassword -p 3306:3306 -d mysql:latest
# Check running containers
docker ps
# Verify port 3306 is in use
lsof -i :3306
# Copy SQL file to container
docker cp <sql_file.sql> mysql-db:/docker-entrypoint-initdb.d/</sql_file.sql>
Traditional CRUD Implementation
Entity Class
The entity class represents our database table:
@Data
@NoArgsConstructor
@RequiredArgsConstructor
@Entity
@Table(name = "worker")
public class Personnel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int identificationNumber;
@NonNull
@Column(name = "first_name")
private String givenName;
@NonNull
@Column(name = "last_name")
private String familyName;
@NonNull
@Column(name = "email")
private String electronicMail;
}
Data Access Layer
The repository interface defines data access methods:
public interface PersonnelRepository {
List<personnel> retrieveAll();
Personnel findById(int id);
Personnel persist(Personnel personnel);
void removeById(int id);
}</personnel>
The repository implementation:
@Repository
public class PersonnelRepositoryImpl implements PersonnelRepository {
private final EntityManager entityManager;
@Autowired
public PersonnelRepositoryImpl(EntityManager entityManager) {
this.entityManager = entityManager;
}
@Override
public List<personnel> retrieveAll() {
TypedQuery<personnel> query = entityManager.createQuery("from Personnel", Personnel.class);
return query.getResultList();
}
@Override
public Personnel findById(int id) {
return entityManager.find(Personnel.class, id);
}
@Override
public Personnel persist(Personnel personnel) {
return entityManager.merge(personnel);
}
@Override
public void removeById(int id) {
Personnel personnel = this.findById(id);
entityManager.remove(personnel);
}
}</personnel></personnel>
REST Controller
The initial controller implementation:
@RestController
@RequestMapping("/api")
public class PersonnelController {
private final PersonnelRepository personnelRepository;
public PersonnelController(PersonnelRepository personnelRepository) {
this.personnelRepository = personnelRepository;
}
@GetMapping("/workers")
public List<personnel> retrieveAll() {
return personnelRepository.retrieveAll();
}
}</personnel>
Service Layer Implementation
The service layer implements business logic and separates concerns between the controller and data access layers.
Service Interface
public interface PersonnelService {
List<personnel> findAll();
Personnel locateById(int personnelId);
Personnel addPersonnel(Personnel newPersonnel);
Personnel updatePersonnel(Personnel personnel);
void removePersonnel(int personnelId);
}</personnel>
Service Implementation
@Service
public class PersonnelServiceImpl implements PersonnelService {
private final PersonnelRepository personnelRepository;
public PersonnelServiceImpl(PersonnelRepository personnelRepository) {
this.personnelRepository = personnelRepository;
}
@Override
public List<personnel> findAll() {
return personnelRepository.retrieveAll();
}
@Override
public Personnel locateById(int personnelId) {
Personnel found = personnelRepository.findById(personnelId);
if (found == null) {
throw new RuntimeException("Personnel with ID not found: " + personnelId);
}
return found;
}
@Override
@Transactional
public Personnel addPersonnel(Personnel newPersonnel) {
newPersonnel.setIdentificationNumber(0);
return personnelRepository.persist(newPersonnel);
}
@Override
@Transactional
public Personnel updatePersonnel(Personnel personnel) {
return personnelRepository.persist(personnel);
}
@Override
@Transactional
public void removePersonnel(int personnelId) {
Personnel found = personnelRepository.findById(personnelId);
if (found == null) {
throw new RuntimeException("Personnel with ID not found: " + personnelId);
}
personnelRepository.removeById(personnelId);
}
}</personnel>
Updated Controller
@RestController
@RequestMapping("/api")
public class PersonnelController {
private final PersonnelService personnelService;
public PersonnelController(PersonnelService personnelService) {
this.personnelService = personnelService;
}
@GetMapping("/workers")
public List<personnel> findAll() {
return personnelService.findAll();
}
@GetMapping("/workers/{personnelId}")
public Personnel findById(@PathVariable int personnelId) {
return personnelService.locateById(personnelId);
}
@PostMapping("/workers")
public Personnel addPersonnel(@RequestBody Personnel newPersonnel) {
return personnelService.addPersonnel(newPersonnel);
}
@PutMapping("/workers")
public Personnel updatePersonnel(@RequestBody Personnel personnel) {
return personnelService.updatePersonnel(personnel);
}
@DeleteMapping("/workers/{personnelId}")
public String removePersonnel(@PathVariable int personnelId) {
personnelService.removePersonnel(personnelId);
return "Removed personnel with ID: " + personnelId;
}
}</personnel>
Optimization with Spring Data
Spring Data reduces boilerplate code by providing common CRUD implementations.
Adding Dependencies
Add these dependencies to your pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Using Spring Data JPA
Replace the custom repository with Spring Datta JPA:
package com.example.demo.repository;
import com.example.demo.entity.Personnel;
import org.springframework.data.jpa.repository.JpaRepository;
public interface PersonnelRepository extends JpaRepository<Personnel, Integer> {
}
Update the service implementation:
@Service
public class PersonnelServiceImpl implements PersonnelService {
private final PersonnelRepository personnelRepository;
public PersonnelServiceImpl(PersonnelRepository personnelRepository) {
this.personnelRepository = personnelRepository;
}
@Override
public List<Personnel> findAll() {
return this.personnelRepository.findAll();
}
@Override
public Personnel locateById(int personnelId) {
Optional<Personnel> result = this.personnelRepository.findById(personnelId);
if (result.isEmpty()) {
throw new RuntimeException("Personnel not found with ID: " + personnelId);
}
return result.get();
}
@Override
@Transactional
public Personnel addPersonnel(Personnel personnel) {
personnel.setIdentificationNumber(0);
return this.personnelRepository.save(personnel);
}
@Override
@Transactional
public Personnel updatePersonnel(Personnel personnel) {
return this.personnelRepository.save(personnel);
}
@Override
@Transactional
public void removePersonnel(int personnelId) {
this.personnelRepository.deleteById(personnelId);
}
}
Using Spring Data REST
With Spring Data REST, you can eliminate the service and controller layers entirely:
@RepositoryRestResource(path = "staff")
public interface PersonnelRepository extends JpaRepository<Personnel, Integer> {
}
Configuration Properties
Configure Spring Data REST in application.properties:
# Spring Data Rest properties
spring.data.rest.base-path=/api
spring.data.rest.default-page-size=50
spring.data.rest.sort-param=sort
The Spring Data REST implementation automatically exposes REST endpoints for all CRUD operations, including pagination, sorting, and filtering capabilities.