DEV Community

Cover image for Jakarta Validator em Construtores
Uiratan Cavalcante
Uiratan Cavalcante

Posted on

Jakarta Validator em Construtores

As as anotações de validação do Jakarta Validation (Bean Validation) @notblank, @Email e @Size aplicadas nos parâmetros do construtor não impedem a criação do objeto em estado inconsistente automaticamente.

❌ Por que essas anotações não impedem a criação do objeto?

  • As anotações de validação do Jakarta Validation (ex: @NotBlank, @Size) não são verificadas automaticamente dentro do construtor. Elas só são aplicadas quando o Spring ou outro framework as invoca explicitamente com um mecanismo de validação.
  • No código apresentado, nada impede que o construtor receba valores inválidos, pois o Java puro não interpreta essas anotações.

🛠 Como evitar a criação de um objeto inconsistente?

Para garantir que a validação ocorra dentro do construtor, você pode fazer uma das seguintes abordagens:

1️⃣ Utilizar @Valid em um DTO e converter para a entidade (Recomendado ✅)

Em um Controller do Spring Boot, a validação acontece automaticamente quando você usa @Valid na entrada de um método.

import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/autores")
public class AutorController {

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public void cadastrarAutor(@RequestBody @Valid AutorRequest autorRequest) {
        Autor autor = autorRequest.toModel(); // Conversão segura após validação
        System.out.println("Autor cadastrado: " + autor.getNome());
    }
}
Enter fullscreen mode Exit fullscreen mode

No DTO:

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

public class AutorRequest {
    @NotBlank(message = "O nome é obrigatório")
    private String nome;

    @NotBlank(message = "O email é obrigatório")
    @Email(message = "Formato de email inválido")
    private String email;

    @NotBlank(message = "A descrição é obrigatória")
    @Size(max = 400, message = "A descrição não pode passar de 400 caracteres")
    private String descricao;

    public AutorRequest(String nome, String email, String descricao) {
        this.nome = nome;
        this.email = email;
        this.descricao = descricao;
    }

    public Autor toModel() {
        return new Autor(nome, email, descricao);
    }
}
Enter fullscreen mode Exit fullscreen mode

✅ Aqui a validação acontece antes do objeto Autor ser criado!


2️⃣ Forçar a validação manualmente no construtor

Caso queira manter as anotações no construtor e garantir que a validação seja feita dentro da própria classe, você pode chamar um Validator manualmente:

import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import jakarta.validation.constraints.*;

import java.time.Instant;
import java.util.Set;
import jakarta.validation.ConstraintViolation;

public class Autor {

    @NotBlank(message = "O nome é obrigatório")
    private final String nome;

    @NotBlank(message = "O email é obrigatório")
    @Email(message = "Formato de email inválido")
    private final String email;

    @NotBlank(message = "A descrição é obrigatória")
    @Size(max = 400, message = "A descrição não pode passar de 400 caracteres")
    private final String descricao;

    private final Instant instanteCriacao;

    public Autor(String nome, String email, String descricao) {
        this.nome = nome;
        this.email = email;
        this.descricao = descricao;
        this.instanteCriacao = Instant.now();
        validar(this); // Chama a validação manualmente
    }

    private void validar(Autor autor) {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();
        Set<ConstraintViolation<Autor>> violations = validator.validate(autor);

        if (!violations.isEmpty()) {
            throw new IllegalArgumentException(violations.iterator().next().getMessage());
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

🚨 Desvantagem: Essa abordagem é um pouco mais burocrática, pois exige a criação de um Validator manualmente.


3️⃣ Usar @Validated com Spring para validar no construtor

Caso você utilize o Spring, pode anotar a classe como @Validated e injetar um Validator:

import jakarta.validation.constraints.*;
import org.springframework.validation.annotation.Validated;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.Instant;

@Component
@Validated
public class Autor {

    private final String nome;
    private final String email;
    private final String descricao;
    private final Instant instanteCriacao;

    @Autowired
    public Autor(
        @NotBlank(message = "O nome é obrigatório") String nome,
        @NotBlank(message = "O email é obrigatório") @Email(message = "Formato de email inválido") String email,
        @NotBlank(message = "A descrição é obrigatória") @Size(max = 400, message = "A descrição não pode passar de 400 caracteres") String descricao
    ) {
        this.nome = nome;
        this.email = email;
        this.descricao = descricao;
        this.instanteCriacao = Instant.now();
    }
}
Enter fullscreen mode Exit fullscreen mode

✅ Essa abordagem usa o Spring para injetar automaticamente a validação.


🎯 Conclusão

🔴 As anotações @NotBlank, @Email, @Size dentro do construtor NÃO impedem a criação do objeto automaticamente.

✅ Para evitar um estado inconsistente, é melhor validar os dados antes de criar a instância, usando um DTO (@Valid no Controller).

✅ Se necessário, você pode chamar a validação manualmente dentro do construtor ou usar Spring para gerenciá-la com @Validated.

Image of Timescale

Timescale – the developer's data platform for modern apps, built on PostgreSQL

Timescale Cloud is PostgreSQL optimized for speed, scale, and performance. Over 3 million IoT, AI, crypto, and dev tool apps are powered by Timescale. Try it free today! No credit card required.

Try free

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay