はじめてのBean Validation その4
- http://d.hatena.ne.jp/shin/20100112/p5
- http://d.hatena.ne.jp/shin/20100113/p1
- http://d.hatena.ne.jp/shin/20100114/p5
の続き。
今回はバリデーショングループについて。
たとえばバリデーションをするときにこの新規作成時のタイミングではここはチェックを行わないけど、次にUpdateで呼ばれたときはここはチェックをするといったことがよくある。ここもBean Validationは定義してある。
それがバリデーショングループだ。
使い方
まず適当にインターフェースを作る。
public interface FooGroup { }
それをbeanに指定する。
@DecimalMax(value="29",groups={FooGroup.class},message="20代までOK") String data;
呼び出すとき。
ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
Validator v = vf.getValidator();
Set<ConstraintViolation<InputBean>> result = v.validate(ib ,FooGroup.class );
validateメソッドで先ほど作ったインターフェースを引数に追加してある。ここは「Class ...」 で定義してあるので複数のグループでバリデーションをするといった使い方も可能だ。
ちなみに何もグループ指定しない場合は「javax.validation.groups.Default.class」を指定したのと同じになる。
複数の指定
同じバリデーションロジックで複数のパターンを検証したい場合はどうするのか。Javaのアノテーションは1種類しかつけることができないのだ。
そういうときには以下のようにする。
@DecimalMax.List({ @DecimalMax(value="39",groups={FooGroup.class},message="おいらは30代までOK"), @DecimalMax(value="19",groups={BarGroup.class},message="きしださんは10代までOK") }) String data;
したがってちゃんとしたカスタムバリデータを作成するときにはアノテーションでListを用意してn件対応にしなくてはならないということである。
以下サンプル。
@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @Size(min=1,message="必須") @NotNull(message="必須") @Constraint(validatedBy={}) public @interface Required { String message() default "必須入力ですぜ旦那"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; @Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @interface List { Required[] value(); } }
あ、アノテーションの中にアノテーション書くことできるんだ…。この場合パラメータなしの必須入力なのでList対応するのは意味ないんだけどね。
複数のグループをひとつにまとめるといった使い方もできるが、本質的ではないので省略で。
ちなみにJSF 2.0ではBean Validationに対応している。またJPA 2.0もBeanValidationをサポートしていて、アプリケーションのミスにより変な値がデータベースに入ってしまうというのをある程度防ぐことが可能になっている。
データに注目してプログラミングする場合バリデーションドリブンプログラミング(今思いついた造語)も面白いかもしれない。契約プログラミングの延長とでもいうべきか。問題があるのがどこなのか問題の切り分けが出来るとか。できないとか。