Validation

Jakarta Bean Validation

Jakarta Bean Validation 提供了一套常见的内置约束注解,涵盖了不同类型的数据验证需求。以下是这些内置约束注解的详细解释,包括其功能、适用场景和使用示例。

1. @NotNull

  • 功能: 验证字段不能为 null,但可以是空字符串、空集合等。
  • 适用类型: 任何引用类型(如对象、集合、数组等),不适用于原始类型(int, boolean 等)。
  • 示例:
    @NotNull(message = "用户名不能为空")
    private String username;
    
    如果 usernamenull,验证将失败。

2. @NotEmpty

  • 功能: 验证集合、数组或字符串不能为 null 且不能为空。
  • 适用类型: 字符串、集合、数组。
  • 示例:
    @NotEmpty(message = "用户名不能为空")
    private String username;
    
    @NotEmpty(message = "角色列表不能为空")
    private List<String> roles;
    
    如果 username 是空字符串 ""roles 是空集合 [],验证将失败。

3. @NotBlank

  • 功能: 验证字符串不能为 null,且去除空格后必须至少包含一个非空字符。
  • 适用类型: 字符串。
  • 示例:
    @NotBlank(message = "用户名不能为空")
    private String username;
    
    如果 usernamenull、空字符串 "" 或只包含空格 " ",验证将失败。

4. @Size

  • 功能: 验证字符串、数组、集合或 Map 的长度或大小在指定范围内。
  • 适用类型: 字符串、数组、集合、Map。
  • 示例:
    @Size(min = 5, max = 15, message = "用户名长度必须在5到15个字符之间")
    private String username;
    
    @Size(min = 1, message = "至少要有一个角色")
    private List<String> roles;
    
    验证 username 的长度必须在 5 到 15 个字符之间,roles 至少要包含一个元素。

5. @Min@Max

  • 功能: 验证数值必须大于等于 @Min 指定的值,或小于等于 @Max 指定的值。
  • 适用类型: 数值类型(如 int, long, double)。
  • 示例:
    @Min(value = 18, message = "年龄不能小于18岁")
    @Max(value = 65, message = "年龄不能大于65岁")
    private int age;
    
    验证 age 必须在 18 到 65 之间。

6. @Pattern

  • 功能: 验证字符串是否符合指定的正则表达式模式。
  • 适用类型: 字符串。
  • 示例:
    @Pattern(regexp = "^[A-Za-z0-9]+$", message = "用户名只能包含字母和数字")
    private String username;
    
    验证 username 只能包含字母和数字。

7. @Email

  • 功能: 验证字符串是否是一个合法的电子邮件地址。
  • 适用类型: 字符串。
  • 示例:
    @Email(message = "电子邮件格式不正确")
    private String email;
    
    验证 email 是否符合电子邮件格式。

8. @Positive@PositiveOrZero

  • 功能: @Positive 验证数值必须为正数;@PositiveOrZero 验证数值必须为正数或零。
  • 适用类型: 数值类型。
  • 示例:
    @Positive(message = "库存数量必须为正数")
    private int stock;
    
    @PositiveOrZero(message = "库存不能为负数")
    private int stock;
    

9. @Negative@NegativeOrZero

  • 功能: @Negative 验证数值必须为负数;@NegativeOrZero 验证数值必须为负数或零。
  • 适用类型: 数值类型。
  • 示例:
    @Negative(message = "余额必须为负数")
    private BigDecimal balance;
    
    @NegativeOrZero(message = "余额必须为零或负数")
    private BigDecimal balance;
    

10. @Future@FutureOrPresent

  • 功能: @Future 验证日期必须是将来的时间;@FutureOrPresent 验证日期必须是将来或当前时间。
  • 适用类型: 日期类型(如 java.util.Date, java.time.LocalDateTime, java.time.LocalDate)。
  • 示例:
    @Future(message = "预定日期必须是未来的时间")
    private LocalDate bookingDate;
    
    如果 bookingDate 是当前时间或过去的时间,验证将失败。

11. @Past@PastOrPresent

  • 功能: @Past 验证日期必须是过去的时间;@PastOrPresent 验证日期必须是过去或当前时间。
  • 适用类型: 日期类型。
  • 示例:
    @Past(message = "出生日期必须是过去的时间")
    private LocalDate birthDate;
    

12. @AssertTrue@AssertFalse

  • 功能: @AssertTrue 验证字段的值必须为 true@AssertFalse 验证字段的值必须为 false
  • 适用类型: 布尔类型。
  • 示例:
    @AssertTrue(message = "必须同意用户协议")
    private boolean agreed;
    
    @AssertFalse(message = "这个选项不能为真")
    private boolean someFlag;
    

13. @Digits

  • 功能: 验证数值的整数部分和小数部分的位数。
  • 适用类型: 数值类型。
  • 示例:
    @Digits(integer = 5, fraction = 2, message = "金额格式不正确,整数部分最多5位,小数部分最多2位")
    private BigDecimal amount;
    

14. @DecimalMin@DecimalMax

  • 功能: 验证数值是否大于等于(@DecimalMin)或小于等于(@DecimalMax)指定的值,支持小数。
  • 适用类型: 数值类型。
  • 示例:
    @DecimalMin(value = "0.01", message = "金额不能小于0.01")
    private BigDecimal price;
    
    @DecimalMax(value = "10000.00", message = "金额不能超过10000.00")
    private BigDecimal price;
    

15. @CreditCardNumber

  • 功能: 验证是否是合法的信用卡号(基于 Luhn 校验算法)。
  • 适用类型: 字符串。
  • 示例:
    @CreditCardNumber(message = "信用卡号不合法")
    private String creditCardNumber;
    

16. @Range(来自 Hibernate Validator)

  • 功能: 验证数值是否在指定的范围内(类似于 @Min@Max)。
  • 适用类型: 数值类型。
  • 示例:
    @Range(min = 1, max = 100, message = "值必须在1到100之间")
    private int number;
    

17. @URL(来自 Hibernate Validator)

  • 功能: 验证字符串是否是合法的 URL。
  • 适用类型: 字符串。
  • 示例:
    @URL(message = "URL格式不正确")
    private String website;
    

18. @SafeHtml(来自 Hibernate Validator)

  • 功能: 验证字符串是否仅包含安全的 HTML 内容,防止 XSS 攻击。
  • 适用类型: 字符串。
  • 示例:
    @SafeHtml(message = "HTML内容不安全")
    private String htmlContent;
    

19. @UniqueElements(来自 Hibernate Validator)

  • 功能: 验证集合中的元素是否唯一。
  • 适用类型: 集合类型。
  • 示例:
    @UniqueElements(message = "集合中的元素必须唯一")
    private List<String> tags;
    

Hibernate Validator

org.hibernate.validatorHibernate Validator 的核心包,它是 Jakarta Bean Validation(JSR 380)的参考实现。除了 Jakarta Bean Validation 标准提供的内置约束注解外,Hibernate Validator 还扩展了许多额外的注解,用于更复杂或特定场景的数据校验。以下是 Hibernate Validator 提供的全部内置约束注解的详细讲解。

1. @Length

  • 功能: 验证字符串的长度是否在指定范围内。
  • 适用类型: 字符串。
  • 示例:
    @Length(min = 5, max = 15, message = "用户名长度必须在5到15个字符之间")
    private String username;
    
    如果 username 的长度不在 5 到 15 个字符之间,验证将失败。

2. @Range

  • 功能: 验证数值是否在指定的范围内。
  • 适用类型: 数值类型(如 int, long, double)。
  • 示例:
    @Range(min = 1, max = 100, message = "值必须在1到100之间")
    private int value;
    
    验证 value 是否在 1 到 100 之间。

3. @CreditCardNumber

  • 功能: 验证字符串是否是有效的信用卡号(基于 Luhn 校验算法)。
  • 适用类型: 字符串。
  • 示例:
    @CreditCardNumber(message = "信用卡号不合法")
    private String creditCardNumber;
    
    验证信用卡号是否合法。

4. @URL

  • 功能: 验证字符串是否是合法的 URL。
  • 适用类型: 字符串。
  • 示例:
    @URL(message = "URL格式不正确")
    private String website;
    
    验证 website 是否是合法的 URL。

5. @EAN

  • 功能: 验证字符串是否是有效的 EAN(国际商品编码)。支持 EAN-8 和 EAN-13。
  • 适用类型: 字符串。
  • 示例:
    @EAN(type = EAN.Type.EAN13, message = "必须是有效的EAN-13编码")
    private String productCode;
    
    验证 productCode 是否为有效的 EAN-13 代码。

6. @ISBN

  • 功能: 验证字符串是否是有效的 ISBN(国际标准书号)。支持 ISBN-10 和 ISBN-13。
  • 适用类型: 字符串。
  • 示例:
    @ISBN(type = ISBN.Type.ISBN13, message = "必须是有效的ISBN-13编码")
    private String bookCode;
    
    验证 bookCode 是否为有效的 ISBN-13。

7. @SafeHtml

  • 功能: 验证字符串是否仅包含安全的 HTML 内容。它用于防止 XSS(跨站脚本)攻击。
  • 适用类型: 字符串。
  • 示例:
    @SafeHtml(message = "HTML内容不安全")
    private String description;
    
    验证 description 是否仅包含安全的 HTML 标签。

8. @UniqueElements

  • 功能: 验证集合或数组中的元素是否唯一。
  • 适用类型: 集合、数组。
  • 示例:
    @UniqueElements(message = "集合中的元素必须唯一")
    private List<String> tags;
    
    验证 tags 列表中的元素是否唯一。

9. @ScriptAssert

  • 功能: 通过脚本表达式进行自定义校验,支持 Groovy、JavaScript 等语言的脚本校验。
  • 适用类型: 类级别的约束注解,用于校验整个对象。
  • 示例:
    @ScriptAssert(lang = "groovy", script = "_this.startDate.before(_this.endDate)", message = "开始日期必须早于结束日期")
    public class Event {
        private Date startDate;
        private Date endDate;
    }
    
    验证 startDate 必须早于 endDate

10. @ParameterScriptAssert

  • 功能: 类似于 @ScriptAssert,但用于方法参数的校验。
  • 适用类型: 方法级别的约束注解。
  • 示例:
    @ParameterScriptAssert(lang = "groovy", script = "arg1 != null && arg1.size() > 0", message = "参数不能为空")
    public void process(@Valid List<String> data) {
        // 方法逻辑
    }
    
    验证传递给 data 的参数不能为空且不为空列表。

11. @Email(Hibernate 扩展)

  • 功能: 验证字符串是否是合法的电子邮件地址。相比于 Jakarta 标准的 @Email 注解,Hibernate 的 @Email 支持更多高级配置,如允许不包含顶级域名的电子邮件地址。
  • 适用类型: 字符串。
  • 示例:
    @Email(message = "邮箱格式不正确")
    private String email;
    

12. @Mod10Check@Mod11Check

  • 功能: 验证字符串是否符合 Mod10 或 Mod11 校验算法,通常用于校验条形码或身份证号等。
  • 适用类型: 字符串。
  • 示例:
    @Mod10Check(message = "条形码不合法")
    private String barcode;
    

13. @CNPJ@CPF

  • 功能: 验证巴西的企业识别号(CNPJ)和个人识别号(CPF)是否合法。
  • 适用类型: 字符串。
  • 示例:
    @CPF(message = "CPF号不合法")
    private String cpf;
    
    @CNPJ(message = "CNPJ号不合法")
    private String cnpj;
    

14. @LuhnCheck

  • 功能: 使用 Luhn 校验算法验证数值是否合法。Luhn 算法常用于验证信用卡号等。
  • 适用类型: 字符串、数字。
  • 示例:
    @LuhnCheck(message = "信用卡号不合法")
    private String creditCardNumber;
    

15. @Currency

  • 功能: 验证字段是否符合 ISO 4217 货币代码(例如 USD、EUR)。
  • 适用类型: 字符串。
  • 示例:
    @Currency(value = "USD", message = "必须是有效的货币代码")
    private String currency;
    

16. @DurationMax@DurationMin

  • 功能: 验证 java.time.Duration 对象的时间长度是否在指定的范围内。
  • 适用类型: java.time.Duration 类型。
  • 示例:
    @DurationMin(hours = 1, message = "持续时间必须至少1小时")
    @DurationMax(days = 1, message = "持续时间不能超过1天")
    private Duration eventDuration;
    

17. @ParameterScriptAssert

  • 功能: 用于方法参数的复杂自定义验证,通过脚本表达式定义校验逻辑。
  • 适用类型: 方法参数。
  • 示例:
    @ParameterScriptAssert(lang = "groovy", script = "arg1 > arg2", message = "第一个参数必须大于第二个参数")
    public void validateParams(int param1, int param2) {
        // 逻辑
    }
    

