Différence entre les contraintes @NotNull, @NotEmpty et @NotBlank dans la validation de bean

Différence entre les contraintes @NotNull, @NotEmpty et @NotBlank dans la validation de bean

1. Vue d'ensemble

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.

Bien que dans l'ensemble, l'utilisation d'implémentations de validation de bean telles queHibernate Validator soit assez simple, il vaut la peine d'explorer quelques différences subtiles - mais pertinentes - concernant la manière dont certaines de ces contraintes sont implémentées.

Dans ce didacticiel,we’ll spot the differences between the @NotNull, @NotEmpty, and @NotBlank constraints.

2. Les dépendances Maven

Pour configurer rapidement un environnement de travail et tester le comportement des contraintes@NotNull,@NotEmpty et@NotBlank, nous devons d'abord ajouter les dépendances Maven requises.

Dans ce cas, nous utiliseronsHibernate Validator, l'implémentation de référence de validation de bean, pour valider nos objets de domaine.

Voici la section pertinente de notre fichierpom.xml:


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

Nous utiliseronsJUnit etAssertJ dans nos tests unitaires, alors assurez-vous de vérifier les dernières versions dehibernate-validator,GlassFish’s EL implementation,junit etassertj-core sur Maven Central.

3. La contrainte@NotNull

À l'avenir, implémentons une classe de domaine naïveUserNotNull etconstrain son champname avec l'annotation@NotNull:

public class UserNotNull {

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

    // standard constructors / getters / toString
}

Maintenant, nous devons voir comment@NotNull fonctionne réellement sous le capot

Pour ce faire, créons un test unitaire simple pour la classe et validons-en quelques instances:

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

Comme prévu, la contrainte@NotNull n'autorisera pas les valeurs nulles pour le ou les champs contraints. Même dans ce cas, le ou les champs peuvent être vides.

Pour mieux comprendre cela, examinons la méthodeNotNullValidator classisValid(), que la contrainte@NotNull utilise. L'implémentation de la méthode est vraiment triviale:

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

Comme indiqué ci-dessus,a field (e.g. CharSequence, Collection, Map, or Array) constrained with @NotNull must be not null. An empty value, however, is perfectly legal.

4. La contrainte@NotEmpty

Maintenant, implémentons un exemple de classeUserNotEmpty et utilisons la contrainte@NotEmpty:

public class UserNotEmpty {

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

    // standard constructors / getters / toString
}

Une fois la classe en place, testons-la simplement en attribuant différentes valeurs au champname:

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

L'annotation@NotEmpty utilise l'implémentation de la classe@NotNull 'isValid() et vérifie en outre que la taille / longueur de l'objet fourni (bien sûr, cela varie en fonction du type d'objet validé ) est supérieur à zéro.

En un mot,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.

De plus, nous pouvons être encore plus restrictifs si nous utilisons l'annotation@NotEmpty en conjonction avec@Size.

Ce faisant, nous nous assurons également que les valeurs de taille minimale et maximale de l'objet sont comprises dans la plage min / max spécifiée:

@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 contrainte@NotBlank

De même, nous pouvons contraindre un champ de classe avec l'annotation@NotBlank:

public class UserNotBlank {

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

    // standard constructors / getters / toString

}

Dans le même esprit, nous pouvons implémenter un test unitaire pour comprendre le fonctionnement de la contrainte@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);
}

L'annotation@NotBlank utilise la classeNotBlankValidator, qui vérifie que la longueur tronquée d'une séquence de caractères n'est pas vide:

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

Assez drôle, la méthode retourne true pour les valeurs null. Ainsi, nous pourrions penser que@NotBlank autorise les valeurs nulles, mais ce n’est pas le cas.

La méthode isValid () de la classe@NotNull est appelée après la classe@NotBlank isValid (), ce qui interdit les valeurs nulles.

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

6. Une comparaison côte à côte

Jusqu'à présent, nous avons examiné en détail comment les contraintes@NotNull,@NotEmpty et@NotBlank fonctionnent individuellement sur les champs de classe.

Faisons une rapide comparaison côte à côte, afin que nous puissions avoir une vue d'ensemble des fonctionnalités des contraintes et repérer facilement leurs différences:

  • @NotNull: a contraintCharSequence,Collection,Map, ouArray est valide tant qu'il n'est pas nul, mais il peut être vide

  • @NotEmpty: a contraintCharSequence,Collection,Map, ouArray est valide tant qu'il n'est pas nul et que sa taille / longueur est supérieure à zéro

  • @NotBlank: unString contraint est valide tant qu'il n'est pas nul et que la longueur tronquée est supérieure à zéro

7. Conclusion

Dans cet article, nous avons examiné les contraintesNotNull,@NotEmpty et@NotBlank implémentées dans Bean Validation et mis en évidence leurs similitudes et leurs différences.

Comme d'habitude, tous les exemples de code présentés dans cet article sont disponiblesover on GitHub.