kagamihogeの日記

kagamihogeの日記です。

The Java EE 7 TutorialのIntroduction to Bean Validationの章をテキトーに訳した

The Java EE 7 Tutorial21 Introduction to Bean Validationの章をテキトーに訳した。

21 Introduction to Bean Validation

データの整合性を維持するためにユーザの入力値を検証することはアプリケーションロジックの重要な部分です。データの検証は、Developing a Simple Facelets Application: The guessnumber-jsf Example Applicationなどの、シンプルなアプリケーションでさえ異なるレイヤーで現れます。guessnumber-jsfサンプルアプリケーションは、プレゼンテーションレイヤーでユーザ入力(h:inputTextタグ)の数値データ検証を行い、ビジネスレイヤーで数値の範囲検証を行います。

Java API for JavaBean Validation ("Bean Validation")は、オブジェクト・オブジェクトのメンバ・メソッドコンストラクタの検証機能を提供します。Java EE環境では、Bean ValidationはJava EEコンテナとサービスに統合されており、開発者は容易に検証制約の定義と実行が行えます。Bean ValidationはJava EE 7プラットフォームの一部として利用可能です。

この章では以下のトピックを扱います。

  • Using Bean Validation Constraints
  • Validating Null and Empty Strings
  • Validating Constructors and Methods
  • Further Information about Bean Validation

21.1 Using Bean Validation Constraints

Bean Validationモデルはアノテーション形式の制約によって実現されており、アノテーションはマネージドビーンなどのJavaBeansコンポーネントのクラス・メソッド・フィールドに付与します。

制約はビルトインもユーザ定義のどちらも使用可能です。ユーザ定義制約はカスタム制約と呼ばれます。たいてのビルトイン制約はjavax.validation.constraintsパッケージにあります。表 21-1にすべてのビルトイン制約を示します。カスタム制約の生成についてはCreating Custom Constraintsを参照してください。

表 21-1 ビルトインBean Validation制約の一覧

制約 説明 使用例
@AssertFalse フィールドやプロパティの値がfalseである。 @AssertFalse
boolean isUnsupported;
@AssertTrue フィールドやプロパティの値がtrueである。 @AssertTrue
boolean isActive;
@DecimalMax フィールドやプロパティの値がアノテーションの要素値以下の10進数である。 @DecimalMax("30.00")
BigDecimal discount;
@DecimalMin フィールドやプロパティの値がアノテーションの要素値以上の10進数である。 @DecimalMin("5.00")
BigDecimal discount;
@Digits フィールドやプロパティの値が指定範囲内の数値である。integer要素には最大の整数桁を指定し、fractionには最大の少数桁を指定します。 @Digits(integer=6, fraction=2)
BigDecimal price;
@Future フィールドやプロパティの値が未来の日時である。 @Future
Date eventDate;
@Max フィールドやプロパティの値がアノテーションの要素値以下の整数である。 @Max(10)
int quantity;
@Min フィールドやプロパティの値がアノテーションの要素値以上の整数である。 @Min(5)
int quantity;
@NotNull フィールドやプロパティの値がnullではない。 @NotNull
String username;
@Null フィールドやプロパティの値がnullである。 @Null
String unusedString;
@Past フィールドやプロパティの値が過去である。 @Past
Date birthday;
@Pattern フィールドやプロパティの値がregexp要素の正規表現とマッチする。 @Pattern(regexp="\(\d{3}\)\d{3}-\d{4}")
String phoneNumber;
@Size 評価されたフィールドやプロパティのサイズが指定範囲にマッチする。もしフィールドやプロパティがStringの場合、文字列のサイズが評価されます。もしフィールドやプロパティがCollectionの場合、Collectionのサイズが評価されます。もしフィールドやプロパティがMapの場合、Mapのサイズが評価されます。もしフィールドやプロパティが配列の場合、配列のサイズが評価されます。オプションのmaxmin要素で範囲を指定します。 @Size(min=2, max=240)
String briefMessage;

以下のサンプルコードでは、ビルトインの@NotNull制約をフィールドに付与しています。

public class Name {
    @NotNull 
    private String firstname;

    @NotNull 
    private String lastname;
    ...
}

一つのJavaBeansコンポーネントオブジェクトに一つ以上の制約を付与することも可能です。たとえば、firstnamelastnameフィールドにサイズの制約を付与できます。

public class Name {
    @NotNull
    @Size(min=1, max=16)
    private String firstname;

    @NotNull 
    @Size(min=1, max=16)
    private String lastname;
    ...
}

以下のサンプルは、企業の電子メールアカウントに対し、事前定義済みの電子メールパターンによるチェック制約をメソッドに付与しています。

@ValidEmail 
public String getEmailAddress() {
    return emailAddress;
}

ビルトイン制約にはデフォルト実装が利用可能です。ユーザ定義やカスタム制約は検証ロジックの実装が必要です。前述の例では、@ValidEmailカスタム制約が実装クラスを必要とします。

検証失敗時にはh:messagesタグで表示可能です。

Bean Validationを含むマネージドビーンは自動的にJSFアプリケーションのwebページのフィールドに配置される検証制約を取得します。

