Разница между ограничениями @NotNull, @NotEmpty и @NotBlank при проверке бинов

Разница между ограничениями @NotNull, @NotEmpty и @NotBlank при проверке бинов

1. обзор

Bean Validationis a standard validation specification that allows us to easily validate domain objects by using a set of constraints declared in the form of annotations.

Хотя в целом использование реализаций проверки bean-компонентов, таких какHibernate Validator, довольно просто, стоит изучить некоторые тонкие, но актуальные различия, касающиеся того, как реализуются некоторые из этих ограничений.

В этом руководствеwe’ll spot the differences between the @NotNull, @NotEmpty, and @NotBlank constraints.

2. Зависимости Maven

Чтобы быстро настроить рабочую среду и протестировать поведение ограничений@NotNull,@NotEmpty и@NotBlank, сначала нам нужно добавить необходимые зависимости Maven.

В этом случае мы будем использоватьHibernate Validator, эталонную реализацию проверки bean-компонента, для проверки наших объектов домена.

Вот соответствующий раздел нашего файлаpom.xml:


    
        org.hibernate
        hibernate-validator
        6.0.13.Final
    
    
        org.glassfish
        javax.el
        3.0.0
     

Мы будем использоватьJUnit иAssertJ в наших модульных тестах, поэтому обязательно проверьте последние версииhibernate-validator,GlassFish’s EL implementation,junit иassertj-coreна Maven Central.

3. Ограничение@NotNull

Двигаясь вперед, давайте реализуем наивный класс доменаUserNotNull иconstrain его полеname с аннотацией@NotNull:

public class UserNotNull {

    @NotNull(message = "Name may not be null")
    private String name;

    // standard constructors / getters / toString
}

Теперь нам нужно увидеть, как@NotNull на самом деле работает под капотом

Для этого давайте создадим простой модульный тест для класса и проверим несколько его экземпляров:

@BeforeClass
public static void setupValidatorInstance() {
    validator = Validation.buildDefaultValidatorFactory().getValidator();
}

@Test
public void whenNotNullName_thenNoConstraintViolations() {
    UserNotNull user = new UserNotNull("John");
    Set> violations = validator.validate(user);

    assertThat(violations.size()).isEqualTo(0);
}

@Test
public void whenNullName_thenOneConstraintViolation() {
    UserNotNull user = new UserNotNull(null);
    Set> violations = validator.validate(user);

    assertThat(violations.size()).isEqualTo(1);
}

@Test
public void whenEmptyName_thenNoConstraintViolations() {
    UserNotNull user = new UserNotNull("");
    Set> violations = validator.validate(user);

    assertThat(violations.size()).isEqualTo(0);
}

Как и ожидалось, ограничение@NotNull не допускает нулевых значений для ограниченного поля (полей). Тем не менее, поле (я) может быть пустым.

Чтобы лучше понять это, давайте посмотрим на методNotNullValidator classisValid(), который используется ограничением@NotNull. Реализация метода действительно тривиальна:

public boolean isValid(Object object) {
    return object != null;
}

Как показано выше,a field (e.g. CharSequence, Collection, Map, or Array) constrained with @NotNull must be not null. An empty value, however, is perfectly legal.

4. Ограничение@NotEmpty

Теперь давайте реализуем образец классаUserNotEmpty и воспользуемся ограничением@NotEmpty:

public class UserNotEmpty {

    @NotEmpty(message = "Name may not be empty")
    private String name;

    // standard constructors / getters / toString
}

Имея класс на месте, давайте просто протестируем его, присвоив различные значения полюname:

@Test
public void whenNotEmptyName_thenNoConstraintViolations() {
    UserNotEmpty user = new UserNotEmpty("John");
    Set> violations = validator.validate(user);

    assertThat(violations.size()).isEqualTo(0);
}

@Test
public void whenEmptyName_thenOneConstraintViolation() {
    UserNotEmpty user = new UserNotEmpty("");
    Set> violations = validator.validate(user);

    assertThat(violations.size()).isEqualTo(1);
}

