Diferencia entre las restricciones de @NotNull, @NotEmpty y @NotBlank en la validación de Bean

1. Información general

Bean Validation es una especificación de validación estándar que nos permite validar fácilmente objetos de dominio mediante el uso de un conjunto de restricciones declaradas en forma de anotaciones .

Si bien en general, el uso de implementaciones de validación de beans como Hibernate Validator es bastante sencillo, vale la pena explorar algunas diferencias sutiles, pero relevantes, con respecto a cómo se implementan algunas de estas restricciones.

En este tutorial, vamos a encontrar las diferencias entre las @NotNull , @NotEmpty, y @NotBlank limitaciones .

2. Las dependencias de Maven

Para configurar rápidamente un entorno de trabajo y probar el comportamiento de las restricciones @NotNull , @NotEmpty y @NotBlank , primero debemos agregar las dependencias de Maven necesarias.

En este caso, usaremos Hibernate Validator, la implementación de referencia de validación de beans, para validar nuestros objetos de dominio.

Aquí está la sección relevante de nuestro archivo pom.xml :

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

Usaremos JUnit y AssertJ en nuestras pruebas unitarias, así que asegúrese de verificar las últimas versiones de hibernate-validator, implementación EL de GlassFish, junit y assertj-core en Maven Central.

3. La restricción @NotNull

En el futuro, implementemos una clase de dominio UserNotNull ingenua y restrinjamos su campo de nombre con la anotación @NotNull :

public class UserNotNull { @NotNull(message = "Name may not be null") private String name; // standard constructors / getters / toString }

Ahora, necesitamos ver cómo funciona @NotNull bajo el capó .

Para hacerlo, creemos una prueba unitaria simple para la clase y validemos algunas instancias de la misma:

@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 se esperaba, la restricción @NotNull no permitirá valores nulos para los campos restringidos. Aun así, los campos pueden estar vacíos.

Para entender mejor esto, vamos a ver el NotNullValidator clase isValid () método, que los @NotNull usos de restricción. La implementación del método es realmente trivial:

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

Como se muestra arriba, un campo (por ejemplo , CharSequence , Collection , Map o Array) restringido con @NotNull no debe ser nulo. Sin embargo, un valor vacío es perfectamente legal .

4. La restricción @NotEmpty

Ahora, implementemos una clase UserNotEmpty de muestra y usemos la restricción @NotEmpty :

public class UserNotEmpty { @NotEmpty(message = "Name may not be empty") private String name; // standard constructors / getters / toString }

Con la clase en su lugar, probémosla asignando diferentes valores al campo de nombre :

@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); }
      
     
    

La anotación @NotEmpty hace uso de la implementación isValid () de la clase @NotNull y además verifica que el tamaño / longitud del objeto suministrado (por supuesto, esto varía según el tipo de objeto que se valida) es mayor que cero.

En pocas palabras, esto significa que un campo (por ejemplo , CharSequence , Collection , Map o Array) restringido con @NotEmpty no debe ser nulo y su tamaño / longitud debe ser mayor que cero .

Además, podemos ser aún más restrictivos si usamos la anotación @NotEmpty junto con @Size.

Al hacerlo, también exigiríamos que los valores de tamaño mínimo y máximo del objeto estén dentro del rango 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. La restricción @NotBlank

De manera similar, podemos restringir un campo de clase con la anotación @NotBlank :

public class UserNotBlank { @NotBlank(message = "Name may not be blank") private String name; // standard constructors / getters / toString }

En la misma línea, podemos implementar una prueba unitaria para comprender cómo funciona la restricción @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); } 
       
      
     
    

La anotación @NotBlank usa la clase NotBlankValidator , que verifica que la longitud recortada de una secuencia de caracteres no esté vacía:

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

Curiosamente, el método devuelve verdadero para valores nulos. Entonces, podríamos pensar que @NotBlank sí permite valores nulos, pero en realidad no lo hace.

El @NotNull clase método isValid () se llama después de la @NotBlank clase isValid (), por lo tanto, prohibiendo valores nulos.

En pocas palabras, un campo String restringido con @NotBlank no debe ser nulo y la longitud recortada debe ser mayor que cero .

6. Una comparación lado a lado

Hasta ahora, hemos analizado en profundidad cómo las restricciones @NotNull , @NotEmpty y @NotBlank operan individualmente en los campos de clase.

Realicemos una comparación rápida en paralelo, para que podamos tener una vista panorámica de la funcionalidad de las restricciones y detectar fácilmente sus diferencias:

  • @NotNull: una CharSequence , Collection , Map o Array restringida es válida siempre que no sea nula, pero puede estar vacía
  • @NotEmpty: una CharSequence , Collection , Map o Array restringida es válida siempre que no sea nula y su tamaño / longitud sea mayor que cero
  • @NotBlank: una cadena restringida es válida siempre que no sea nula y la longitud recortada sea mayor que cero

7. Conclusión

En este artículo, analizamos las restricciones NotNull , @NotEmpty y @NotBlank implementadas en Bean Validation y resaltamos sus similitudes y diferencias.

Como de costumbre, todos los ejemplos de código que se muestran en este artículo están disponibles en GitHub.