Mastering Data Validation in Java Applications

Let me tell you about the time I debugged a weird bug for half a day… only to discover a null into the database because someone thought that the first name isn’t mandatory.

Yeah, not my proudest moment. But it taught me something important:

Always validate your data before it hits the database. Not after. Not during. Before.

This might sound obvious, but I still see a lot of Java applications treating validation as an afterthought. The result? Spaghetti logic, surprise NullPointerExceptions, and worst of all inconsistent data that makes everything harder.

Let’s fix that.

Early Validation

If you validate all incoming data right when it enters the system, in the controller or a service layer, a few magical things happen:

  • Your database stays clean and predictable.
  • You avoid littering your codebase with if (data != null && ...).
  • Your business logic gets way simpler
  • Bugs become easier to spot and fix.

It’s the classic “garbage in, garbage out” problem. Don’t let garbage in.

Controllers Must Validate Input Data

Think of your app like a security building. You wouldn’t wait for sneaky hackers to stroll in, kick off their shoes, and ask for a snack before checking their ID; no, you’d be stopping them at the entrance.

In a typical Spring Boot application, the “gates” are your controllers or entry-point services. Use validation annotations to keep things tight:

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

    @PostMapping
    public ResponseEntity<Void> createUser(@Valid @RequestBody UserDto userDto) {
        userService.create(userDto);
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }
}

public class UserDto {

    @NotBlank
    private String username;

    @Email
    private String email;

    @Size(min = 8)
    private String password;

    // getters and setters
}

Spring will automatically reject invalid input with a 400 Bad Request. Clean, declarative, no if statements in sight.

Validate Your Business Data

One validation step is good. Two is better.

Validate at the DTO level when data enters the system. Then validate again when it moves into your domain model — especially if you’re using complex business rules.

For example:

public class User {

    private final String username;
    private final String email;

    public User(String username, String email) {
        if (username == null || username.isBlank()) {
            throw new IllegalArgumentException("Username is required");
        }
        if (!email.contains("@")) {
            throw new IllegalArgumentException("Invalid email");
        }
        this.username = username;
        this.email = email;
    }

    // getters
}

Yes, it’s a bit more work. But it gives you two lines of defense:

  • External validation (catching bad data from users or the network)
  • Internal validation (making sure your domain always behaves correctly)

Avoiding Checking for Null Values

The real win here? Once you validate your inputs up front, you don’t need to wrap every piece of business logic in paranoid if-else blocks.

// Without validation
if (user != null && user.getEmail() != null && user.getEmail().contains("@")) {
    sendWelcomeEmail(user);
}

// With validation
sendWelcomeEmail(user); // Boom. Clean.

Suddenly your code looks like it was written by someone who enjoys life.

Bonus: Custom Validators for the Win

Sometimes built-in annotations aren’t enough. That’s fine, just write your own:

@Documented
@Constraint(validatedBy = CountryCodeValidator.class)
@Target({ FIELD })
@Retention(RUNTIME)
public @interface ValidCountryCode {
    String message() default "Invalid country code";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

public class CountryCodeValidator implements ConstraintValidator<ValidCountryCode, String> {
    @Override
    public boolean isValid(String code, ConstraintValidatorContext context) {
        return List.of("US", "AR", "DE").contains(code);
    }
}

Plug that into your DTO and you’re golden.

Validation Checklist

  • Validate input as early as possible, preferably at the DTO/controller level.
  • Don’t trust data from the outside world (or the inside world, honestly).
  • Add internal checks in your domain models if needed.
  • Use annotations and custom validators to keep things declarative.
  • Simplify logic by avoiding repetitive null and condition checks.

Clean validation = cleaner data = cleaner code. Your future self (and your team) will thank you.


Never Miss Another Tech Innovation

Concrete insights and actionable resources delivered straight to your inbox to boost your developer career.

My New ebook, Best Practices To Create A Backend With Spring Boot 3, is available now.

Best practices to create a backend with Spring Boot 3

Leave a comment

Discover more from The Dev World - Sergio Lema

Subscribe now to keep reading and get access to the full archive.

Continue reading