首先,解释一下什么是分组校验?所谓分组,就是允许我们在校验过程中指定使用哪些约束。比如说:一个被校验的bean有10个属性。在某次校验中,我们只想校验前5个属性,后5个属性不想校验(也就是说,定义在前5个属性上的约束生效,而定义在后5个属性上的约束不生效);在另一次校验中,我们只想校验后5个属性,而不想校验前5个属性。要实现这个目标,我们可以对属性进行分组,然后在实际校验的时候明确指定我们要校验哪个或者哪些分组。下面通过实例来解释这个话题:
- Person类
public class Person {
/**
* 没有明确指定分组的话,默认使用的是
*
* {@link javax.validation.groups.Default}
*
* 分组,而不是空
*
* 也就是说,当分组为 Default 的时候,name属性不能为空
*/
@NotNull
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- Driver
public class Driver extends Person {
@Min(value = 18, message = "You have to be 18 to drive a car", groups = DriverChecks.class)
private int age;
@AssertTrue(message = "You first have to pass the driving test", groups = DriverChecks.class)
private boolean hasDrivingLicense;
public Driver(String name) {
super(name);
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isHasDrivingLicense() {
return hasDrivingLicense;
}
public void setHasDrivingLicense(boolean hasDrivingLicense) {
this.hasDrivingLicense = hasDrivingLicense;
}
}
需要注意的是,在Driver的定义中,我们为age和hasDrivingLicense属性的校验明确指定了分组--DriverCheck,其定义如下:
public interface DriverChecks {
}
可以看出,所谓分组,其实只是一个简单的标记接口。
- Car
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 licensePlate, int seatCount) {
this.manufacturer = manufacturer;
this.licensePlate = licensePlate;
this.seatCount = seatCount;
}
public String getManufacturer() {
return manufacturer;
}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
public String getLicensePlate() {
return licensePlate;
}
public void setLicensePlate(String licensePlate) {
this.licensePlate = licensePlate;
}
public int getSeatCount() {
return seatCount;
}
public void setSeatCount(int seatCount) {
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;
}
}
和Driver类似,我们为Car的passedVehicleInspection属性指定了分组--CarChecks,其定义如下:
public interface CarChecks {
}
至此,我们需要总结一下:
- 定义在Person的name属性、Car的manufacturer属性、Car的licensePlate属性和Car的seatCount属性上面的约束都属于 Default 分组(因为没有明确指定分组)
- 定义在Driver的age和hasDrivingLicense属性上面的约束都属于 DriverChecks 分组
- 定义在Car的passedVehicleInspection属性上面的约束属于CarChecks分组
最后,我们通过测试看一下分组校验的效果:
@Test
public void groupValidateFirstTest() {
Car car = new Car("Morris", "DD-AB-123", 2);
// 校验 car 对象的属于 Default 分组的属性
Set<ConstraintViolation<Car>> constraintViolations = validator.validate(car);
assertEquals(0, constraintViolations.size());
// 校验 car 对象的属于 CarChecks 分组的属性
constraintViolations = validator.validate(car, CarChecks.class);
assertEquals(1, constraintViolations.size());
assertEquals("The car has to pass the vehicle inspection first",
constraintViolations.iterator().next().getMessage());
// 校验 car 对象的属于 CarChecks 分组的属性
car.setPassedVehicleInspection(true);
assertEquals(0, validator.validate(car, CarChecks.class).size());
// 默认情况下,jhon没有通过驾驶测试,因为布尔值的默认值是false
Driver john = new Driver("John Doe");
john.setAge(18);
car.setDriver(john);
// 校验 driver 对象的属于 DriverChecks 分组的属性
constraintViolations = validator.validate(car, DriverChecks.class);
assertEquals(1, constraintViolations.size());
assertEquals("You first have to pass the driving test",
constraintViolations.iterator().next().getMessage());
// 让john通过驾驶测试
john.setHasDrivingLicense(true);
assertEquals(0, validator.validate(car, DriverChecks.class).size());
// 校验所有分组
assertEquals(0,
validator.validate(car, Default.class, CarChecks.class, DriverChecks.class).size());
}