Diferença entre as restrições @NotNull, @NotEmpty e @NotBlank na validação de bean

Diferença entre as restrições @NotNull, @NotEmpty e @NotBlank na validação de bean

1. Visão geral

Bean Validationis a standard validation specification that allows us to easily validate domain objects by using a set of constraints declared in the form of annotations.

Embora no geral, o uso de implementações de validação de bean, comoHibernate Validator seja bastante simples, vale a pena explorar algumas diferenças sutis - ainda que relevantes - sobre como algumas dessas restrições são implementadas.

Neste tutorial,we’ll spot the differences between the @NotNull, @NotEmpty, and @NotBlank constraints.

2. As dependências do Maven

Para configurar rapidamente um ambiente de trabalho e testar o comportamento das restrições@NotNull,@NotEmpty e@NotBlank, primeiro precisamos adicionar as dependências Maven necessárias.

Neste caso, usaremosHibernate Validator, a implementação de referência de validação de bean, para validar nossos objetos de domínio.

Esta é a seção relevante de nosso arquivopom.xml:


    
        org.hibernate
        hibernate-validator
        6.0.13.Final
    
    
        org.glassfish
        javax.el
        3.0.0
     

UsaremosJUniteAssertJ em nossos testes de unidade, portanto, certifique-se de verificar as versões mais recentes dehibernate-validator,GlassFish’s EL implementation,junit eassertj-core no Maven Central.

3. A restrição@NotNull

Seguindo em frente, vamos implementar uma classe de domínioUserNotNull ingênua econstrain seu camponame com a anotação@NotNull:

public class UserNotNull {

    @NotNull(message = "Name may not be null")
    private String name;

    // standard constructors / getters / toString
}

Agora, precisamos ver como@NotNull realmente funciona nos bastidores

Para fazer isso, vamos criar um teste de unidade simples para a classe e validar algumas instâncias dele:

@BeforeClass
public static void setupValidatorInstance() {
    validator = Validation.buildDefaultValidatorFactory().getValidator();
}

@Test
public void whenNotNullName_thenNoConstraintViolations() {
    UserNotNull user = new UserNotNull("John");
    Set> violations = validator.validate(user);

    assertThat(violations.size()).isEqualTo(0);
}

@Test
public void whenNullName_thenOneConstraintViolation() {
    UserNotNull user = new UserNotNull(null);
    Set> violations = validator.validate(user);

    assertThat(violations.size()).isEqualTo(1);
}

@Test
public void whenEmptyName_thenNoConstraintViolations() {
    UserNotNull user = new UserNotNull("");
    Set> violations = validator.validate(user);

    assertThat(violations.size()).isEqualTo(0);
}

Como esperado, a restrição@NotNull não permitirá valores nulos para o (s) campo (s) restrito (s). Mesmo assim, os campos podem estar vazios.

Para entender melhor isso, vamos dar uma olhada no métodoNotNullValidator classisValid(), que a restrição@NotNull usa. A implementação do método é realmente trivial:

public boolean isValid(Object object) {
    return object != null;
}

Como mostrado acima,a field (e.g. CharSequence, Collection, Map, or Array) constrained with @NotNull must be not null. An empty value, however, is perfectly legal.

4. A restrição@NotEmpty

Agora, vamos implementar uma classe de amostraUserNotEmpty e usar a restrição@NotEmpty:

public class UserNotEmpty {

    @NotEmpty(message = "Name may not be empty")
    private String name;

    // standard constructors / getters / toString
}

Com a classe estabelecida, vamos apenas testá-la atribuindo valores diferentes ao camponame:

@Test
public void whenNotEmptyName_thenNoConstraintViolations() {
    UserNotEmpty user = new UserNotEmpty("John");
    Set> violations = validator.validate(user);

    assertThat(violations.size()).isEqualTo(0);
}

@Test
public void whenEmptyName_thenOneConstraintViolation() {
    UserNotEmpty user = new UserNotEmpty("");
    Set> violations = validator.validate(user);

    assertThat(violations.size()).isEqualTo(1);
}

