jav spring boot mastery

Data Validation and Exception Handling [Java Spring Boot Mastery Series – Part 4]

🎯 Objective

Learn to validate incoming data using annotations and handle exceptions globally for consistent and informative error responses.

✅ Add Validation Dependency (if not already included)

In pom.xml:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

This enables Java Bean Validation using annotations like @NotNull, @Size, etc.

🧾 Update Product Entity with Validation

@Entity
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotBlank(message = "Product name is required")
    private String name;

    @NotNull(message = "Price is required")
    @Min(value = 0, message = "Price must be positive")
    private Double price;

    // Getters and Setters
}

🔍 Explanation

  • @NotBlank: Field must not be null or empty.
  • @NotNull: Field must not be null.
  • @Min: Value must be at least 0.

🔁 Update Controller to Validate Input

@PostMapping
public ResponseEntity<Product> createProduct(@Valid @RequestBody Product product) {
    return new ResponseEntity<>(productService.save(product), HttpStatus.CREATED);
}

🔍 Explanation

  • @Valid: Triggers validation of the request body.
  • If validation fails, Spring throws a MethodArgumentNotValidException.

❗ Global Exception Handler

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();

        ex.getBindingResult().getFieldErrors().forEach(error ->
            errors.put(error.getField(), error.getDefaultMessage())
        );

        return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<String> handleNotFound(ResourceNotFoundException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleGenericException(Exception ex) {
        return new ResponseEntity<>("Unexpected error: " + ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

🔍 Explanation

  • @RestControllerAdvice: Handles exceptions across the whole application.
  • Catches validation errors, resource not found, and other generic exceptions.
  • Returns user-friendly error messages.

🧪 Test with Invalid Input

POST /api/products

{
  "name": "",
  "price": -10
}

🔁 Response:

{
  "name": "Product name is required",
  "price": "Price must be positive"
}

✅ With this setup, you ensure clean, validated input and consistent error messages.

➡️ Next Up: Part 5 – Using DTOs and ModelMapper for Clean Architecture

Similar Posts