Contents

Hibernate Validator 约束

1. 概述

在本教程中,我们将回顾 Hibernate Validator 约束,这些约束内置在 Hibernate Validator 中,但在 Bean Validation 规范之外。

有关 Bean 验证的回顾,请参阅我们关于 Java Bean 验证基础 的文章。

2. Hibernate Validator设置

至少,我们应该将Hibernate Validator 添加到我们的依赖项中:

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.16.Final</version>
</dependency>

请注意,Hibernate Validator 不依赖于Hibernate,即我们在许多其他文章中介绍过的 ORM。

此外,我们将介绍的一些注解仅适用于我们的项目使用某些库的情况。因此,对于其中的每一个,我们将指出必要的依赖关系。

3. 验证价值

3.1. 验证信用卡号

有效的信用卡号必须满足校验和,我们使用Luhn 算法 计算。当字符串满足校验和时,@CreditCardNumber约束成功。

**@CreditCardNumber不对输入字符串执行任何其他检查。**特别是,它不检查输入的长度。因此,它只能检测由于小错字而无效的数字。

请注意,默认情况下,如果字符串包含不是数字的字符,则约束会失败,但我们可以告诉它忽略它们:

@CreditCardNumber(ignoreNonDigitCharacters = true)
private String lenientCreditCardNumber;

然后,我们可以包含空格或破折号等字符:

validations.setLenientCreditCardNumber("7992-7398-713");
constraintViolations = validator.validateProperty(validations, "lenientCreditCardNumber");
assertTrue(constraintViolations.isEmpty());

3.2. 验证货币价值

@Currency验证器检查给定的货币金额是否为指定货币:

@Currency("EUR")
private MonetaryAmount balance;

MonetaryAmount是 Java Money 的一部分。因此,@Currency仅适用于Java Money 实现可用的情况

一旦我们正确设置了 Java Money,我们就可以检查约束:

bean.setBalance(Money.of(new BigDecimal(100.0), Monetary.getCurrency("EUR")));
constraintViolations = validator.validateProperty(bean, "balance");
assertEquals(0, constraintViolations.size());

4. 验证范围

4.1. 数字和货币范围

bean 验证规范定义了几个我们可以对数字字段强制执行的约束。除此之外,Hibernate Validator 还提供了一个方便的注解*@Range*,它作为*@Min@Max*的组合,包含匹配一个范围:

@Range(min = 0, max = 100)
private BigDecimal percent;

与*@Min@Max一样,@Range适用于原始数字类型的字段及其包装器;BigIntegerBigDecimal*, 上面的字符串表示形式,最后是MonetaryValue字段。

4.2. 持续时间

除了代表时间点的值的标准 JSR 380 注解之外,Hibernate Validator 还包括Duration的约束。确保首先检查Java Time的PeriodDuration  。

因此,我们可以对属性强制执行最小和最大持续时间:

@DurationMin(days = 1, hours = 2)
@DurationMax(days = 2, hours = 1)
private Duration duration;

即使我们没有在这里全部显示,注解也具有从纳秒到天的所有时间单位的参数。

请注意,默认情况下,**最小值和最大值都包含在内。**也就是说,与最小值或最大值完全相同的值将通过验证。

如果我们希望边界值无效,相反,我们将inclusive属性定义为 false:

@DurationMax(minutes = 30, inclusive = false)

5. 验证字符串

5.1. 字符串长度

我们可以使用两个稍微不同的约束来强制字符串具有一定的长度。

通常,我们希望确保字符串的字符长度(我们使用Length方法测量的长度)在最小值和最大值之间。在这种情况下,我们在 String 属性或字段上使用*@Length* :

@Length(min = 1, max = 3)
private String someString;

但是,由于 Unicode 的复杂性,有时字符的长度和代码点的长度会有所不同。当我们要检查后者时,我们使用@CodePointLength:**

@CodePointLength(min = 1, max = 3)
private String someString;

例如,字符串“aa\uD835\uDD0A”有 4 个字符长,但它只包含 3 个代码点,所以它会通过第一个约束并通过第二个约束。

此外,对于这两个注解,我们可以省略最小值或最大值。

5.2. 检查数字字符串

我们已经了解了如何检查字符串是否为有效的信用卡号。但是,Hibernate Validator 包括其他几个对数字字符串的约束。

我们正在审查的第一个是*@LuhnCheck*。这是*@CreditCardNumber* 的通用版本,因为它执行相同的检查,但允许附加参数:

@LuhnCheck(startIndex = 0, endIndex = Integer.MAX_VALUE, checkDigitIndex = -1)
private String someString;

在这里,我们已经展示了参数的默认值,所以上面相当于一个简单的*@LuhnCheck*注解。

但是,正如我们所见,我们可以对子字符串(startIndexendIndex)执行检查,并告诉约束哪个数字是校验和数字,-1 表示检查的子字符串中的最后一位。

其他有趣的约束包括模 10 检查 ( @Mod10Check ) 和模 11 检查 ( @Mod11Check ),它们通常用于条形码和其他代码,例如 ISBN。

但是,对于这些特定情况,Hibernate Validator 恰好提供了一个约束来验证 ISBN 代码*@ISBN以及EAN 条形码@EAN*约束。

5.3. URL 和 HTML 验证

@Url约束验证字符串是否是 URL 的有效表示。此外,我们可以检查 URL 的特定组件是否具有特定值:

@URL(protocol = "https")
private String url;

因此,我们可以检查协议、主机和端口。如果这还不够,我们可以使用regexp属性将 URL 与正则表达式进行匹配。

我们还可以验证一个属性是否包含“安全”的 HTML 代码(例如,没有脚本标签):

@SafeHtml
private String html;

@SafeHtml使用JSoup 库 ,它必须包含在我们的依赖项中。

我们可以使用内置的标签白名单(注解的白名单属性)并包括额外的标签和属性(additionalTagsadditionalTagsWithAttributes参数)来根据我们的需要定制 HTML 清理。

6. 其他约束

让我们简单提一下,Hibernate Validator 包括一些特定于国家和地区的约束,特别是对于一些巴西和波兰的身份证号、纳税人代码等。请参阅文档的相关部分以 获取完整列表。

此外,我们可以使用*@UniqueElements*检查集合不包含重复项。

最后,对于现有注解未涵盖的复杂情况,我们可以调用用 JSR-223 兼容的脚本引擎编写的脚本。当然,我们在关于 Nashorn 的文章 中谈到了 JSR-223,这是现代 JVM 中包含的 JavaScript 实现。

在这种情况下,注解在类级别,脚本在整个实例上调用,作为变量*_this* 传递:

@ScriptAssert(lang = "nashorn", script = "_this.valid")
public class AdditionalValidations {
    private boolean valid = true;
    // standard getters and setters
}

然后,我们可以检查整个实例的约束:

bean.setValid(false);
constraintViolations = validator.validate(bean);
assertEquals(1, constraintViolations.size());