Difference Between @Valid and @Validated in Spring Boot

Spring Boot provides robust support for validating application data, ensuring that the data entering your application adheres to predefined constraints. Two primary annotations used for validation in Spring Boot are @Valid and @Validated. Although both serve the purpose of validating data, they have distinct contexts and specific usages. In this blog post, we will explore the differences between @Valid and @Validated, their purposes, and how to use them effectively in a Spring Boot application. We will use the Employee entity as our example.

What is @Valid?

Purpose

The @Valid annotation is part of the Jakarta Bean Validation API. It is used to trigger validation on the annotated method parameter, method return value, or field. The constraints are defined in the bean class using annotations like @NotNull, @Size, @Min, @Max, and others.

Usage

  • Commonly used on method parameters in controllers to validate incoming data.
  • Can also be used on method return values and fields within a class.

Example

Defining the Employee Entity with Validation Constraints

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

public class Employee {
    @NotNull(message = "ID cannot be null")
    private Long id;

    @NotNull(message = "First name cannot be null")
    @Size(min = 2, max = 30, message = "First name must be between 2 and 30 characters")
    private String firstName;

    @NotNull(message = "Last name cannot be null")
    @Size(min = 2, max = 30, message = "Last name must be between 2 and 30 characters")
    private String lastName;

    @NotNull(message = "Email cannot be null")
    @Email(message = "Email should be valid")
    private String email;

    // Getters and Setters
}

Using @Valid in a Controller

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import jakarta.validation.Valid;

@RestController
@Validated
public class EmployeeController {

    @PostMapping("/employees")
    public ResponseEntity<String> createEmployee(@Valid @RequestBody Employee employee) {
        // If validation passes, proceed with the business logic
        return new ResponseEntity<>("Employee is valid", HttpStatus.OK);
    }
}

In this example, the @Valid annotation on the Employee parameter triggers the validation of the Employee object when the /employees endpoint is called. If the Employee object is invalid, a MethodArgumentNotValidException is thrown, and Spring Boot handles it by returning a 400 Bad Request response with validation error details.

What is @Validated?

Purpose

The @Validated annotation is a Spring-specific variant that provides additional capabilities on top of what @Valid offers. It is used to trigger validation on method parameters, method return values, and class-level validation in more complex scenarios. One of its key features is support for validation groups, which allows you to apply different validation rules in different contexts.

Usage

  • Commonly used on class-level annotations to enable validation on methods within the class.
  • Supports validation groups, which are useful for conditional validation based on different operations like create or update.

Example

Defining Validation Groups

public class ValidationGroups {
    public interface Create {}
    public interface Update {}
}

Defining the Employee Entity with Validation Constraints for Groups

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

public class Employee {
    @NotNull(groups = ValidationGroups.Create.class, message = "ID cannot be null")
    private Long id;

    @NotNull(message = "First name cannot be null")
    @Size(min = 2, max = 30, message = "First name must be between 2 and 30 characters")
    private String firstName;

    @NotNull(message = "Last name cannot be null")
    @Size(min = 2, max = 30, message = "Last name must be between 2 and 30 characters")
    private String lastName;

    @NotNull(message = "Email cannot be null")
    @Email(message = "Email should be valid")
    private String email;

    // Getters and Setters
}

Using @Validated with Groups in a Controller

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Validated
public class EmployeeController {

    @PostMapping("/employees")
    public ResponseEntity<String> createEmployee(@Validated(ValidationGroups.Create.class) @RequestBody Employee employee) {
        // If validation passes, proceed with the business logic
        return new ResponseEntity<>("Employee is valid", HttpStatus.OK);
    }

    @PostMapping("/employees/update")
    public ResponseEntity<String> updateEmployee(@Validated(ValidationGroups.Update.class) @RequestBody Employee employee) {
        // If validation passes, proceed with the business logic
        return new ResponseEntity<>("Employee update is valid", HttpStatus.OK);
    }
}

In this example, the @Validated annotation is used with validation groups to apply different validation rules for create and update operations. The createEmployee method validates the Employee object against the Create group constraints, while the updateEmployee method validates against the Update group constraints.

Complete Example

Let’s put it all together in a complete Spring Boot application.

Step 1: Set Up the Project

pom.xml

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>spring-boot-validation-example</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencies>
        <!-- Spring Boot Starter Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring Boot Starter Validation -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
    </dependencies>

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

Step 2: Create the Validation Groups

ValidationGroups.java

public class ValidationGroups {
    public interface Create {}
    public interface Update {}
}

Step 3: Create the Employee Entity

Employee.java

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

public class Employee {
    @NotNull(groups = ValidationGroups.Create.class, message = "ID cannot be null")
    private Long id;

    @NotNull(message = "First name cannot be null")
    @Size(min = 2, max = 30, message = "First name must be between 2 and 30 characters")
    private String firstName;

    @NotNull(message = "Last name cannot be null")
    @Size(min = 2, max = 30, message = "Last name must be between 2 and 30 characters")
    private String lastName;

    @NotNull(message = "Email cannot be null")
    @Email(message = "Email should be valid")
    private String email;

    // Getters and Setters
}

Step 4: Create the Controller

EmployeeController.java

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Validated
public class EmployeeController {

    @PostMapping("/employees")
    public ResponseEntity<String> createEmployee(@Validated(ValidationGroups.Create.class) @RequestBody Employee employee) {
        // If validation passes, proceed with the business logic
        return new ResponseEntity<>("Employee is valid", HttpStatus.OK);
    }

    @PostMapping("/employees/update")
    public ResponseEntity<String> updateEmployee(@Validated(ValidationGroups.Update.class) @RequestBody Employee employee) {
        // If validation passes, proceed with the business logic
        return new ResponseEntity<>("Employee update is valid", HttpStatus.OK);
    }
}

Step 5: Running the Application

Run the Spring Boot application from your IDE or from the command line using the following command:

mvn spring-

boot:run

Testing the Endpoints

  1. Creating an Employee

    • Endpoint: POST /employees
    • Body:
      {
          "id": 1,
          "firstName": "John",
          "lastName": "Doe",
          "email": "john.doe@example.com"
      }
      
  2. Updating an Employee

    • Endpoint: POST /employees/update
    • Body:
      {
          "id": 1,
          "firstName": "John",
          "lastName": "Doe",
          "email": "john.doe@example.com"
      }
      

If the input data violates any validation constraints, Spring Boot will return a 400 Bad Request response with validation error details.

Conclusion

In this blog post, we explored the differences between @Valid and @Validated in Spring Boot. We discussed their purposes, usages, and provided a complete example using the Employee entity. Understanding these annotations and their differences helps you apply appropriate validation strategies in your Spring Boot applications, ensuring data integrity and consistency.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top