Published on

优雅的新增和更新实例

Authors
  • avatar
    Name
    lzs39
    Twitter

优雅的新增和更新实例

使用 Hibernate Validator(Bean Validation 实现)对 DTO 参数进行校验的步骤如下:

1. 添加依赖(Maven)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

2. 在 DTO 字段上添加校验注解

import javax.validation.constraints.*;
import java.util.List;

public class UserDTO {
    
    @NotBlank(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度需在3-20之间")
    private String username;

    @Email(message = "邮箱格式不正确")
    private String email;

    @NotNull(message = "年龄不能为空")
    @Min(value = 18, message = "年龄需满18岁")
    @Max(value = 100, message = "年龄不能超过100岁")
    private Integer age;

    @Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$", 
             message = "密码需至少8字符,包含字母和数字")
    private String password;

    @AssertTrue(message = "必须接受条款")
    private Boolean acceptedTerms;

    @NotEmpty(message = "角色不能为空")
    private List<@NotBlank String> roles; // 嵌套校验

    // 自定义对象校验(AddressDTO需有自己的校验注解)
    @Valid
    private AddressDTO address;

    // Lombok 或手动生成 getter/setter
}

3. 在 Controller 方法中启用校验

import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;

@RestController
@RequestMapping("/users")
public class UserController {

    @PostMapping
    public ResponseEntity<?> createUser(@RequestBody @Valid UserDTO userDTO) {
        // 业务逻辑
        return ResponseEntity.ok("校验通过");
    }

    // 校验路径变量
    @GetMapping("/{id}")
    public ResponseEntity<?> getUser(
        @PathVariable @Min(1) Long id, // 直接校验基本类型
        @RequestParam @NotBlank String token) {
        return ResponseEntity.ok("校验通过");
    }
}

4. 处理校验异常(全局异常处理)

import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.http.HttpStatus;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Map<String, String> handleValidationExceptions(
        MethodArgumentNotValidException ex) {
        
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getAllErrors().forEach(error -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        return errors;
    }
}

5. 自定义校验器(示例:手机号校验)

// 自定义注解
@Target({FIELD})
@Retention(RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
public @interface ValidPhone {
    String message() default "手机号格式不正确";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

// 校验逻辑实现
public class PhoneValidator implements ConstraintValidator<ValidPhone, String> {
    private static final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");

    @Override
    public boolean isValid(String phone, ConstraintValidatorContext context) {
        return phone != null && PHONE_PATTERN.matcher(phone).matches();
    }
}

// 在DTO中使用
public class UserDTO {
    @ValidPhone
    private String phone;
}

6. 分组校验(按场景区分规则)

// 定义分组接口
public interface CreateGroup {}
public interface UpdateGroup {}

// DTO中使用分组
public class UserDTO {
    @Null(groups = CreateGroup.class, message = "创建时ID必须为空")
    @NotNull(groups = UpdateGroup.class, message = "更新时ID不能为空")
    private Long id;
}

// Controller指定分组
@PostMapping("/create")
public ResponseEntity<?> create(@RequestBody @Validated(CreateGroup.class) UserDTO dto) {...}

常用校验注解:

| 注解 | 作用 | | ------------------- | ------------------------------ | | @NotNull | 值不能为 null | | @NotBlank | 字符串不能为空(trim后长度>0) | | @NotEmpty | 集合/数组不能为空 | | @Size(min, max) | 检查字符串/集合大小 | | @Min(value) | 数字最小值 | | @Max(value) | 数字最大值 | | @Email | 邮箱格式校验 | | @Pattern(regexp) | 正则表达式匹配 | | @Past / @Future | 日期过去/未来时间 | | @Valid | 级联校验关联对象 |

注意事项:

  1. 嵌套校验:对嵌套对象使用 @Valid 触发级联校验
  2. 集合校验:使用 List<@Valid YourDTO> list 校验集合元素
  3. 分组校验:通过 @Validated(Group.class) 实现不同场景不同规则
  4. 手动校验:非Controller层可用 Validator 工具
    @Autowired Validator validator;
    Set<ConstraintViolation<Object>> violations = validator.validate(object);
    

提示:Spring Boot 2.3+ 需显式引入 spring-boot-starter-validation,旧版本已自动包含。