Hibernate Validator5.4.2--分组约束

来源:互联网 发布:mac运行windows 发热 编辑:程序博客网 时间:2024/05/22 00:41

5.分组约束

5.1.请求组

分组可以在验证期间限制应用一组约束。目标组作为var-arg参数传递给适当的validate方法。

看一个例子,Person类的name属性有一个@NotNull约束,由于没有为此约束指定组,因此默认为javax.validation.groups.Default组 。

package org.hibernate.validator.referenceguide.chapter05;public class Person {    @NotNull    private String name;    public Person(String name) {        this.name = name;    }    // getters and setters ...}

Driver类继承Person类并增加了age和hasDrivingLicense属性。司机必须年满18岁(@Min(18))并有驾驶执照(@AssertTrue),这两个约束属于组DriverChecks,它只是一个简单的标记接口。

使用接口使组的类型安全,并允许轻松重构。这也意味着组可以通过类继承相互继承。请参阅5.2.组继承章节。

package org.hibernate.validator.referenceguide.chapter05;public class Driver extends Person {    @Min(            value = 18,            message = "You have to be 18 to drive a car",            groups = DriverChecks.class    )    public int age;    @AssertTrue(            message = "You first have to pass the driving test",            groups = DriverChecks.class    )    public boolean hasDrivingLicense;    public Driver(String name) {        super( name );    }    public void passedDrivingTest(boolean b) {        hasDrivingLicense = b;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }}
package org.hibernate.validator.referenceguide.chapter05;public interface DriverChecks {}

Car类有一些没有指定组的约束,这些是属于默认组的,还有passedVehicleInspection属性上的@AssertTrue约束是属于CarChecks组的,它指示一辆车是否通过了测试。

package org.hibernate.validator.referenceguide.chapter05;public class Car {    @NotNull    private String manufacturer;    @NotNull    @Size(min = 2, max = 14)    private String licensePlate;    @Min(2)    private int seatCount;    @AssertTrue(            message = "The car has to pass the vehicle inspection first",            groups = CarChecks.class    )    private boolean passedVehicleInspection;    @Valid    private Driver driver;    public Car(String manufacturer, String licencePlate, int seatCount) {        this.manufacturer = manufacturer;        this.licensePlate = licencePlate;        this.seatCount = seatCount;    }    public boolean isPassedVehicleInspection() {        return passedVehicleInspection;    }    public void setPassedVehicleInspection(boolean passedVehicleInspection) {        this.passedVehicleInspection = passedVehicleInspection;    }    public Driver getDriver() {        return driver;    }    public void setDriver(Driver driver) {        this.driver = driver;    }    // getters and setters ...}
package org.hibernate.validator.referenceguide.chapter05;public interface CarChecks {}

在这个例子中总共使用了三个不同的组:

  • Person.name,Car.manufacturer,Car.licensePlate和Car.seatCount的约束都属于Default组。
  • Driver.age和Driver.hasDrivingLicense约束属于DriverChecks组。
  • Car.passedVehicleInspection约束属于CarChecks组。

下面的例子展示如何传递给Validator#validate()方法不同的组组合,以得到不同的验证结果。

// create a car and check that everything is ok with it.Car car = new Car( "Morris", "DD-AB-123", 2 );Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car );assertEquals( 0, constraintViolations.size() );// but has it passed the vehicle inspection?constraintViolations = validator.validate( car, CarChecks.class );assertEquals( 1, constraintViolations.size() );assertEquals(        "The car has to pass the vehicle inspection first",        constraintViolations.iterator().next().getMessage());// let's go to the vehicle inspectioncar.setPassedVehicleInspection( true );assertEquals( 0, validator.validate( car, CarChecks.class ).size() );// now let's add a driver. He is 18, but has not passed the driving test yetDriver john = new Driver( "John Doe" );john.setAge( 18 );car.setDriver( john );constraintViolations = validator.validate( car, DriverChecks.class );assertEquals( 1, constraintViolations.size() );assertEquals(        "You first have to pass the driving test",        constraintViolations.iterator().next().getMessage());// ok, John passes the testjohn.passedDrivingTest( true );assertEquals( 0, validator.validate( car, DriverChecks.class ).size() );// just checking that everything is in order nowassertEquals( 0,        validator.validate(                car,                Default.class,                CarChecks.class,                DriverChecks.class        ).size());

该例子中的第一个validate()调用没有指定组,即使passedVehicleInspection的值是false,也没有出现验证错误,因为此约束不属于默认组。

下一个validate()调用指定组CarChecks,第一次验证错误,然后把passedVehicleInspection值设置成true,通过了验证。向car实例添加了一个Driver,该Driver实例的passedVehicleInspection为false,未能通过DriverChecks组的验证,只有在设置passedDrivingTest为true之后,才能通过DriverChecks组的验证。

最后一次validate()调用显示所有的约束都通过了所有组的验证。

5.2.组继承

使用验证组,我们需要为每个验证组调用validate()方法或者指定全部验证组。

在一些情况下,可能需要定义一个包含其他组的验证组,可以使用组继承。

下面的例子,在类SuperCar中,定义了一个RaceCarChecks组,它继承了Default组。它新增了一个safetyBelt(安全带)属性,表示比赛用车必须具有安全带。

package org.hibernate.validator.referenceguide.chapter05.groupinheritance;public class SuperCar extends Car {    @AssertTrue(            message = "Race car must have a safety belt",            groups = RaceCarChecks.class    )    private boolean safetyBelt;    // getters and setters ...}
package org.hibernate.validator.referenceguide.chapter05.groupinheritance;import javax.validation.groups.Default;public interface RaceCarChecks extends Default {}

在下面的例子中,将分别验证普通汽车跟比赛用车的约束。

// create a supercar and check that it's valid as a generic CarSuperCar superCar = new SuperCar( "Morris", "DD-AB-123", 1  );assertEquals( "must be greater than or equal to 2", validator.validate( superCar ).iterator().next().getMessage() );// check that this supercar is valid as generic car and also as race carSet<ConstraintViolation<SuperCar>> constraintViolations = validator.validate( superCar, RaceCarChecks.class );assertThat( constraintViolations ).extracting( "message" ).containsOnly(        "Race car must have a safety belt",        "must be greater than or equal to 2");

在第一次调用validate()方法,没有指定特定的组,使用默认组。这里有一个验证错误,因为汽车必须至少有一个座位,该约束属于默认组。

在第二次调用validate()方法,指定了RaceCarChecks组,有两个验证错误,一个是关于默认组的座位约束,一个是RaceCarChecks组的安全带约束。

5.3.定义组序列

默认的,不管约束属于哪些组,验证约束是没有特定的顺序的。但是,我们可以控制约束被验证的顺序。

下面的例子,要求在检查汽车的其它状况之前首先要通过所有默认组的约束。最后,在开车之前,应该检查驾驶员约束。

要实现这样的验证顺序,只需要通过定义接口,并使用@GroupSequence注解,以定义组验证顺序。如果在这写组中有一个约束没有通过验证,那么其它组的约束都不会被验证。

定义验证组顺序:

package org.hibernate.validator.referenceguide.chapter05;import javax.validation.GroupSequence;import javax.validation.groups.Default;@GroupSequence({ Default.class, CarChecks.class, DriverChecks.class })public interface OrderedChecks {}

使用定义的组:

Car car = new Car( "Morris", "DD-AB-123", 2 );car.setPassedVehicleInspection( true );Driver john = new Driver( "John Doe" );john.setAge( 18 );john.passedDrivingTest( true );car.setDriver( john );assertEquals( 0, validator.validate( car, OrderedChecks.class ).size() );

5.4.重新定义默认的组序列

5.4.1. @GroupSequence

除了定义组顺序之外,@GroupSequence注解还允许重新定义指定类的默认组。只需在类上添加@GroupSequence注解,并在注解中指定特定的组顺序来替换Default 组。

下面的例子,RentalCar类重新定义了默认组。

package org.hibernate.validator.referenceguide.chapter05;@GroupSequence({ RentalChecks.class, CarChecks.class, RentalCar.class })public class RentalCar extends Car {    @AssertFalse(message = "The car is currently rented out", groups = RentalChecks.class)    private boolean rented;    public RentalCar(String manufacturer, String licencePlate, int seatCount) {        super( manufacturer, licencePlate, seatCount );    }    public boolean isRented() {        return rented;    }    public void setRented(boolean rented) {        this.rented = rented;    }}
package org.hibernate.validator.referenceguide.chapter05;public interface RentalChecks {}

通过这个定义,可以像验证默认组那样验证RentalChecks,CarChecks和RentalCar,
如下面的例子:

RentalCar rentalCar = new RentalCar( "Morris", "DD-AB-123", 2 );rentalCar.setPassedVehicleInspection( true );rentalCar.setRented( true );Set<ConstraintViolation<RentalCar>> constraintViolations = validator.validate( rentalCar );assertEquals( 1, constraintViolations.size() );assertEquals(        "Wrong message",        "The car is currently rented out",        constraintViolations.iterator().next().getMessage());rentalCar.setRented( false );constraintViolations = validator.validate( rentalCar );assertEquals( 0, constraintViolations.size() );

5.4.2.@GroupSequenceProvider

除了通过@GroupSequence静态地重定义默认组外,Hibernate Validator还提供了一个SPI,用于根据对象状态动态地重定义默认组序列。

为此,需要实现接口DefaultGroupSequenceProvider并通过@GroupSequenceProvider注解将这个实现注册到目标类。下面的例子就动态地添加CarChecks组。

package org.hibernate.validator.referenceguide.chapter05.groupsequenceprovider;public class RentalCarGroupSequenceProvider        implements DefaultGroupSequenceProvider<RentalCar> {    @Override    public List<Class<?>> getValidationGroups(RentalCar car) {        List<Class<?>> defaultGroupSequence = new ArrayList<Class<?>>();        defaultGroupSequence.add( RentalCar.class );        if ( car != null && !car.isRented() ) {            defaultGroupSequence.add( CarChecks.class );        }        return defaultGroupSequence;    }}
package org.hibernate.validator.referenceguide.chapter05.groupsequenceprovider;@GroupSequenceProvider(RentalCarGroupSequenceProvider.class)public class RentalCar extends Car {    @AssertFalse(message = "The car is currently rented out", groups = RentalChecks.class)    private boolean rented;    public RentalCar(String manufacturer, String licencePlate, int seatCount) {        super( manufacturer, licencePlate, seatCount );    }    public boolean isRented() {        return rented;    }    public void setRented(boolean rented) {        this.rented = rented;    }}

5.5.转换组

如果想验证car的相关检查和driver的检查,可以将所需的组显式传递给validate调用,但是如果想使这些验证作为Default组验证的一部分进行,可以使用@ConvertGroup,它允许在级联验证时去使用跟原来不同的组。

下面的例子中,@GroupSequence({ CarChecks.class, Car.class })重新定义Default组;@ConvertGroup(from = Default.class, to = DriverChecks.class)确保driver在级联验证期间将该Default组转换为DriverChecks组。

package org.hibernate.validator.referenceguide.chapter05.groupconversion;public class Driver {    @NotNull    private String name;    @Min(            value = 18,            message = "You have to be 18 to drive a car",            groups = DriverChecks.class    )    public int age;    @AssertTrue(            message = "You first have to pass the driving test",            groups = DriverChecks.class    )    public boolean hasDrivingLicense;    public Driver(String name) {        this.name = name;    }    public void passedDrivingTest(boolean b) {        hasDrivingLicense = b;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    // getters and setters ...}
package org.hibernate.validator.referenceguide.chapter05.groupconversion;@GroupSequence({ CarChecks.class, Car.class })public class Car {    @NotNull    private String manufacturer;    @NotNull    @Size(min = 2, max = 14)    private String licensePlate;    @Min(2)    private int seatCount;    @AssertTrue(            message = "The car has to pass the vehicle inspection first",            groups = CarChecks.class    )    private boolean passedVehicleInspection;    @Valid    @ConvertGroup(from = Default.class, to = DriverChecks.class)    private Driver driver;    public Car(String manufacturer, String licencePlate, int seatCount) {        this.manufacturer = manufacturer;        this.licensePlate = licencePlate;        this.seatCount = seatCount;    }    public boolean isPassedVehicleInspection() {        return passedVehicleInspection;    }    public void setPassedVehicleInspection(boolean passedVehicleInspection) {        this.passedVehicleInspection = passedVehicleInspection;    }    public Driver getDriver() {        return driver;    }    public void setDriver(Driver driver) {        this.driver = driver;    }    // getters and setters ...}

看下面的验证程序:

// create a car and validate. The Driver is still null and does not get validatedCar car = new Car( "VW", "USD-123", 4 );car.setPassedVehicleInspection( true );Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car );assertEquals( 0, constraintViolations.size() );// create a driver who has not passed the driving testDriver john = new Driver( "John Doe" );john.setAge( 18 );// now let's add a driver to the carcar.setDriver( john );constraintViolations = validator.validate( car );assertEquals( 1, constraintViolations.size() );assertEquals(        "The driver constraint should also be validated as part of the default group",        constraintViolations.iterator().next().getMessage(),        "You first have to pass the driving test");

可以在使用@Valid注解时使用转换组,也就是说可以使用在方法、构造器的参数和返回值。
多个转换组使用@ConvertGroup.List定义。

不过也有一下限制:

  • @ConvertGroup只能结合@Valid使用。如果没有,则抛出 ConstraintDeclarationException异常。
  • 具有相同的值的同一元素上有多个转换规则是不合法的。在这种情况下,会抛出ConstraintDeclarationException异常。
  • from属性不能引用组序列。在这种情况下会抛出ConstraintDeclarationException异常。

转换规则不会被递归执行。这第一个匹配的规则将会被使用,后续的规则将会被忽略。

原创粉丝点击