← Back to list

Best Practices for Data Validation in Spring Boot — A Beginner-Friendly Guide

When you're building a Spring Boot application, one thing you will quickly realize is that not every request coming from the client can be…

Tanisha Suyal · 2026-08-17 07:59 · 4 claps · 8.5 min read
#spring-boot #java #devlopment #data-validation #first-website
Open on Medium ↗

Best Practices for Data Validation in Spring Boot — A Beginner-Friendly Guide

When you're building a Spring Boot application, one thing you will quickly realize is that not every request coming from the client can be trusted.

A user might send:

{
  "username": "",
  "email": "hello",
  "age": -5
}

Your application should not blindly accept this data.

That's where validation comes in.

In this article, we'll understand:

  • Why validation is necessary
  • @Valid vs @Validate
  • Important validation annotations
  • Where validation should be placed
  • @NotNull vs @Column(nullable = false)
  • Commonly confused annotations
  • A practical Spring Boot example
  • Validation best practices for beginners

1. Why Do We Need Validation?

Imagine you have a registration API:

POST /users

The client sends:

{
  "username": "",
  "email": "abc",
  "age": -10
}

Without validation, your application might continue processing this request and eventually save invalid data to the database.

Validation allows us to reject invalid input before it reaches our business logic or database.

For example:

@NotBlank
private String username;

@Email
private String email;

@Min(18)
private int age;

Now Spring Boot can automatically check whether the incoming data satisfies these rules.

Think of validation as a gate

Client Request
      ↓
   Validation
      ↓
 ┌────┴────┐
Valid    Invalid
 ↓          ↓
Service   Error
 ↓
Database

This is much better than manually writing:

if (username == null || username.isEmpty()) {
    // error
}

if (age < 18) {
    // error
}

for every request.

2. Spring Boot Validation: The Basic Idea

Spring Boot uses Jakarta Bean Validation for declarative validation.

In a typical Spring Boot project, you add:

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

Then you can use annotations such as:

@NotBlank
@Email
@Size
@Min
@Max
@NotNull
@Positive

These annotations describe what is valid.

But there is an important distinction:

*@NotBlank and @Email define the validation rule. @Valid and @Validated help determine when validation is triggered.*

This distinction is one of the most important things to understand.

3. @Valid vs @Validated

This is probably the biggest source of confusion for beginners.

They look similar, but they are not exactly the same.

@Valid

@Valid comes from Jakarta:

import jakarta.validation.Valid;

It is commonly used in controllers to tell Spring:

"Validate this object using the constraints declared inside it."

Example:

@PostMapping
public ResponseEntity<String> createUser(
        @Valid @RequestBody UserDto userDto) {

    return ResponseEntity.ok("User is valid");
}

Suppose our DTO is:

public class UserDto {

    @NotBlank
    private String username;

    @Email
    @NotBlank
    private String email;

    @Min(18)
    private int age;
}

When the request arrives, @Valid tells Spring to check these annotations.

Without @Valid

public ResponseEntity<String> createUser(
        @RequestBody UserDto userDto)

The constraints inside UserDto will not automatically be triggered for this request body.

With @Valid

public ResponseEntity<String> createUser(
        @Valid @RequestBody UserDto userDto)

Spring validates the DTO before entering your controller method.

4. What is @Validated?

@Validated is a Spring-specific annotation:

import org.springframework.validation.annotation.Validated;

It is particularly useful for method-level validation and validation groups.

For example:

@Service
@Validated
public class UserService {

    public void updateEmail(
            @NotNull Long id,
            @Email @NotBlank String newEmail) {

        // business logic
    }
}

Here, @Validated on the service class enables method parameter validation.

If someone calls:

updateEmail(null, "wrong-email");

the validation mechanism can detect the invalid arguments.

5. So What Is the Actual Difference?

A simple way to remember it:

@Valid@ValidatedJakarta Bean Validation annotationSpring annotationCommonly used with request DTOsCommonly used for method validationCascades validation into an objectCan activate method validationDoes not select validation groupsSupports validation groupsVery common in controllersCommonly used at service/class level

Beginner rule

For normal request DTO validation:

@Valid @RequestBody UserDto dto

For method parameter validation in a Spring-managed service:

@Service
@Validated
public class UserService {

    public void method(@NotNull Long id) {
    }
}

Don't think of them as competitors.

They solve different parts of the validation problem.

6. Important Validation Annotations

Let's divide them into categories.

String Validation

@NotNull

Checks that the value is not null.

@NotNull
private String username;

But this is valid:

username = ""

because an empty string is not null.

@NotEmpty

Checks that the value is not null and has at least one element/character.

@NotEmpty
private String username;

This rejects:

null
""

But it does not reject a string containing only whitespace:

"   "

@NotBlank

This is usually the better choice for required text fields.

@NotBlank
private String username;

It rejects:

null
""
"   "

Easy way to remember

@NotNull
    ↓
must exist

@NotEmpty
    ↓
must contain something

@NotBlank
    ↓
must contain meaningful text

For most required String fields, @NotBlank is a great default.

7. @Size

Used to specify length or collection-size limits.