18. @Digits

  • 功能: 验证数值的整数部分和小数部分的位数。
  • 适用类型: 数值类型。
  • 示例:
    @Digits(integer = 5, fraction = 2, message = "金额格式不正确,整数部分最多5位,小数部分最多2位")
    private BigDecimal price;
    

Jakarta Bean ValidationHibernate Validator 都支持自定义验证器,允许你根据特定的业务需求定义自定义的验证规则。自定义验证器的工作原理是通过创建一个自定义的约束注解(Constraint),并为其实现对应的验证逻辑。

Validator(验证器)

Jakarta Bean ValidationHibernate Validator 都允许自定义验证器,使你可以根据业务需求实现高度定制的校验规则。

自定义验证器的场景:

  • 特定业务规则:如验证用户输入的用户名格式、密码强度、特殊业务规则等。
  • 跨字段验证:如验证两个字段的值是否一致(如密码与确认密码)。
  • 复杂对象验证:如验证对象内的嵌套对象或列表中的元素是否满足特定条件。
  • 自定义验证器通过注解、ConstraintValidator 接口以及实体类的结合来工作,可以处理简单或复杂的验证逻辑。
  • 可以在自定义注解中传递参数,使验证器更加灵活,满足不同的验证需求。

自定义验证器的步骤:

  1. 创建自定义注解:定义注解用于标识需要应用验证规则的字段或方法。
  2. 实现 ConstraintValidator 接口:编写验证逻辑。
  3. 将自定义注解应用到实体类:在需要的字段或方法上使用该自定义注解。

1. 创建自定义注解

首先需要定义一个自定义注解。这个注解必须使用 @Constraint 注解来指定验证器类,并且它本身可以包含验证消息、分组、以及负载类型的参数。

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// 自定义注解
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MyCustomValidator.class) // 绑定验证器类
public @interface MyCustomConstraint {

    String message() default "自定义校验失败";  // 默认错误消息

    Class<?>[] groups() default {};  // 分组

    Class<? extends Payload>[] payload() default {};  // 用于附加信息的载荷
}
  • @Target: 指定自定义注解的适用范围,FIELD 表示可以应用于字段,METHOD 表示可以应用于方法,PARAMETER 表示可以应用于方法参数。
  • @Retention: 表示注解的生命周期,RUNTIME 意味着注解将在运行时有效。
  • @Constraint: 指定验证器类,通过 validatedBy 属性将注解与验证逻辑类绑定。
  • message: 定义校验失败时的默认错误消息。
  • groupspayload: 这两个属性用于分组校验和自定义载荷,是 Jakarta Bean Validation 规范的要求,尽管你在大多数情况下不会使用它们。

2. 实现 ConstraintValidator 接口

在实现自定义验证逻辑时,需要创建一个实现 ConstraintValidator 接口的类。该类定义如何对目标对象进行验证。

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class MyCustomValidator implements ConstraintValidator<MyCustomConstraint, String> {

    @Override
    public void initialize(MyCustomConstraint constraintAnnotation) {
        // 可以在此初始化一些逻辑,比如读取注解的参数
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        // 自定义校验逻辑
        if (value == null || value.contains("abc")) {
            return true; // 校验通过
        }
        return false; // 校验失败
    }
}
  • ConstraintValidator 是一个泛型接口,接收两个参数:

    1. 第一个参数是自定义注解的类型(MyCustomConstraint)。
    2. 第二个参数是要校验的数据类型(这里是 String)。
  • initialize 方法允许在验证器初始化时执行一些逻辑(如从注解中读取属性值),通常用于准备工作。

  • isValid 方法包含实际的校验逻辑,如果验证成功,返回 true,否则返回 false

3. 在实体类中使用自定义注解

最后,将自定义注解应用于实体类中的字段、方法或参数,来验证数据。

public class User {

    @MyCustomConstraint(message = "用户名必须包含 'abc'")
    private String username;

    // getters and setters
}

在这个例子中,username 字段将会使用自定义的验证规则进行校验:如果 username 包含字符串 "abc",验证通过,否则验证失败。

自定义验证器的高级用法

在自定义验证器中,你可以定义更复杂的校验逻辑,并根据需求自定义注解的行为。

1. 处理复杂数据类型

自定义验证器不仅仅可以处理简单类型(如 String),还可以处理复杂类型(如 ListMap、自定义类等)。例如,可以编写一个验证器,验证列表中的每个元素是否符合某个规则。

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import java.util.List;

public class ListValidator implements ConstraintValidator<MyCustomConstraint, List<String>> {

    @Override
    public boolean isValid(List<String> value, ConstraintValidatorContext context) {
        if (value == null || value.isEmpty()) {
            return false; // 验证列表是否为空
        }
        // 验证列表中的每个元素
        for (String element : value) {
            if (element == null || !element.matches("[A-Za-z0-9]+")) {
                return false;
            }
        }
        return true;
    }
}

在实体类中使用:

public class Product {

    @MyCustomConstraint(message = "标签列表中必须包含有效的标签")
    private List<String> tags;

    // getters and setters
}

2. 使用注解的参数

自定义注解可以接收参数,并在 ConstraintValidator 中使用这些参数动态调整校验逻辑。

修改注解定义以接收参数:

@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MyCustomValidator.class)
public @interface MyCustomConstraint {

    String message() default "自定义校验失败";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    // 自定义参数
    String prefix() default "abc";
}

在验证器中使用参数:

public class MyCustomValidator implements ConstraintValidator<MyCustomConstraint, String> {

    private String prefix;

    @Override
    public void initialize(MyCustomConstraint constraintAnnotation) {
        this.prefix = constraintAnnotation.prefix(); // 从注解中读取参数
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        // 根据 prefix 参数进行校验
        if (value != null && value.startsWith(prefix)) {
            return true;
        }
        return false;
    }
}

使用时可以指定不同的 prefix 参数:

public class User {

    @MyCustomConstraint(prefix = "user", message = "用户名必须以 'user' 开头")
    private String username;

    // getters and setters
}

3. 组合约束

你可以组合多个约束注解,形成一个复杂的自定义校验逻辑。组合约束允许你复用现有的注解。

import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

@NotNull
@Size(min = 5, max = 15)
@MyCustomConstraint
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface CombinedConstraint {
    String message() default "组合校验失败";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

在实体类中使用组合约束:

public class User {

    @CombinedConstraint(message = "用户名校验失败")
    private String username;

    // getters and setters
}

分组验证(Groups)

  • 分组验证(Groups) 是一种高级功能,允许在不同业务场景中应用不同的验证规则。
  • 分组接口 用于标识和区分不同的验证逻辑。
  • 可以通过 @GroupSequence 设定分组验证的执行顺序,以确保某些组的验证优先执行。
  • Hibernate Validator 提供了扩展功能,如分组转换(Group Conversion)和对集合元素进行分组验证。
  • 在 Spring 中,可以使用 @Validated 注解来指定分组验证,结合 Bean Validation 和服务层逻辑进行数据验证。

Jakarta Bean ValidationHibernate Validator 的分组验证(Groups)是验证机制中一种高级功能,它允许开发者为相同的实体类字段或方法指定不同的验证规则,并根据业务需求选择性地应用这些验证规则。分组验证使得在不同场景(如创建、更新、删除等)时,能够灵活地应用不同的验证逻辑。

1. 分组验证的基本概念

在 Jakarta Bean Validation 中,所有的验证默认属于 默认组Default 组)。如果没有指定验证组,验证器会将所有注解都视为属于 Default 组并执行。

通过分组验证,开发者可以创建多个验证组,并为每个字段定义不同组下的验证规则。这样可以根据不同的场景(如表单验证、API 调用、实体更新等)来选择性应用验证规则。

2. 如何定义和使用分组验证

a. 定义分组接口

首先,需要定义空接口来表示验证分组。通常这些接口只起标记作用,不需要任何方法。

public interface CreateGroup {}
public interface UpdateGroup {}

b. 在实体类中使用分组

通过 groups 属性为每个约束注解指定它属于哪个分组。

import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

public class User {

    @NotNull(message = "用户名不能为空", groups = CreateGroup.class)
    @Size(min = 5, max = 20, message = "用户名长度必须在5到20个字符之间", groups = {CreateGroup.class, UpdateGroup.class})
    private String username;

    @NotNull(message = "密码不能为空", groups = CreateGroup.class)
    @Size(min = 8, message = "密码长度至少为8个字符", groups = CreateGroup.class)
    private String password;
    
    // getters 和 setters
}

在这个例子中:

  • username 字段在 CreateGroup 分组下需要满足非空和长度限制,而在 UpdateGroup 分组下,只需要满足长度限制。
  • password 字段只在创建用户时(CreateGroup)需要验证,更新时不需要。

