1. Introduction
1.绪论
In our Java Bean Validation Basics tutorial, we saw the usage of various built-in javax.validation constraints. In this tutorial, we’ll see how to group javax.validation constraints.
在我们的Java Bean验证基础教程中,我们看到了各种内置javax.validation约束的用法。在本教程中,我们将看到如何对javax.validation约束进行分组。
2. Use Case
2.使用案例
There are many scenarios where we need to apply constraints on a certain set of fields of the bean, and then later we want to apply constraints on another set of fields of the same bean.
在很多情况下,我们需要在Bean的某一组字段上应用约束,随后我们又想在同一个Bean的另一组字段上应用约束。
For example, let us imagine that we have a two-step signup form. In the first step, we ask the user to provide basic information like the first name, last name, email id, phone number, and captcha. When the user submits this data, we want to validate this information only.
例如,让我们想象一下,我们有一个两步的注册表格。在第一步,我们要求用户提供基本信息,如名字、姓氏、电子邮件地址、电话号码和验证码。当用户提交这些数据时,我们只想验证这些信息。
In the next step, we ask the user to provide some other information like an address, and we want to validate this information as well — note that captcha is present in both steps.
在下一步,我们要求用户提供一些其他信息,如地址,我们也要验证这些信息–注意,这两个步骤中都有验证码。
3. Grouping Validation Constraints
3.分组验证约束
All javax validation constraints have an attribute named groups. When we add a constraint to an element, we can declare the name of the group to which the constraint belongs. This is done by specifying the class name of the group interface in the groups attributes of the constraint.
所有的javax验证约束都有一个名为groups的属性。 当我们为一个元素添加约束时,我们可以声明该约束所属的组的名称。这是通过在约束的groups属性中指定组接口的类名来实现的。
The best way to understand something is to get our hands dirty. Let’s see in action how we combine javax constraints into groups.
理解一件事的最好方法就是动手去做。让我们看看我们是如何将javax约束组合成组的。
3.1. Declaring Constraint Groups
3.1.声明约束组
The first step is to create some interfaces. These interfaces will be the constraint group names. In our use-case, we’re dividing validation constraints into two groups.
第一步是创建一些接口。这些接口将是约束组的名称。在我们的用例中,我们要把验证约束分成两组。
Let’s see the first constraint group, BasicInfo:
让我们看看第一个约束组,BasicInfo。
public interface BasicInfo {
}
The next constraint group is AdvanceInfo:
下一个约束组是AdvanceInfo。
public interface AdvanceInfo {
}
3.2. Using Constraint Groups
3.2.使用约束组
Now that we’ve declared our constraint groups, it’s time to use them in our RegistrationForm Java bean:
现在我们已经声明了我们的约束组,现在是时候在我们的RegistrationForm Java Bean中使用它们了。
public class RegistrationForm {
@NotBlank(groups = BasicInfo.class)
private String firstName;
@NotBlank(groups = BasicInfo.class)
private String lastName;
@Email(groups = BasicInfo.class)
private String email;
@NotBlank(groups = BasicInfo.class)
private String phone;
@NotBlank(groups = {BasicInfo.class, AdvanceInfo.class})
private String captcha;
@NotBlank(groups = AdvanceInfo.class)
private String street;
@NotBlank(groups = AdvanceInfo.class)
private String houseNumber;
@NotBlank(groups = AdvanceInfo.class)
private String zipCode;
@NotBlank(groups = AdvanceInfo.class)
private String city;
@NotBlank(groups = AdvanceInfo.class)
private String contry;
}
With the constraint groups attribute, we have divided the fields of our bean into two groups according to our use case. By default, all constraints are included in the Default constraint group.
通过约束groups属性,我们根据使用情况将Bean的字段分为两组。默认情况下,所有的约束都包含在默认约束组中。
3.3. Testing Constraints Having One Group
3.3.测试约束有一个组
Now that we’ve declared constraint groups and used them in our bean class, it’s time to see these constraint groups in action.
现在我们已经声明了约束组并在我们的bean类中使用了它们,现在是时候看看这些约束组的作用了。
First, we’ll see when basic information is not complete, using our BasicInfo constraint group for validation. We should get a constraint violation for any field left blank where we used BasicInfo.class in the groups attribute of the field’s @NotBlank constraint:
首先,我们将看到基本信息不完整时,使用我们的BasicInfo约束组进行验证。当我们在字段的@NotBlank约束的groups属性中使用了BasicInfo.class时,我们应该得到任何字段留空的约束违反。
public class RegistrationFormUnitTest {
private static Validator validator;
@BeforeClass
public static void setupValidatorInstance() {
validator = Validation.buildDefaultValidatorFactory().getValidator();
}
@Test
public void whenBasicInfoIsNotComplete_thenShouldGiveConstraintViolationsOnlyForBasicInfo() {
RegistrationForm form = buildRegistrationFormWithBasicInfo();
form.setFirstName("");
Set<ConstraintViolation<RegistrationForm>> violations = validator.validate(form, BasicInfo.class);
assertThat(violations.size()).isEqualTo(1);
violations.forEach(action -> {
assertThat(action.getMessage()).isEqualTo("must not be blank");
assertThat(action.getPropertyPath().toString()).isEqualTo("firstName");
});
}
private RegistrationForm buildRegistrationFormWithBasicInfo() {
RegistrationForm form = new RegistrationForm();
form.setFirstName("devender");
form.setLastName("kumar");
form.setEmail("anyemail@yopmail.com");
form.setPhone("12345");
form.setCaptcha("Y2HAhU5T");
return form;
}
//... additional tests
}
In the next scenario, we’ll check when the advanced information is incomplete, using our AdvanceInfo constraint group for validation:
在下一个场景中,我们将在高级信息不完整时进行检查,使用我们的AdvanceInfo约束组进行验证。
@Test
public void whenAdvanceInfoIsNotComplete_thenShouldGiveConstraintViolationsOnlyForAdvanceInfo() {
RegistrationForm form = buildRegistrationFormWithAdvanceInfo();
form.setZipCode("");
Set<ConstraintViolation<RegistrationForm>> violations = validator.validate(form, AdvanceInfo.class);
assertThat(violations.size()).isEqualTo(1);
violations.forEach(action -> {
assertThat(action.getMessage()).isEqualTo("must not be blank");
assertThat(action.getPropertyPath().toString()).isEqualTo("zipCode");
});
}
private RegistrationForm buildRegistrationFormWithAdvanceInfo() {
RegistrationForm form = new RegistrationForm();
return populateAdvanceInfo(form);
}
private RegistrationForm populateAdvanceInfo(RegistrationForm form) {
form.setCity("Berlin");
form.setContry("DE");
form.setStreet("alexa str.");
form.setZipCode("19923");
form.setHouseNumber("2a");
form.setCaptcha("Y2HAhU5T");
return form;
}
3.4. Testing Constraints Having Multiple Groups
3.4.测试具有多个组的约束条件
We can specify multiple groups for a constraint. In our use case, we’re using captcha in both basic and advanced info. Let’s first test the captcha with BasicInfo:
我们可以为一个约束条件指定多个组。在我们的用例中,我们在基本和高级信息中都使用captcha。让我们首先用BasicInfo来测试captcha。
@Test
public void whenCaptchaIsBlank_thenShouldGiveConstraintViolationsForBasicInfo() {
RegistrationForm form = buildRegistrationFormWithBasicInfo();
form.setCaptcha("");
Set<ConstraintViolation<RegistrationForm>> violations = validator.validate(form, BasicInfo.class);
assertThat(violations.size()).isEqualTo(1);
violations.forEach(action -> {
assertThat(action.getMessage()).isEqualTo("must not be blank");
assertThat(action.getPropertyPath().toString()).isEqualTo("captcha");
});
}
Now let’s test the captcha with AdvanceInfo:
现在让我们用AdvanceInfo来测试captcha。
@Test
public void whenCaptchaIsBlank_thenShouldGiveConstraintViolationsForAdvanceInfo() {
RegistrationForm form = buildRegistrationFormWithAdvanceInfo();
form.setCaptcha("");
Set<ConstraintViolation<RegistrationForm>> violations = validator.validate(form, AdvanceInfo.class);
assertThat(violations.size()).isEqualTo(1);
violations.forEach(action -> {
assertThat(action.getMessage()).isEqualTo("must not be blank");
assertThat(action.getPropertyPath().toString()).isEqualTo("captcha");
});
}
4. Specifying Constraint Group Validation Order with GroupSequence
4.用GroupSequence指定约束组验证顺序
By default, the constraint groups are not evaluated in any particular order. But we may have use cases where some groups should be validated before others. For achieving this, we can specify the order of group validation using GroupSequence.
默认情况下,约束组不会以任何特定的顺序被评估。但是我们可能会有一些用例,一些组应该在其他组之前被验证。为了实现这一点,我们可以使用GroupSequence来指定组的验证顺序。
There are two ways of using the GroupSequence annotation:
有两种使用GroupSequence注解的方法。
- on the entity being validated
- on an Interface
4.1. Using GroupSequence on the Entity Being Validated
4.1.在被验证的实体上使用GroupSequence
This is a simple way of ordering the constraints. Let’s annotate the entity with GroupSequence and specify the order of constraints:
这是一种简单的排列约束的方式。让我们用GroupSequence来注解实体,并指定约束的顺序。
@GroupSequence({BasicInfo.class, AdvanceInfo.class})
public class RegistrationForm {
@NotBlank(groups = BasicInfo.class)
private String firstName;
@NotBlank(groups = AdvanceInfo.class)
private String street;
}
4.2. Using GroupSequence on an Interface
4.2.在一个接口上使用GroupSequence
We can also specify the order of constraint validation using an interface. The advantage of this approach is that the same sequence can be used for other entities. Let’s see how we can use GroupSequence with the interfaces we defined above:
我们还可以使用接口来指定约束条件验证的顺序。这种方法的优点是,同样的序列可以用于其他实体。让我们看看我们如何使用GroupSequence与我们上面定义的接口。
@GroupSequence({BasicInfo.class, AdvanceInfo.class})
public interface CompleteInfo {
}
4.3. Testing GroupSequence
4.3.测试GroupSequence
Now let’s test GroupSequence. First, we will test that if BasicInfo is incomplete, then the AdvanceInfo group constraint will not be evaluated:
现在我们来测试一下GroupSequence。首先,我们将测试如果BasicInfo不完整,那么AdvanceInfo组约束将不会被评估。
@Test
public void whenBasicInfoIsNotComplete_thenShouldGiveConstraintViolationsForBasicInfoOnly() {
RegistrationForm form = buildRegistrationFormWithBasicInfo();
form.setFirstName("");
Set<ConstraintViolation<RegistrationForm>> violations = validator.validate(form, CompleteInfo.class);
assertThat(violations.size()).isEqualTo(1);
violations.forEach(action -> {
assertThat(action.getMessage()).isEqualTo("must not be blank");
assertThat(action.getPropertyPath().toString()).isEqualTo("firstName");
});
}
Next, test that when BasicInfo is complete, then the AdvanceInfo constraint should be evaluated:
接下来,测试一下当BasicInfo完成后,AdvanceInfo约束应该被评估。
@Test
public void whenBasicAndAdvanceInfoIsComplete_thenShouldNotGiveConstraintViolationsWithCompleteInfoValidationGroup() {
RegistrationForm form = buildRegistrationFormWithBasicAndAdvanceInfo();
Set<ConstraintViolation<RegistrationForm>> violations = validator.validate(form, CompleteInfo.class);
assertThat(violations.size()).isEqualTo(0);
}
5. Conclusion
5.总结
In this quick tutorial, we saw how to group javax.validation constraints.
在这个快速教程中,我们看到了如何对javax.validation约束进行分组。
As usual, all code snippets are available over on GitHub.
像往常一样,所有的代码片段都可以在GitHub上找到。