Hibernate Validator(Bean Validaton)ともう少し戯れてみる

前回簡単にHibernateValidator(Bean Validation)について説明しましたが、今回はもう少し突っ込んだ内容を解説しようと思います。

HibernateValidatorのバリデータアノテーションJavaDocを見ていると各アノテーションの中に@Listというインナーアノテーションが定義されていて、それぞれのバリデータアノテーションの配列をもつことができるようになっています。追加バリデーションライブラリのm4hv-extensionsも同様にListアノテーションを持っています。このListアノテーションハ同じバリデーションで複数の条件を指定するようなときに利用することができます。とりあえず、まずは以下の例を見てください。

import java.util.Iterator;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import org.hibernate.validator.constraints.Length;

public class Main3 {

    public static void main(String[] args) {
        ValidatedBean bean = getBean();

        bean.setField1("12345abcde");
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();
        Set<ConstraintViolation<ValidatedBean>> constraintViolations = validator.validate(bean, SmallGroup.class);

        if (constraintViolations.size() > 0) {
            Iterator<ConstraintViolation<ValidatedBean>> iterator = constraintViolations.iterator();
            while (iterator.hasNext()) {
                System.out.println(iterator.next().getMessage());
            }
        }
    }

    private static ValidatedBean getBean() {
        ValidatedBean bean = new ValidatedBean();
        return bean;
    }

    static class ValidatedBean {

        @Length.List({
                @Length(min = 5, max = 10),
                @Length(min = 15, max = 20)
        })
        private String field1;

        public ValidatedBean(){}

        public String getField1() {
            return field1;
        }

        public void setField1(String field1) {
            this.field1 = field1;
        }
    }
}

このプログラムを実行すると「長さは15以上20以下でなければなりません。」とコンソールに表示されます。(日本語メッセージの出力方法については前回までの記事で紹介していますのでそちらを参照願います。ここでは設定していることを前提とします。)ValidatedBeanのfield1に設定した値は長さ10文字の文字列なので最初の@Lengthは検査OKですが、次の@LengthでNGとなりエラーメッセージが表示されます。

では、この@Listはどういうケースで使うのかというと、これまた各アノテーションにgroupsという項目があり、クラス型の配列を設定することが可能となっています。ここまで来るとオマジナイ(?)のように感じますが、HibernateValidatorではバリデーションのグループをインターフェースで定義することができます。@Listアノテーションはこのグループと合わせて利用すると効果を発揮します。

それでは次のようなグループインターフェースを定義してみます。

interface SmallGroup{}
interface LargeGroup{}

加えてValidatedBeanクラスを次のように変更してください。

static class ValidatedBean {

    @Length.List({
           @Length(min = 5, max = 10, groups = SmallGroup.class),
           @Length(min = 15, max = 20, groups = LargeGroup.class)
    })
    private String field1;

    public ValidatedBean(){}

    public String getField1() {
        return field1;
    }

    public void setField1(String field1) {
        this.field1 = field1;
    }
}

変更点は@Length.Listアノテーション内の@Lengthアノテーションのgroupsに先ほど定義したグループインターフェースを設定している点です。

最後にメインメソッド内のHibernateValidatorのValidator#validateを次のように変更してプログラムを実行してみてください。

Set<ConstraintViolation<ValidatedBean>> constraintViolations = validator.validate(bean, SmallGroup.class);

実行結果はコンソールに何も表示されずにプログラムが終了すると思います。今回の実行例ではValiator#validateメソッドの第2引数にグループインターフェースを渡すことでHibernateValidatorは@Length.Listアノテーションに設定されている@Length(min = 5, max = 10, groups = SmallGroup.class)アノテーションの内容でプロパティの検査を行います。従ってこの場合設定されている文字列が10文字であるため検査OKとなります。

Validator#validateの第2引数にLargeGroupインターフェースを設定すると今度はバリデーションエラーとなりエラーメッセージが表示されます。

HibernateValidatorのバリデータアノテーションはすべてgroupsを設定することが可能です。このgroupsは配列で設定することが可能なので複数のグループを定義することができます。

このあたりをうまく組み合わせば結構柔軟なバリデーション機能を実現することができるかと思います。