Hibernate Validator5.4.2--分组约束

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 inspection
car.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 yet
Driver 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 test
john.passedDrivingTest( true );
assertEquals( 0, validator.validate( car, DriverChecks.class ).size() );

// just checking that everything is in order now
assertEquals( 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 Car
SuperCar 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 car
Set<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 validated
Car 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 test
Driver john = new Driver( "John Doe" );
john.setAge( 18 );

// now let's add a driver to the car
car.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异常。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值