c. 验证时指定分组

不推荐,这种会增加代码的可读性

要使用分组验证,需要在调用 Validator.validate() 方法时指定分组。Jakarta Bean Validation 提供了 Validator 接口,可以手动指定需要验证的分组。

import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;

public class UserService {

    private Validator validator;

    public UserService() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        this.validator = factory.getValidator();
    }

    // 创建用户时使用创建分组
    public void createUser(User user) {
        Set<ConstraintViolation<User>> violations = validator.validate(user, CreateGroup.class);
        handleViolations(violations);
    }

    // 更新用户时使用更新分组
    public void updateUser(User user) {
        Set<ConstraintViolation<User>> violations = validator.validate(user, UpdateGroup.class);
        handleViolations(violations);
    }

    private void handleViolations(Set<ConstraintViolation<User>> violations) {
        if (!violations.isEmpty()) {
            for (ConstraintViolation<User> violation : violations) {
                System.out.println(violation.getPropertyPath() + ": " + violation.getMessage());
            }
        }
    }
}

在创建和更新用户时,分别调用 validate() 方法并传递不同的分组(CreateGroupUpdateGroup),以便应用不同的验证规则。

3. 组合验证分组

a. 同时应用多个分组

如果你想在一次验证中应用多个分组,可以在 validate() 方法中传递多个分组类。系统会同时执行这些组中的所有验证规则。

Set<ConstraintViolation<User>> violations = validator.validate(user, CreateGroup.class, UpdateGroup.class);

b. 通过 @GroupSequence 设定验证顺序

在某些情况下,你可能希望按特定顺序执行不同的验证组。例如,先执行某些基本验证组(如 Default 组),然后再执行其他验证组。可以使用 @GroupSequence 来定义验证的执行顺序。

import jakarta.validation.GroupSequence;

@GroupSequence({Default.class, CreateGroup.class, UpdateGroup.class})
public interface OrderedValidation {}

这样,当使用 OrderedValidation 作为验证组时,验证器会先执行 Default 组的验证,然后执行 CreateGroup 组,最后执行 UpdateGroup 组。如果某一组的验证失败,后续的验证将不会执行。

在验证时,你可以这样调用:

Set<ConstraintViolation<User>> violations = validator.validate(user, OrderedValidation.class);

4. 在 Spring 中使用分组验证

如果你在 Spring 项目中使用 Jakarta Bean Validation 或 Hibernate Validator,分组验证也可以通过 @Validated 注解在控制器或服务中使用。

使用示例:

在 Spring 中的控制器或服务层中,你可以通过 @Validated 注解指定要使用的分组。

@RestController
public class UserController {

    @PostMapping("/createUser")
    public ResponseEntity<String> createUser(@RequestBody @Validated(CreateGroup.class) User user) {
        // 创建用户的逻辑
        return ResponseEntity.ok("用户创建成功");
    }

    @PutMapping("/updateUser")
    public ResponseEntity<String> updateUser(@RequestBody @Validated(UpdateGroup.class) User user) {
        // 更新用户的逻辑
        return ResponseEntity.ok("用户更新成功");
    }
}

在这个例子中,@Validated(CreateGroup.class)@Validated(UpdateGroup.class) 注解分别在不同的 API 方法中应用,分别在创建和更新用户时执行不同的验证逻辑。

5. Hibernate Validator 的扩展分组功能

Hibernate Validator 是 Jakarta Bean Validation 的实现,它在标准规范基础上添加了一些扩展功能。关于分组验证,Hibernate Validator 提供了以下扩展功能:

a. Group Conversion

在某些情况下,Hibernate Validator 允许将嵌套对象中的一个分组自动转换为另一个分组。这种转换有助于在复杂对象之间进行不同的分组映射。

public class Parent {

    @Valid
    @ConvertGroup(from = Default.class, to = ChildGroup.class)
    private Child child;
}

在这个例子中,当 Parent 对象被验证时,Default 组的验证在 Child 对象上被转换为 ChildGroup 组的验证。

b. 在集合元素上应用分组

你还可以在集合的每个元素上应用分组验证,例如验证列表中的每个元素是否符合特定的分组规则:

public class GroupExample {

    @NotEmpty
    @Valid
    private List<@Validated(CreateGroup.class) User> users;

    // getters 和 setters
}