@Size(min = 3, max = 20)
private String username;

This means:

3 ≤ username length ≤ 20

Important:

@Size does not mean "required."

For example:

@Size(min = 3, max = 20)
private String username;

does not reject null.

If the field is mandatory, you may need:

@NotBlank
@Size(min = 3, max = 20)
private String username;

8. @Email

Used to validate email format.

@Email
private String email;

One important detail:

@Email by itself does not mean the field is required.

Therefore, if the email must exist:

@NotBlank
@Email
private String email;

This is a very common combination.

9. @Pattern

Used when you need a custom regular expression.

For example:

@Pattern(
    regexp = "^[0-9]{10}$",
    message = "Phone number must contain 10 digits"
)
private String phoneNumber;

This can be useful for:

  • phone numbers
  • PIN codes
  • usernames
  • custom IDs
  • specific formats

Don't use @Pattern when a standard annotation already expresses the rule clearly.

10. Numeric Validation

@Min

@Min(18)
private int age;

Means:

age >= 18

@Max

@Max(100)
private int age;

Means:

age <= 100

@Positive

@Positive
private int quantity;

Means:

quantity > 0

@PositiveOrZero

@PositiveOrZero
private int quantity;

Means:

quantity >= 0

Similarly:

@Negative
@NegativeOrZero

can be used for negative numbers.

11. A Common Confusion: @Min vs @Positive

Consider:

@Min(1)
private int quantity;

and:

@Positive
private int quantity;

For many integer use cases, both effectively enforce:

quantity >= 1

But they communicate slightly different intentions.

@Positive says:

The value must be positive.

@Min(5) says:

The value must be at least 5.

So use the annotation that expresses your business rule most clearly.

12. Date Validation

For date/time fields, you can use:

@Past

The date must be in the past.

@PastOrPresent

The date can be in the past or present.

@Future

The date must be in the future.

@FutureOrPresent

The date can be today or in the future.

For example:

@Past
private LocalDate dateOfBirth;

A person's date of birth should normally be in the past.

13. Boolean Validation

You can also validate boolean values.

@AssertTrue
private boolean termsAccepted;

This can be useful when the user must explicitly accept something:

@AssertTrue(message = "You must accept the terms")
private boolean termsAccepted;

14. Where Should Validation Be Placed?

This is where beginners often put everything inside the Entity.

A better mental model is:

Controller
    ↓
DTO validation
    ↓
Service
    ↓
Business validation
    ↓
Repository
    ↓
Database constraints

Different layers have different responsibilities.

15. Controller Layer: Validate Incoming Requests

Suppose we have:

public class UserDto {

    @NotBlank(message = "Username is required")
    private String username;

    @NotBlank(message = "Email is required")
    @Email(message = "Invalid email")
    private String email;

    @Min(value = 18, message = "Age must be at least 18")
    private int age;
}

Our controller:

@RestController
@RequestMapping("/users")
public class UserController {

    @PostMapping
    public ResponseEntity<String> createUser(
            @Valid @RequestBody UserDto userDto) {

        return ResponseEntity.ok("User created");
    }
}

The flow is:

HTTP Request
     ↓
@RequestBody
     ↓
@Valid
     ↓
UserDto constraints
     ↓
Valid? ─── No ──→ Validation Error
  │
 Yes
  ↓
Controller logic

This is why DTO validation is generally a good practice.

16. Why Not Put Everything in the Entity?

You might see:

@Entity
public class User {

    @NotBlank
    private String username;

    @Email
    private String email;
}

This can work, but using entities directly as API request objects can create unnecessary coupling between:

API contract
      ↕
Database model

A DTO allows you to keep these responsibilities separate.

For example:

UserRequestDto
      ↓
Controller
      ↓
UserService
      ↓
User Entity
      ↓
Repository

The DTO represents what the API accepts.

The Entity represents how your application persists the data.

17. Entity Validation: @NotNull vs @Column(nullable = false)

This is another extremely important distinction.

Consider:

@NotNull
@Column(nullable = false)
private String email;

They may look like they do the same thing.

They don't.

@NotNull

This is a Bean Validation constraint.

It says:

"This Java value must not be null when validation is triggered."

Example:

@NotNull
private String email;

Validation happens at the application/validation layer.

@Column(nullable = false)

This is a JPA mapping configuration.

@Column(nullable = false)
private String email;

It communicates that the corresponding database column should not allow NULL values, particularly when schema generation is involved.

So:

@NotNull
    ↓
Application validation

@Column(nullable = false)
    ↓
Database/schema constraint

Best practice?

For important mandatory fields, you may want both:

@NotNull
@Column(nullable = false)
private String email;

Why?

Because application validation and database constraints provide different layers of protection.

18. Another Important Confusion: @NotNull on Primitive Types

Consider:

@NotNull
private int age;

This doesn’t make much sense because a primitive int can never be null.

It always has a value.

Instead, if you need to distinguish between:

age not provided

and:

age = 0

use:

@NotNull
private Integer age;

This is because Integer can be:

null
18
25

while int cannot be null.

This small difference becomes very important when designing DTOs.

19. Validation Does Not Replace Business Logic

