Containerelemente mit Bean Validation 2.0 prüfen

Überprüfen von Containerelementen mit Bean Validation 2.0

1. Überblick

Die 2.0-Version derJava Bean Validation-Spezifikation fügt mehrere neue Funktionen hinzu, darunter die Möglichkeit, Elemente von Containern zu validieren.

Diese neue Funktionalität nutzt die in Java 8 eingeführten Typanmerkungen. Daher ist Java Version 8 oder höher erforderlich, um zu funktionieren.

Validierungsanmerkungen können Containern wie Sammlungen,Optional-Objekten und anderen integrierten sowie benutzerdefinierten Containern hinzugefügt werden.

Eine Einführung inJava Bean Validation und das Einrichten der benötigtenMaven-Abhängigkeiten finden Sie in unserenprevious article here.

In den folgenden Abschnitten konzentrieren wir uns auf die Validierung von Elementen für jeden Containertyp.

2. Sammlungselemente

Wir können Elementen von Sammlungen vom Typjava.util.Iterable,java.util.List undjava.util.Map Validierungsanmerkungen hinzufügen.

Sehen wir uns ein Beispiel für die Validierung der Elemente einer Liste an:

public class Customer {
     List<@NotBlank(message="Address must not be blank") String> addresses;

    // standard getters, setters
}

Im obigen Beispiel haben wir eineaddresses-Eigenschaft für eineCustomer-Klasse definiert, die Elemente enthält, die nicht leer sein dürfenStrings.

Beachten Sie, dassthe @NotBlank validation applies to the String elements, and not the entire collection. Wenn die Sammlung leer ist, wird keine Validierung angewendet.

Lassen Sie uns überprüfen, ob das Validierungsframework einConstraintViolation zurückgibt, wenn wir versuchen, ein leeresString zuraddresses-Liste hinzuzufügen:

@Test
public void whenEmptyAddress_thenValidationFails() {
    Customer customer = new Customer();
    customer.setName("John");

    customer.setAddresses(Collections.singletonList(" "));
    Set> violations =
      validator.validate(customer);

    assertEquals(1, violations.size());
    assertEquals("Address must not be blank",
      violations.iterator().next().getMessage());
}

Als nächstes wollen wir sehen, wie wir die Elemente einer Sammlung vom TypMap validieren können:

public class CustomerMap {

    private Map<@Email String, @NotNull Customer> customers;

    // standard getters, setters
}

Beachten Sie, dasswe can add validation annotations for both the key and the value of a Map element.

Lassen Sie uns überprüfen, ob das Hinzufügen eines Eintrags mit einer ungültigen E-Mail zu einem Validierungsfehler führt:

@Test
public void whenInvalidEmail_thenValidationFails() {
    CustomerMap map = new CustomerMap();
    map.setCustomers(Collections.singletonMap("john", new Customer()));
    Set> violations
      = validator.validate(map);

    assertEquals(1, violations.size());
    assertEquals(
      "Must be a valid email",
      violations.iterator().next().getMessage());
}

3. Optional Werte

Validierungsbeschränkungen können auch auf den Wert vonOptionalangewendet werden:

private Integer age;

public Optional<@Min(18) Integer> getAge() {
    return Optional.ofNullable(age);
}

Erstellen wir einCustomer mit einem zu niedrigen Alter - und stellen Sie sicher, dass dies zu einem Validierungsfehler führt:

@Test
public void whenAgeTooLow_thenValidationFails() {
    Customer customer = new Customer();
    customer.setName("John");
    customer.setAge(15);
    Set> violations
      = validator.validate(customer);

    assertEquals(1, violations.size());
}

Wenn andererseitsage null ist, wird der Wert vonOptional nicht validiert:

@Test
public void whenAgeNull_thenValidationSucceeds() {
    Customer customer = new Customer();
    customer.setName("John");
    Set> violations
      = validator.validate(customer);

    assertEquals(0, violations.size());
}

4. Nicht generische Containerelemente

Neben dem Hinzufügen von Annotationen für Typargumente können wir auch die Validierung auf nicht generische Container anwenden, sofern für den Typ ein Wertextraktor mit der Annotation@UnwrapByDefaultvorhanden ist.

Wertextraktoren sind die Klassen, die die Werte zur Validierung aus den Containern extrahieren.

The reference implementation contains value extractors for OptionalInt, OptionalLong and OptionalDouble:

@Min(1)
private OptionalInt numberOfOrders;

In diesem Fall gilt die Annotation@Minfür den umschlossenen WertIntegerund nicht für den Container.

5. Benutzerdefinierte Containerelemente

Zusätzlich zu den eingebauten Wertextraktoren können wir auch eigene definieren und diese mit einem Containertyp registrieren.

Auf diese Weise können wir Validierungsanmerkungen zu Elementen unserer benutzerdefinierten Container hinzufügen.

Fügen wir eine neueProfile-Klasse hinzu, die einecompanyName-Eigenschaft enthält:

public class Profile {
    private String companyName;

    // standard getters, setters
}

Als Nächstes möchten wir derCustomer-Eigenschaft eineProfile-Eigenschaft mit einer@NotBlank-Annotation hinzufügen. Dadurch wird überprüft, obcompanyName kein leeresString ist:

@NotBlank
private Profile profile;

Damit dies funktioniert, benötigen wir einen Wertextraktor, der die Validierung festlegt, die auf die EigenschaftcompanyNameund nicht direkt auf das Profilobjekt angewendet werden soll.

Fügen wir eineProfileValueExtractor-Klasse hinzu, die dieValueExtractor-Schnittstelle implementiert und dieextractValue()-Methode überschreibt:

@UnwrapByDefault
public class ProfileValueExtractor
  implements ValueExtractor<@ExtractedValue(type = String.class) Profile> {

    @Override
    public void extractValues(Profile originalValue,
      ValueExtractor.ValueReceiver receiver) {
        receiver.value(null, originalValue.getCompanyName());
    }
}

Diese Klasse muss auch den Typ des Werts angeben, der mit der Annotation@ExtractedValueextrahiert wurde.

Außerdem haben wirthe @UnwrapByDefault annotation that specifies the validation should be applied to the unwrapped value and not the container hinzugefügt.

Schließlich müssen wir die Klasse registrieren, indem wir dem VerzeichnisMETA-INF/serviceseine Datei mit dem Namenjavax.validation.valueextraction.ValueExtractor hinzufügen, die den vollständigen Namen unserer KlasseProfileValueExtractorenthält:

org.example.javaxval.container.validation.valueextractors.ProfileValueExtractor

Wenn wir nun einCustomer-Objekt mit einerprofile-Eigenschaft mit einem leerencompanyName validieren, wird ein Validierungsfehler angezeigt:

@Test
public void whenProfileCompanyNameBlank_thenValidationFails() {
    Customer customer = new Customer();
    customer.setName("John");
    Profile profile = new Profile();
    profile.setCompanyName(" ");
    customer.setProfile(profile);
    Set> violations
     = validator.validate(customer);

    assertEquals(1, violations.size());
}

Wenn Siehibernate-validator-annotation-processor verwenden, führt das Hinzufügen einer Validierungsanmerkung zu einer benutzerdefinierten Containerklasse, wenn diese als@UnwrapByDefault markiert ist, in Version 6.0.2 zu einem Kompilierungsfehler.

Dies ist einknown issue und wird wahrscheinlich in einer zukünftigen Version behoben.

6. Fazit

In diesem Artikel haben wir gezeigt, wie wir verschiedene Arten von Containerelementen mitJava Bean Validation 2.0. validieren können

Sie finden den vollständigen Quellcode der Beispieleover on GitHub.