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.