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
-
Creating an Employee
- Endpoint:
POST /employees
- Body:
{ "id": 1, "firstName": "John", "lastName": "Doe", "email": "john.doe@example.com" }
- Endpoint:
-
Updating an Employee
- Endpoint:
POST /employees/update
- Body:
{ "id": 1, "firstName": "John", "lastName": "Doe", "email": "john.doe@example.com" }
- Endpoint:
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.