Prerequisites
- JDK 17 or later
- Maven or Gradle
- IDE (IntelliJ IDEA, Eclipse, etc.)
Step 1: Set Up a Spring Boot Project
1.1 Create a New Spring Boot Project
Use Spring Initializr to create a new project with the following dependencies:
- Spring Web
- Spring Boot Actuator
- Spring Data JPA
- H2 Database
- Validation
1.2 Configure application.properties
Set up the application properties for your project.
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=create-drop
Step 2: Define the Entity with Validation Annotations
Create an entity class with validation annotations to enforce constraints on the fields.
package com.example.demo;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
@Entity
public class Product {
@Id
@NotNull(message = "ID cannot be null")
private Long id;
@NotBlank(message = "Name is mandatory")
@Size(min = 2, max = 100, message = "Name must be between 2 and 100 characters")
private String name;
@NotNull(message = "Price cannot be null")
@DecimalMin(value = "0.0", inclusive = false, message = "Price must be greater than zero")
private Double price;
// Constructors
public Product() {
}
public Product(Long id, String name, Double price) {
this.id = id;
this.name = name;
this.price = price;
}
// Getters and setters
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 Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
}
Step 3: Create a Repository
Create a repository interface to manage Product entities.
package com.example.demo;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ProductRepository extends JpaRepository<Product, Long> {
}
Step 4: Create a Service
Create a service to handle business logic and validation.
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import jakarta.validation.Valid;
import java.util.List;
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
public List<Product> getAllProducts() {
return productRepository.findAll();
}
public Product getProductById(Long id) {
return productRepository.findById(id).orElse(null);
}
public Product addProduct(@Valid Product product) {
return productRepository.save(product);
}
}
Step 5: Create a Controller
Create a REST controller to handle HTTP requests and validate input data.
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
import java.util.List;
@RestController
@RequestMapping("/products")
@Validated
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping
public List<Product> getAllProducts() {
return productService.getAllProducts();
}
@GetMapping("/{id}")
public Product getProductById(@PathVariable Long id) {
return productService.getProductById(id);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Product addProduct(@Valid @RequestBody Product product) {
return productService.addProduct(product);
}
}
Step 6: Global Exception Handling
Create a global exception handler to handle validation errors and return meaningful responses.
package com.example.demo;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
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.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) {
Map<String, String> errors = ex.getBindingResult().getFieldErrors()
.stream()
.collect(Collectors.toMap(
fieldError -> fieldError.getField(),
fieldError -> fieldError.getDefaultMessage()
));
return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseEntity<Map<String, String>> handleGlobalExceptions(Exception ex) {
Map<String, String> error = new HashMap<>();
error.put("message", ex.getMessage());
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Step 7: Testing the Application
7.1 Create Test Cases
Write test cases to verify the validation logic.
package com.example.demo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import static org.junit.jupiter.api.Assertions.assertEquals;
@SpringBootTest
class ProductControllerTests {
@Autowired
private RestTemplate restTemplate;
@Test
void testAddProductValidation() {
Product product = new Product(null, "", -1.0);
ResponseEntity<String> response = restTemplate.postForEntity("/products", product, String.class);
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
}
}
7.2 Run the Application
Run the Spring Boot application and navigate to http://localhost:8080/products
to test the endpoints and see the validation in action.
Conclusion
In this tutorial, you have learned how to implement validation in a Spring Boot 3.2 application. We covered how to use validation annotations on entity fields, handle validation in services and controllers, and create a global exception handler to manage validation errors. This approach ensures that your application processes only valid data and provides meaningful error messages to clients.
Comments
Post a Comment