Differenza tra i vincoli @NotNull, @NotEmpty e @NotBlank nella convalida dei bean

1. Panoramica

Bean Validation è una specifica di convalida standard che ci consente di convalidare facilmente gli oggetti di dominio utilizzando una serie di vincoli dichiarati sotto forma di annotazioni .

Sebbene nel complesso, l'uso di implementazioni di convalida dei bean come Hibernate Validator sia abbastanza semplice, vale la pena esplorare alcune differenze sottili, ma rilevanti, riguardo a come vengono implementati alcuni di questi vincoli.

In questo tutorial, avremo individuare le differenze tra le @NotNull , @NotEmpty, e @NotBlank vincoli .

2. Le dipendenze di Maven

Per configurare rapidamente un ambiente di lavoro e testare il comportamento dei vincoli @NotNull , @NotEmpty e @NotBlank , è prima necessario aggiungere le dipendenze Maven richieste.

In questo caso, utilizzeremo Hibernate Validator, l'implementazione di riferimento per la convalida dei bean, per convalidare i nostri oggetti di dominio.

Ecco la sezione pertinente del nostro file pom.xml :

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

Useremo JUnit e AssertJ nei nostri unit test, quindi assicurati di controllare le ultime versioni di hibernate-validator, l'implementazione EL di GlassFish, junit e assertj-core su Maven Central.

3. Il vincolo @NotNull

Andando avanti, implementiamo una classe di dominio UserNotNull ingenua e vincoliamo il campo del nome con l' annotazione @NotNull :

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

Ora, dobbiamo vedere come @NotNull funziona effettivamente sotto il cofano .

Per fare ciò, creiamo un semplice unit test per la classe e convalidiamo alcune istanze di esso:

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

Come previsto, il vincolo @NotNull non consentirà valori nulli per i campi vincolati. Anche così, i campi possono essere vuoti.

Per comprendere meglio questo, diamo un'occhiata alla NotNullValidator class' isValid () metodo, che i @NotNull usi vincolo. L'implementazione del metodo è davvero banale:

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

Come mostrato sopra, un campo (ad esempio CharSequence , Collection , Map o Array) vincolato con @NotNull non deve essere nullo. Un valore vuoto, tuttavia, è perfettamente legale .

4. Il vincolo @NotEmpty

Ora, implementiamo una classe UserNotEmpty di esempio e usiamo il vincolo @NotEmpty :

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

Con la classe in atto, proviamola semplicemente assegnando valori diversi al campo del nome :

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

I @NotEmpty marchi annotazione uso del @NotNull classe isValid () attuazione e inoltre controlla che la dimensione / lunghezza dell'oggetto fornito (naturalmente, questo varia a seconda del tipo di oggetto convalidato) è maggiore di zero.

In poche parole, ciò significa che un campo (ad esempio CharSequence , Collection , Map o Array) vincolato con @NotEmpty non deve essere nullo e la sua dimensione / lunghezza deve essere maggiore di zero .

Inoltre, possiamo essere ancora più restrittivi se usiamo l' annotazione @NotEmpty insieme a @Size.

In tal modo, imporremmo anche che i valori di dimensione minima e massima dell'oggetto siano all'interno dell'intervallo minimo / massimo specificato:

@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. Il vincolo @NotBlank

Allo stesso modo, possiamo vincolare un campo di classe con l' annotazione @NotBlank :

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

Sulla stessa linea, possiamo implementare uno unit test per capire come funziona il vincolo @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' annotazione @NotBlank utilizza la classe NotBlankValidator , che controlla che la lunghezza ridotta di una sequenza di caratteri non sia vuota:

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

Abbastanza divertente, il metodo restituisce true per valori null. Quindi, potremmo pensare che @NotBlank consenta valori nulli, ma in realtà non lo fa.

Il @NotNull classe metodo isValid () viene chiamato dopo il @NotBlank classe isValid (), quindi vietando valori nulli.

Per dirla semplicemente, un campo String vincolato con @NotBlank non deve essere nullo e la lunghezza tagliata deve essere maggiore di zero .

6. Un confronto fianco a fianco

Finora, abbiamo esaminato in modo approfondito come i vincoli @NotNull , @NotEmpty e @NotBlank operano individualmente sui campi di classe.

Eseguiamo un rapido confronto fianco a fianco, in modo da poter avere una visione a volo d'uccello della funzionalità dei vincoli e individuare facilmente le loro differenze:

  • @NotNull: un CharSequence , Collection , Map o Array vincolato è valido fintanto che non è nullo, ma può essere vuoto
  • @NotEmpty: una CharSequence , Collection , Map o Array vincolata è valida fintanto che non è nulla e la sua dimensione / lunghezza è maggiore di zero
  • @NotBlank: una stringa vincolata è valida fintanto che non è nulla e la lunghezza tagliata è maggiore di zero

7. Conclusione

In questo articolo, abbiamo esaminato i vincoli NotNull , @NotEmpty e @NotBlank implementati in Bean Validation e ne abbiamo evidenziato le somiglianze e le differenze.

Come al solito, tutti gli esempi di codice mostrati in questo articolo sono disponibili su GitHub.