@Test
public void whenNullName_thenOneConstraintViolation() {
    UserNotEmpty user = new UserNotEmpty(null);
    Set> violations = validator.validate(user);

    assertThat(violations.size()).isEqualTo(1);
}

A anotação@NotEmpty faz uso da implementaçãoisValid() da classe@NotNull e, adicionalmente, verifica se o tamanho / comprimento do objeto fornecido (claro, isso varia de acordo com o tipo de objeto que está sendo validado ) é maior que zero.

Resumindo,this means that a field (e.g. CharSequence, Collection, Map, or Array) constrained with @NotEmpty must be not null and its size/length must be greater than zero.

Além disso, podemos ser ainda mais restritivos se usarmos a anotação@NotEmpty em conjunto com@Size.

Ao fazer isso, também impomos que os valores de tamanho mínimo e máximo do objeto estejam dentro do intervalo mínimo / máximo especificado:

@NotEmpty(message = "Name may not be empty")
@Size(min = 2, max = 32, message = "Name must be between 2 and 32 characters long")
private String name;

5. A restrição@NotBlank

Da mesma forma, podemos restringir um campo de classe com a anotação@NotBlank:

public class UserNotBlank {

    @NotBlank(message = "Name may not be blank")
    private String name;

    // standard constructors / getters / toString

}

Na mesma linha, podemos implementar um teste de unidade para entender como funciona a restrição@NotBlank:

@Test
public void whenNotBlankName_thenNoConstraintViolations() {
    UserNotBlank user = new UserNotBlank("John");
    Set> violations = validator.validate(user);

    assertThat(violations.size()).isEqualTo(0);
}

@Test
public void whenBlankName_thenOneConstraintViolation() {
    UserNotBlank user = new UserNotBlank(" ");
    Set> violations = validator.validate(user);

    assertThat(violations.size()).isEqualTo(1);
}

@Test
public void whenEmptyName_thenOneConstraintViolation() {
    UserNotBlank user = new UserNotBlank("");
    Set> violations = validator.validate(user);

    assertThat(violations.size()).isEqualTo(1);
}

@Test
public void whenNullName_thenOneConstraintViolation() {
    UserNotBlank user = new UserNotBlank(null);
    Set> violations = validator.validate(user);

    assertThat(violations.size()).isEqualTo(1);
}

A anotação@NotBlank usa a classeNotBlankValidator, que verifica se o comprimento aparado de uma sequência de caracteres não está vazio:

public boolean isValid(
  CharSequence charSequence,
  ConstraintValidatorContext constraintValidatorContext)
    if (charSequence == null ) {
        return true;
    }
    return charSequence.toString().trim().length() > 0;
}

Engraçado o suficiente, o método retorna true para valores nulos. Então, podemos pensar que@NotBlank permite valores nulos, mas na verdade não permite.

O método isValid () da classe@NotNull é chamado após isValid () da classe@NotBlank, portanto, proibindo valores nulos.

Simplificando,a String field constrained with @NotBlank must be not null and the trimmed length must be greater than zero.

6. Uma comparação lado a lado

Até agora, demos uma olhada em profundidade em como as restrições@NotNull,@NotEmpty e@NotBlank operam individualmente nos campos de classe.

Vamos fazer uma rápida comparação lado a lado, para que possamos ter uma visão geral da funcionalidade das restrições e identificar facilmente suas diferenças:

  • @NotNull: umCharSequence restrito,Collection,Map, ouArray é válido desde que não seja nulo, mas pode estar vazio

  • @NotEmpty: umCharSequence restrito,Collection,Map, ouArray é válido, contanto que não seja nulo e seu tamanho / comprimento seja maior que zero

  • @NotBlank: umString restrito é válido, desde que não seja nulo e o comprimento aparado seja maior que zero

7. Conclusão

Neste artigo, examinamos as restriçõesNotNull,@NotEmpty e@NotBlank implementadas na Validação de Bean e destacamos suas semelhanças e diferenças.

Como de costume, todos os exemplos de código mostrados neste artigo estão disponíveisover on GitHub.