検証制約の詳細については下記を参照してください。

21.2 Validating Null and Empty Strings

プログラミング言語Javaはnullと空文字と区別します。空文字は長さゼロの文字列インスタンスで、null文字列は値を持ちません。

空文字は""として表現されます。ゼロ文字の文字シーケンスです。null文字列はnullとして表現されます。文字列インスタンスが無いことを意味します。

inputTextなどのJSFテキストコンポーネントとして表現されるマネージドビーンの要素の初期値はJSF実装が空文字を割り当てます。そうしたフィールドにユーザ入力が不要である場合、文字列フィールドの検証には問題が生じます。以下のサンプルを考えてみると、testStringにユーザが入力した値が設定されて検証されるとします。このケースの場合、フィールドにユーザ入力は不要です。

if (testString==null) {
    doSomething();
} else {
    doAnotherThing();
}

デフォルトでは、ユーザが何も入力しなかったとしてもdoAnotherThingが呼ばれます。その理由は、testString要素は空文字で初期化されるためです。

意図通りにBean Validationを動作させるには、デプロイメント記述子web.xmlでコンテキストパラメータjavax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULLtrueに設定します。

<context-param>
    <param-name>
        javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL
    </param-name>
    <param-value>true</param-value>
</context-param>

このパラメータはJSF実装が空文字をnullとして扱えるようにします。

一方、入力必須のために要素に@NotNulを付与することを考えます。このケースの場合、空文字は検証制約を通過します。ただし、コンテキストパラメータjavax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULLtrueにする場合、マネージドビーンの値にはnullがBean Validationランタイムに渡され、@NotNull制約は失敗します。

21.3 Validating Constructors and Methods

Bean Validation制約は非staticなメソッドコンストラクタの引数や、非staticメソッドの戻り値に付与可能です。staticメソッドコンストラクタは検証されません。

public class Employee {
...
  public Employee (@NotNull String name) { ... }

  public void setSalary(
      @NotNull 
      @Digits(integer=6, fraction=2) BigDecimal salary,
      @NotNull
      @ValidCurrency
      String currencyType) {
    ...
  }
...
}

この例では、Employeeクラスは氏名を必須とするコンストラクタ制約と、メソッド引数に二組の制約を持ちます。employeeのsalaryの制約は、nullではなく、整数6桁小数点以下2桁以内になります。currencyTypeの制約は、nullでなく、カスタム制約を使用しています。

オブジェクト階層のクラスにメソッド制約を付与する場合、サブタイプによる意図しない振る舞いを避けるために特別な配慮が必要です。 詳細についてはUsing Method Constraints in Type Hierarchiesを参照してください。

21.3.1 Cross-Parameter Constraints

クロスパラメータ制約(cross-parameter constraints)複数のパラメータに適用される制約で、メソッドコンストラクタレベルで適用可能です。

@ConsistentPhoneParameters
@NotNull
public Employee (String name, String officePhone, String mobilePhone) {
  ...
}

この例では、カスタムのクロスパラメータ制約@ConsistentPhoneParametersコンストラクタの電話番号の形式が正しいかを検証します。@NotNull制約はコンストラクタのすべての引数に適用されます。

Tip: クロスパラメータ制約はメソッドコンストラクタに対して適用します。また、戻り値制約もメソッドコンストラクタに対して適用します。制約を引数や戻り値に適用する場所に関する混乱を避けるために、カスタム制約が適用される場所を一意に識別できるアノテーション名にしてください。たとえば、先述のカスタム制約@ConsistentPhoneParametersメソッドコンストラクタの引数に適用されることを示しています。

メソッドの引数と戻り値両方に適用されるカスタム制約を作成する場合、検証制約のターゲットを明示的に設定するために、制約アノテーションvalidationAppliesTo要素にConstraintTarget.RETURN_VALUEConstraintTarget.PARAMETERSを設定します。

21.3.2 Identifying Parameter Constraint Violations

メソッド呼び出し時にConstraintViolationExceptionが発生する場合、Bean Validationランタイム制約違反のあった引数インデックスを返します。引数インデックスの形式はargPARAMETER_INDEXで、PARAMETER_INDEXメソッドコンストラクタの最初の引数がゼロから始まる整数です。

21.3.3 Adding Constraints to Method Return Values

メソッドの戻り値を検証するには、メソッドコンストラクタ宣言に制約を付与します。

@NotNull
public Employee getEmployee() { ... }

クロスパラメータ制約はメソッドレベルにも適用可能です。戻り値とメソッド引数両方に適用が可能なカスタム制約のターゲットは曖昧になります。曖昧さを避けるには、制約アノテーション定義のターゲットを明示的に設定するために、validationAppliesTo要素にConstraintTarget.RETURN_VALUEConstraintTarget.PARAMETERSのどちらかをデフォルト設定します。

@Manager(validationAppliesTo=ConstraintTarget.RETURN_VALUE)
public Employee getManager(Employee employee) { ... }

詳細についてはRemoving Ambiguity in Constraint Targetsを参照してください。

21.4 Further Information about Bean Validation

Bean Validationのより詳しい情報については下記を参照してください。

関連リンク