1. 概要

Java Bean Validation 仕様の2.0バージョンでは、コンテナの要素を検証する可能性など、いくつかの新機能が追加されています。

この新機能は、Java8で導入された型注釈を利用します。 したがって、動作するにはJavaバージョン8以降が必要です。

検証アノテーションは、コレクション、オプションオブジェクト、その他の組み込みコンテナーやカスタムコンテナーなどのコンテナーに追加できます。

Java Bean Validation の概要と、必要な Maven の依存関係を設定する方法については、以前の記事を確認してください。

次のセクションでは、各タイプのコンテナの要素の検証に焦点を当てます。

2. コレクション要素

タイプjava.util.Iterable java.util.List 、およびjava.util.Mapのコレクションの要素に検証アノテーションを追加できます。

リストの要素を検証する例を見てみましょう。

public class Customer {    
     List<@NotBlank(message="Address must not be blank") String> addresses;
    
    // standard getters, setters 
}

上記の例では、Customerクラスのaddressesプロパティを定義しました。このクラスには、空にできないStrings要素が含まれています。

@NotBlank検証は、コレクション全体ではなく、String要素に適用されることに注意してください。 コレクションが空の場合、検証は適用されません。

空のStringaddressesリストに追加しようとすると、検証フレームワークがConstraintViolationを返すことを確認しましょう。

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

    customer.setAddresses(Collections.singletonList(" "));
    Set<ConstraintViolation<Customer>> violations = 
      validator.validate(customer);
    
    assertEquals(1, violations.size());
    assertEquals("Address must not be blank", 
      violations.iterator().next().getMessage());
}

次に、タイプMapのコレクションの要素を検証する方法を見てみましょう。

public class CustomerMap {
    
    private Map<@Email String, @NotNull Customer> customers;
    
    // standard getters, setters
}

Map要素のキーと値の両方に検証アノテーションを追加できることに注意してください

無効な電子メールを含むエントリを追加すると、検証エラーが発生することを確認しましょう。

@Test
public void whenInvalidEmail_thenValidationFails() {
    CustomerMap map = new CustomerMap();
    map.setCustomers(Collections.singletonMap("john", new Customer()));
    Set<ConstraintViolation<CustomerMap>> violations
      = validator.validate(map);
 
    assertEquals(1, violations.size());
    assertEquals(
      "Must be a valid email", 
      violations.iterator().next().getMessage());
}

3. オプション

検証制約は、オプション値にも適用できます。

private Integer age;

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

年齢が低すぎるCustomerを作成して、検証エラーが発生することを確認しましょう。

@Test
public void whenAgeTooLow_thenValidationFails() {
    Customer customer = new Customer();
    customer.setName("John");
    customer.setAge(15);
    Set<ConstraintViolation<Customer>> violations
      = validator.validate(customer);
 
    assertEquals(1, violations.size());
}

一方、 age がnullの場合、Optional値は検証されません。

@Test
public void whenAgeNull_thenValidationSucceeds() {
    Customer customer = new Customer();
    customer.setName("John");
    Set<ConstraintViolation<Customer>> violations
      = validator.validate(customer);
 
    assertEquals(0, violations.size());
}

4. 非汎用コンテナ要素

タイプ引数にアノテーションを追加するだけでなく、 @UnwrapByDefault アノテーションが付いたタイプの値エクストラクタがある限り、非ジェネリックコンテナに検証を適用することもできます。

値エクストラクタは、検証のためにコンテナから値を抽出するクラスです。

リファレンス実装には、OptionalInt、OptionalLong、OptionalDoubleの値エクストラクタが含まれています。

@Min(1)
private OptionalInt numberOfOrders;

この場合、 @Min アノテーションは、コンテナではなく、ラップされたInteger値に適用されます。

5. カスタムコンテナ要素

組み込みの値エクストラクタに加えて、独自の値エクストラクタを定義して、コンテナタイプに登録することもできます。

このようにして、カスタムコンテナの要素に検証アノテーションを追加できます。

companyNameプロパティを含む新しいProfileクラスを追加しましょう。

public class Profile {
    private String companyName;
    
    // standard getters, setters 
}

次に、Customerクラスに@NotBlankアノテーションを付けてProfileプロパティを追加します。これにより、companyNameが空の文字列

@NotBlank
private Profile profile;

これを機能させるには、プロファイルオブジェクトではなく、companyNameプロパティに適用される検証を決定する値エクストラクタが必要です。

ValueExtractor インターフェイスを実装し、 extractValue()メソッドをオーバーライドするProfileValueExtractorクラスを追加しましょう。

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

このクラスでは、@ExtractedValueアノテーションを使用して抽出された値のタイプも指定する必要があります。

また、 @UnwrapByDefaultアノテーションを追加しました。これは、検証をコンテナではなくアンラップされた値に適用する必要があることを指定します。

最後に、javax.validation.valueextraction.ValueExtractorというファイルをMETA-INF/servicesディレクトリに追加してクラスを登録する必要があります。このファイルにはのフルネームが含まれています。 ] ProfileValueExtractor クラス:

org.baeldung.javaxval.container.validation.valueextractors.ProfileValueExtractor

ここで、プロファイルプロパティを使用して Customer オブジェクトを検証し、 companyName を空にすると、検証エラーが発生します。

@Test
public void whenProfileCompanyNameBlank_thenValidationFails() {
    Customer customer = new Customer();
    customer.setName("John");
    Profile profile = new Profile();
    profile.setCompanyName(" ");
    customer.setProfile(profile);
    Set<ConstraintViolation<Customer>> violations
     = validator.validate(customer);
 
    assertEquals(1, violations.size());
}

hibernate-validator-annotation-processor を使用している場合、カスタムコンテナクラスに @UnwrapByDefault とマークされているときに検証アノテーションを追加すると、コンパイルエラーが発生することに注意してください。バージョン6.0.2。

これは既知の問題であり、将来のバージョンで解決される可能性があります。

6. 結論

この記事では、 Java BeanValidation2.0を使用していくつかのタイプのコンテナー要素を検証する方法を示しました。

の完全なソースコードはGitHubにあります。