This is another important concept.

Validation:

@Min(18)
private Integer age;

can tell us:

age must be at least 18.

But suppose our business rule is:

A user can only purchase this product if they have sufficient balance.

That’s not simply a Bean Validation constraint.

It belongs in the service/business layer.

For example:

if (user.getBalance() < product.getPrice()) {
    throw new InsufficientBalanceException();
}

So think:

Validation
    ↓
"Is this input structurally valid?"
Business Logic
    ↓
"Is this operation allowed?"

20. A Practical Example

Let’s put everything together.

DTO

public class UserRequest {
    @NotBlank(message = "Username is required")
    @Size(min = 3, max = 20,
          message = "Username must be between 3 and 20 characters")
    private String username;
    @NotBlank(message = "Email is required")
    @Email(message = "Invalid email format")
    private String email;
    @NotNull(message = "Age is required")
    @Min(value = 18, message = "Age must be at least 18")
    private Integer age;
    @AssertTrue(message = "You must accept the terms")
    private boolean termsAccepted;
    // getters and setters
}

Controller

@RestController
@RequestMapping("/users")
public class UserController {
    @PostMapping
    public ResponseEntity<String> createUser(
            @Valid @RequestBody UserRequest request) {
        return ResponseEntity.ok("User created successfully");
    }
}

Now this request:

{
    "username": "",
    "email": "hello",
    "age": 15,
    "termsAccepted": false
}

will fail validation.

The service doesn’t have to manually check every field.

21. What Happens When Validation Fails?

Suppose we send:

{
    "username": "",
    "email": "wrong",
    "age": 15
}

Spring detects the violations and typically raises an exception such as:

MethodArgumentNotValidException

for a validated request body.

In a real production application, you usually don’t want to return a huge framework-generated error response.

Instead, you can create a global exception handler using:

@RestControllerAdvice

and return a clean response such as:

{
    "username": "Username is required",
    "email": "Invalid email format",
    "age": "Age must be at least 18"
}

This makes your API much easier for frontend developers and API consumers to use.

22. Quick Cheat Sheet

Required String

@NotBlank
private String name;

Required object/value

@NotNull
private Integer age;

Email

@NotBlank
@Email
private String email;

String length

@Size(min = 3, max = 20)
private String username;

Number range

@Min(18)
@Max(100)
private Integer age;

Positive number

@Positive
private Integer quantity;

Custom format

@Pattern(regexp = "...")
private String value;

Past date

@Past
private LocalDate dateOfBirth;

Future date

@Future
private LocalDate appointmentDate;

Must be true

@AssertTrue
private boolean accepted;

Trigger DTO validation

@Valid
@RequestBody UserRequest request

Method parameter validation

@Service
@Validated
public class UserService {

    public void method(@NotNull Long id) {
    }
}

23. The Most Important Things to Remember

If you’re learning Spring Boot, don’t try to memorize every annotation immediately.

First understand these:

1. @NotBlank

For required strings.

2. @NotNull

For required objects/values.

3. @Email

For email format.

Usually:

@NotBlank
@Email

for a required email.

4. @Size

For length/size.

5. @Min, @Max

For numeric ranges.

6. @Valid

Usually used when you want Spring to validate an object’s fields, especially a request DTO.

7. @Validated

Useful for Spring method validation and validation groups.

8. @NotNull@Column(nullable = false)

One is application-level Bean Validation; the other is a database/JPA mapping constraint.

Final Mental Model

Don’t think of validation as:

“Which annotation should I memorize?”

Think of it as layers:

CLIENT
                ↓
        ┌─────────────────┐
        │   CONTROLLER    │
        │                 │
        │ @Valid DTO      │
        │ @NotBlank       │
        │ @Email          │
        │ @Size           │
        └────────┬────────┘
                 ↓
        ┌─────────────────┐
        │     SERVICE     │
        │                 │
        │ @Validated      │
        │ Method checks   │
        │ Business rules  │
        └────────┬────────┘
                 ↓
        ┌─────────────────┐
        │   REPOSITORY    │
        └────────┬────────┘
                 ↓
        ┌─────────────────┐
        │    DATABASE     │
        │                 │
        │ NOT NULL        │
        │ UNIQUE          │
        │ FK constraints  │
        └─────────────────┘

The key idea is:

Validation annotations define the rules. @Valid / @Validated help trigger validation. Database constraints provide another line of defense. Business rules belong in the service layer.

Once you understand this separation, Spring Boot validation becomes much less confusing — and you’ll start seeing why production applications don’t simply put every validation annotation on the Entity and call it done.


메타데이터
post_id
01e2879d07df
slug
best-practices-for-data-validation-in-spring-boot-a-beginner-friendly-guide-01e2879d07df
url
https://medium.com/@tanishasuyal43/best-practices-for-data-validation-in-spring-boot-a-beginner-friendly-guide-01e2879d07df
canonical_url
https://medium.com/@tanishasuyal43/best-practices-for-data-validation-in-spring-boot-a-beginner-friendly-guide-01e2879d07df
author_url
https://medium.com/@tanishasuyal43
status
ok
fetched_at
2026-09-08 02:17:38