@Test
public void whenNullName_thenOneConstraintViolation() {
    UserNotEmpty user = new UserNotEmpty(null);
    Set> violations = validator.validate(user);

    assertThat(violations.size()).isEqualTo(1);
}

Аннотация@NotEmpty использует реализацию класса@NotNull 'isValid() и дополнительно проверяет, что размер / длина предоставленного объекта (конечно, это зависит от типа проверяемого объекта. ) больше нуля.

Вкратце,this means that a field (e.g. CharSequence, Collection, Map, or Array) constrained with @NotEmpty must be not null and its size/length must be greater than zero.

Кроме того, мы можем быть еще более ограничительными, если будем использовать аннотацию@NotEmpty вместе с@Size.

При этом мы также должны обеспечить, чтобы значения минимального и максимального размера объекта находились в пределах указанного минимального / максимального диапазона:

@NotEmpty(message = "Name may not be empty")
@Size(min = 2, max = 32, message = "Name must be between 2 and 32 characters long")
private String name;

5. Ограничение@NotBlank

Точно так же мы можем ограничить поле класса аннотацией@NotBlank:

public class UserNotBlank {

    @NotBlank(message = "Name may not be blank")
    private String name;

    // standard constructors / getters / toString

}

Аналогичным образом мы можем реализовать модульный тест, чтобы понять, как работает ограничение@NotBlank:

@Test
public void whenNotBlankName_thenNoConstraintViolations() {
    UserNotBlank user = new UserNotBlank("John");
    Set> violations = validator.validate(user);

    assertThat(violations.size()).isEqualTo(0);
}

@Test
public void whenBlankName_thenOneConstraintViolation() {
    UserNotBlank user = new UserNotBlank(" ");
    Set> violations = validator.validate(user);

    assertThat(violations.size()).isEqualTo(1);
}

@Test
public void whenEmptyName_thenOneConstraintViolation() {
    UserNotBlank user = new UserNotBlank("");
    Set> violations = validator.validate(user);

    assertThat(violations.size()).isEqualTo(1);
}

@Test
public void whenNullName_thenOneConstraintViolation() {
    UserNotBlank user = new UserNotBlank(null);
    Set> violations = validator.validate(user);

    assertThat(violations.size()).isEqualTo(1);
}

В аннотации@NotBlank используется классNotBlankValidator, который проверяет, что обрезанная длина последовательности символов не пуста:

public boolean isValid(
  CharSequence charSequence,
  ConstraintValidatorContext constraintValidatorContext)
    if (charSequence == null ) {
        return true;
    }
    return charSequence.toString().trim().length() > 0;
}

Забавно, но метод возвращает true для нулевых значений. Итак, мы можем подумать, что@NotBlank действительно допускает нулевые значения, но на самом деле это не так.

Метод isValid () класса@NotNull вызывается после класса@NotBlank isValid (), что запрещает нулевые значения.

Проще говоря,a String field constrained with @NotBlank must be not null and the trimmed length must be greater than zero.

6. Параллельное сравнение

Пока что мы подробно рассмотрели, как ограничения@NotNull,@NotEmpty и@NotBlank работают индивидуально с полями классов.

Давайте проведем быстрое параллельное сравнение, чтобы мы могли видеть функциональность ограничений с высоты птичьего полета и легко определять их различия:

  • @NotNull: ограниченныйCharSequence,Collection,Map, илиArray действителен, если он не равен нулю, но может быть пустым

  • @NotEmpty: ограниченныйCharSequence,Collection,Map, илиArray действителен, если он не равен нулю и его размер / длина больше нуля

  • @NotBlank: ограниченныйString действителен до тех пор, пока он не равен нулю и обрезанная длина больше нуля

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

В этой статье мы рассмотрели ограниченияNotNull,@NotEmpty и@NotBlank, реализованные в Bean Validation, и подчеркнули их сходства и различия.

Как обычно, доступны все примеры кода, показанные в этой статьеover on GitHub.