JSF2.xでValidationグループを設定する

BeanValidation1.0では、@NotNullなどの各検証アノテーションにgroup属性を設定することができます。これは、同じドメインオブジェクトに対して、検証のルールのパターンが複数ある場合に有効です。

例えば、以下のような画面を想定してみます。

f:id:n_agetsuma:20130123205334p:plain

本の登録では、ISBNコードとタイトルの両方の入力を必須とします。検索の場合は、どちらか一方が指定されていれば良いこととします。この入力値がバインドされるドメインオブジェクトは両方とも本を示すBookクラスです。

/** 検索の場合にも両方とも必須入力となるケース */
public class Book {

    @NotNull
    private String isbn;

    @NotNull
    private String title;

   // getterとsetterは省略
}

上記のように、何もグループを指定せずに@NotNullをフィールドに付与すると、登録処理時にも検索処理時にも入力検証が動作してしまい、検証ルールを分けることができません。このような場合はValidationグループを使うと便利です。

/** 登録時のみ@NotNullを検証する */
public class Book {

    @NotNull(groups=BookEntryGroup.class)
    private String isbn;

    @NotNull(groups=BookEntryGroup.class)
    private String title;

   // getterとsetterは省略
}

/** グループを示すクラスは空のインタフェース */
public interface BookEntryGroup {}

設定したグループで検証を指示するには、JSFタグの<f:validateBean>を使います*1
validationGroup属性には、検証対象のグループを示すインタフェースを指定します。検証グループを明示的に指定することで、@NotNullにgroups属性と一致する検証のみが実行されます。groups属性が何も設定されていない場合は暗黙的にjavax.validation.groups.Defaultがグループとして設定されています。
このため、登録のXHTML部分には<f:validateBean>を設定し、検索の部分には設定しないことで検証グループを分けることができます。

<!-- ISBNコードの入力(登録時) -->
<h:inputText id="isbn" value="#{bookController.book.isbn}" label="ISBNコード">
    <f:validateBean validationGroups="validation.groups.BookEntryGroup" />
</h:inputText>

<!-- タイトルの入力(登録時) -->
<h:inputText id="title" value="#{bookController.book.title}" label="タイトル">
    <f:validateBean validationGroups="validation.groups.BookEntryGroup" />
</h:inputText>
余談: SpringMVCの場合だともっと便利

JSF2.0の場合、XHTML側でグループ指定を行いますが、SpringMVCではコントローラクラスにアノテーションで実装できます。入力検証の内容がXHTMLとサーバ側のバッキングBeanに散らばると、正直わかりにくいと思う。JSFでもアノテーションでできるようになると嬉しいです。

@RequestMapping("/entry")
public String entryBook( @Validated(BookEntry.class) Book book) { ... }

*1:参考 : Java Server Faces Specification Version2.1 10.4.1.4 <f:validateBean>