116

モデルを検証するためにJPA2.0/Hibernate検証を使用しています。現在、2つのフィールドの組み合わせを検証する必要がある状況があります。

public class MyModel {
    public Integer getValue1() {
        //...
    }
    public String getValue2() {
        //...
    }
}

モデルは、とが両方の場合は無効であり、それ以外の場合は有効です。getValue1()getValue2()null

JPA 2.0 / Hibernateでこの種の検証を実行するにはどうすればよいですか?単純な@NotNullアノテーションでは、検証に合格するには、両方のゲッターがnull以外である必要があります。

4

5 に答える 5

121

複数のプロパティの検証には、クラスレベルの制約を使用する必要があります。Bean Validation Sneak PeekパートIIから :カスタム制約:

クラスレベルの制約

複数のプロパティにまたがる制約を適用する機能、または複数のプロパティに依存する制約を表現する機能について懸念を表明している方もいらっしゃいます。古典的な例は、アドレス検証です。アドレスには複雑なルールがあります。

  • 通りの名前はやや標準的であり、確かに長さの制限が必要です
  • 郵便番号の構造は国によって異なります
  • 多くの場合、都市は郵便番号に関連付けられ、エラーチェックを実行できます(検証サービスにアクセスできる場合)。
  • これらの相互依存性のために、単純なプロパティレベルの制約が法案に適合します

BeanValidation仕様によって提供されるソリューションは2つあります。

  • グループとグループシーケンスを使用して、他の一連の制約の前に一連の制約を強制的に適用する機能を提供します。このテーマについては、次のブログエントリで取り上げます。
  • クラスレベルの制約を定義できます

クラスレベルの制約は、プロパティではなくクラスに適用される通常の制約(アノテーション/実装デュオ)です。言い換えると、クラスレベルの制約は、で(プロパティ値ではなく)オブジェクトインスタンスを受け取りますisValid

@AddressAnnotation 
public class Address {
    @NotNull @Max(50) private String street1;
    @Max(50) private String street2;
    @Max(10) @NotNull private String zipCode;
    @Max(20) @NotNull String city;
    @NotNull private Country country;
    
    ...
}

@Constraint(validatedBy = MultiCountryAddressValidator.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AddressAnnotation {
    String message() default "{error.address}";
    Class<?>[] groups() default { };
    Class<? extends Payload>[] payload() default { };
}

public class MultiCountryAddressValidator implements ConstraintValidator<AddressAnnotation, Address> {
    public void initialize(AddressAnnotation constraintAnnotation) {
    // initialize the zipcode/city/country correlation service
    }

    /**
     * Validate zipcode and city depending on the country
     */
    public boolean isValid(Address object, ConstraintValidatorContext context) {
        if (!(object instanceof Address)) {
            throw new IllegalArgumentException("@AddressAnnotation only applies to Address objects");
        }
        Address address = (Address) object;
        Country country = address.getCountry();
        if (country.getISO2() == "FR") {
            // check address.getZipCode() structure for France (5 numbers)
            // check zipcode and city correlation (calling an external service?)
            return isValid;
        } else if (country.getISO2() == "GR") {
            // check address.getZipCode() structure for Greece
            // no zipcode / city correlation available at the moment
            return isValid;
        }
        // ...
    }
}

高度なアドレス検証ルールは、アドレスオブジェクトから除外され、によって実装されてい MultiCountryAddressValidatorます。オブジェクトインスタンスにアクセスすることにより、クラスレベルの制約には多くの柔軟性があり、複数の相関するプロパティを検証できます。順序付けはここでは方程式から除外されていることに注意してください。次の投稿でそれに戻ります。

専門家グループは、さまざまな複数のプロパティサポートアプローチについて議論しました。クラスレベルの制約アプローチは、依存関係を含む他のプロパティレベルのアプローチと比較して、十分な単純さと柔軟性の両方を提供すると思います。フィードバックは大歓迎です。

于 2010-05-06T19:30:54.913 に答える
13

カスタム クラス レベル バリデーターは、Bean Validation 仕様にとどまりたい場合に最適な方法です (例はこちら) 。

Hibernate Validator 機能を使用したい場合は、Validator-4.1.0.Final 以降で提供されている@ScriptAssertを使用できます。JavaDoc からの抜粋:

スクリプト式は、 JSR 223 ("Scripting for the JavaTM Platform") 互換エンジンがクラスパスにある任意のスクリプト言語または式言語で記述できます。

例:

@ScriptAssert(lang = "javascript", script = "_this.value1 != null || _this != value2)")
public class MyBean {
  private String value1;
  private String value2;
}
于 2010-12-06T14:43:27.933 に答える