Contents

@notnull,@notempty和@notblank约束之间的差异

1. 概述

Bean Validation 是一个标准的验证规范,它允许我们通过使用以注解的形式声明的一组约束来轻松地验证域对象

虽然总体上使用 bean 验证实现(例如Hibernate Validator )相当简单,但值得探索一些关于如何实现这些约束的细微但相关的差异。

在本教程中,我们将探讨@NotNull@NotEmpty和*@NotBlank*约束**之间的区别。

2. Maven 依赖

要快速设置工作环境并测试*@NotNull*、@NotEmpty和*@NotBlank*约束的行为,首先我们需要添加所需的 Maven 依赖项。

在这种情况下,我们将使用bean 验证参考实现Hibernate Validator 来验证我们的域对象。

这是我们的pom.xml文件的相关部分:

<dependencies> 
    <dependency> 
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>6.0.13.Final</version>
    </dependency> 
    <dependency> 
        <groupId>org.glassfish</groupId>
        <artifactId>javax.el</artifactId>
        <version>3.0.0</version>
     </dependency>
</dependencies>

我们将在单元测试中使用JUnitAssertJ ,因此请务必检查最新版本的hibernate-validatorGlassFish 的 EL 实现junit 和Maven Central 上的 assertj-core

3. @NotNull约束

继续前进,让我们实现一个朴素的UserNotNull域类,并使用*@NotNull注解限制其name*字段:

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<ConstraintViolation<UserNotNull>> violations = validator.validate(user);
 
    assertThat(violations.size()).isEqualTo(0);
}
    
@Test
public void whenNullName_thenOneConstraintViolation() {
    UserNotNull user = new UserNotNull(null);
    Set<ConstraintViolation<UserNotNull>> violations = validator.validate(user);
 
    assertThat(violations.size()).isEqualTo(1);
}
    
@Test
public void whenEmptyName_thenNoConstraintViolations() {
    UserNotNull user = new UserNotNull("");
    Set<ConstraintViolation<UserNotNull>> violations = validator.validate(user);
 
    assertThat(violations.size()).isEqualTo(0);
}

正如预期的那样,@NotNull约束不允许受约束字段的空值。但是,字段可以为空。

为了更好地理解这一点,让我们看一下*@NotNull约束使用的NotNullValidator 类的isValid()*方法。方法实现真的很简单:

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

如上所示,@NotNull约束的字段(例如CharSequenceCollectionMapArray)必须不为空。然而,空值是完全合法的。**

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<ConstraintViolation<UserNotEmpty>> violations = validator.validate(user);
 
    assertThat(violations.size()).isEqualTo(0);
}
    
@Test
public void whenEmptyName_thenOneConstraintViolation() {
    UserNotEmpty user = new UserNotEmpty("");
    Set<ConstraintViolation<UserNotEmpty>> violations = validator.validate(user);
 
    assertThat(violations.size()).isEqualTo(1);
}
    
@Test
public void whenNullName_thenOneConstraintViolation() {
    UserNotEmpty user = new UserNotEmpty(null);
    Set<ConstraintViolation<UserNotEmpty>> violations = validator.validate(user);
 
    assertThat(violations.size()).isEqualTo(1);
}

@NotEmpty注解利用 @NotNull类的*isValid()*实现,并且还检查所提供对象的大小/长度(当然,这取决于被验证的对象的类型)是否大于零。

简而言之,这意味着受@NotEmpty约束的字段(例如CharSequenceCollectionMap或*Array)必须不为空,并且其大小/长度必须大于零*。

此外,如果我们将*@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<ConstraintViolation<UserNotBlank>> violations = validator.validate(user);
 
    assertThat(violations.size()).isEqualTo(0);
}
    
@Test
public void whenBlankName_thenOneConstraintViolation() {
    UserNotBlank user = new UserNotBlank(" ");
    Set<ConstraintViolation<UserNotBlank>> violations = validator.validate(user);
 
    assertThat(violations.size()).isEqualTo(1);
}
    
@Test
public void whenEmptyName_thenOneConstraintViolation() {
    UserNotBlank user = new UserNotBlank("");
    Set<ConstraintViolation<UserNotBlank>> violations = validator.validate(user);
 
    assertThat(violations.size()).isEqualTo(1);
}
    
@Test
public void whenNullName_thenOneConstraintViolation() {
    UserNotBlank user = new UserNotBlank(null);
    Set<ConstraintViolation<UserNotBlank>> 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*确实允许空值,但实际上不允许。

@NotNull 类的isValid ()方法在@NotBlank 类的isValid()之后调用,因此禁止空值。 简单来说,@NotBlank约束的String字段不能为空,修剪后的长度必须大于零**。

6. 并排比较

到目前为止,我们已经深入了解了*@NotNull*、@NotEmpty和*@NotBlank*约束如何在类字段上单独操作。

让我们进行快速的并排比较,以便我们可以鸟瞰约束的功能并轻松发现它们的差异:

  • @NotNull:受约束的CharSequenceCollectionMapArray只要不为空就有效,但可以为空。
  • @NotEmpty:受约束的CharSequenceCollectionMapArray是有效的,只要它不为空,并且它的大小/长度大于零。
  • @NotBlank:受约束的string只要不为空就有效,并且修剪后的长度大于零。