«1. Введение

В нашем учебнике по основам проверки Java Bean мы видели использование различных встроенных ограничений javax.validation. В этом руководстве мы увидим, как группировать ограничения javax.validation.

2. Пример использования

Существует много сценариев, в которых нам нужно применить ограничения к определенному набору полей компонента, а затем мы хотим применить ограничения к другому набору полей того же компонента.

Например, давайте представим, что у нас есть двухэтапная форма регистрации. На первом этапе мы просим пользователя предоставить основную информацию, такую ​​как имя, фамилия, идентификатор электронной почты, номер телефона и капча. Когда пользователь отправляет эти данные, мы хотим проверить только эту информацию.

На следующем этапе мы просим пользователя предоставить некоторую другую информацию, например адрес, и мы также хотим проверить эту информацию — обратите внимание, что капча присутствует на обоих этапах.

3. Группирование ограничений проверки

Все ограничения проверки javax имеют атрибут с именем groups. Когда мы добавляем ограничение к элементу, мы можем объявить имя группы, к которой принадлежит ограничение. Это делается путем указания имени класса группового интерфейса в атрибутах групп ограничения.

Лучший способ что-то понять — запачкать руки. Давайте посмотрим в действии, как мы объединяем ограничения javax в группы.

3.1. Объявление групп ограничений

Первым шагом является создание некоторых интерфейсов. Эти интерфейсы будут именами групп ограничений. В нашем случае использования мы разделяем ограничения проверки на две группы.

Давайте посмотрим на первую группу ограничений, BasicInfo:

public interface BasicInfo {
}

Следующая группа ограничений, AdvanceInfo:

public interface AdvanceInfo {
}

3.2. Использование групп ограничений

Теперь, когда мы объявили наши группы ограничений, пришло время использовать их в нашем Java-бине RegistrationForm:

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;
}

С помощью атрибута групп ограничений мы разделили поля нашего компонента на две группы. в соответствии с нашим вариантом использования. По умолчанию все ограничения включены в группу ограничений по умолчанию.

3.3. Тестирование ограничений с одной группой

Теперь, когда мы объявили группы ограничений и использовали их в нашем классе компонентов, пришло время увидеть эти группы ограничений в действии.

Во-первых, мы увидим, когда основная информация не будет полной, используя нашу группу ограничений BasicInfo для проверки. Мы должны получить нарушение ограничения для любого поля, оставленного пустым, где мы использовали BasicInfo.class в атрибуте groups ограничения поля @NotBlank:

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("[email protected]");
        form.setPhone("12345");
        form.setCaptcha("Y2HAhU5T");
        return form;
    }
 
    //... additional tests
}

В следующем сценарии мы проверим, когда расширенная информация неполна, используя нашу группу ограничений 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. Тестирование ограничений, имеющих несколько групп

Мы можем указать несколько групп для ограничения. В нашем случае мы используем капчу как в базовой, так и в расширенной информации. Давайте сначала проверим капчу с помощью BasicInfo:


@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");
    });
}

Теперь давайте проверим капчу с помощью AdvanceInfo:

@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. Указание порядка проверки группы ограничений с помощью GroupSequence

По умолчанию группы ограничений не оцениваются в какой-либо конкретный заказ. Но у нас могут быть случаи использования, когда некоторые группы должны быть проверены раньше других. Для этого мы можем указать порядок групповой проверки с помощью GroupSequence.

Существует два способа использования аннотации GroupSequence:

    на объекте, проверяемом на интерфейсе

4.1. Использование GroupSequence для проверяемой сущности

Это простой способ упорядочить ограничения. Давайте аннотируем сущность с помощью 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. Использование GroupSequence на интерфейсе

Мы также можем указать порядок проверки ограничений с помощью интерфейса. Преимущество этого подхода в том, что ту же последовательность можно использовать для других сущностей. Давайте посмотрим, как мы можем использовать GroupSequence с интерфейсами, которые мы определили выше:

@GroupSequence({BasicInfo.class, AdvanceInfo.class})
public interface CompleteInfo {
}

4.3. Тестирование GroupSequence

Теперь давайте проверим 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");
    });
}

«

@Test
public void whenBasicAndAdvanceInfoIsComplete_thenShouldNotGiveConstraintViolationsWithCompleteInfoValidationGroup() {
    RegistrationForm form = buildRegistrationFormWithBasicAndAdvanceInfo();
 
    Set<ConstraintViolation<RegistrationForm>> violations = validator.validate(form, CompleteInfo.class);
 
    assertThat(violations.size()).isEqualTo(0);
}

«Затем проверьте, что после завершения BasicInfo следует оценить ограничение AdvanceInfo:

5. Заключение

В этом кратком руководстве мы увидели, как группировать ограничения javax.validation.