Hibernate Validator -對象分組驗證(二)(可能是東半球最全的講解了)

緊接上回分解,沒有看到第一章的童鞋可以點擊此處回放上一章節內容
實體類

@Data
@AgeSalaryType
public class Student {

    private Long id;

    @NotBlank(message = "姓名不能爲空")
    private String name;

    @NotNull
    @Min(value = 5, message = "年齡不能低於5歲")
    private int age;

    @IdentifyFieldValue(enumClass = OrderType.class)
    private String orderType;

    @NotNull
    @Digits(integer = 10, fraction = 2, message = "請保留小數點後2位")
    private BigDecimal salary;

}

業務場景

新增學生信息,只需上面這些校驗即可
更新學生信息,id爲必傳項,需要id屬性上增加註解@NotNull
如果新建一個實體類不免顯得有些雞肋了,groups專門解決此類問題

校驗組能夠讓你在驗證的時候選擇應用哪些約束條件. 這樣在某些情況下( 例如嚮導 ) 就可以對每一步進行校驗的時候, 選取對應這步的那些約束條件進行驗證了. 校驗組是通過可變參數傳遞給validate, validatePropertyvalidateValue的.
如果某個約束條件屬於多個組,那麼各個組在校驗時候的順序是不可預知的. 如果一個約束條件沒有被指明屬於哪個組,那麼它就會被歸類到默認組(javax.validation.groups.Default).

  • 新增分組接口
public interface UpdateGroup {
}
public interface AddGroup {
}
  • 在實體類上爲註解分配組別
@Data
@AgeSalaryType(groups = AddGroup.class)
public class Student {

    @NotNull(message = "id主鍵不能爲空", groups = UpdateGroup.class)
    private Long id;

    @NotBlank(message = "姓名不能爲空", groups = Default.class)
    private String name;

    @NotNull
    @Min(value = 5, message = "年齡不能低於5歲")
    private int age;

    @IdentifyFieldValue(enumClass = OrderType.class)
    private String orderType;

    @NotNull
    @Digits(integer = 10, fraction = 2, message = "請保留小數點後2位")
    private BigDecimal salary;

}

注:Default.class 是默認組別,無需顯示聲明,我只是標註出來給大家看一下例子

  • 測試類
    def testStudent() {
        Student student = new Student();
        student.setAge(20);
        student.setSalary(new BigDecimal(50));

        Set<ConstraintViolation<Student>> result = validator.validate(student, Default.class);
        printfError(result);

        Set<ConstraintViolation<Student>> result2 = validator.validate(student, UpdateGroup.class);
        printfError(result2);

        Set<ConstraintViolation<Student>> result3 = validator.validate(student, AddGroup.class);
        printfError(result3);

        expect:
        true
    }
  • 測試結果
==================
姓名不能爲空
==================
id主鍵不能爲空
==================
當年齡大於18歲時,每月薪水不得低於100元

<T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups);可單獨校驗某個分組,這是單獨分割出來校驗,放在Controller層接口上表示爲:

@PostMapping(value = "all")
public String allTableType(@RequestBody @Validated(Update.class) TableType tableType) {
     return JSONObject.toJSONString(tableTypeService.list());
}

關於此種在接口增加校驗的方式,需要綁定BindingResult獲取錯誤信息,寫一個完整的實例吧

  • 就用上面的實體類,下面是接口
@RequestMapping("/add")
    public Map<String, Object> addStudent(@Validated({Default.class, AddGroup.class}) Student student, BindingResult bindingResult) {
        Map<String, Object> resultMap = new HashMap<>(10);
        resultMap.put("success", true);
        if (bindingResult.hasErrors()) {
            resultMap.put("success", false);
            StringBuilder stringBuilder = new StringBuilder();
            bindingResult.getAllErrors().stream().forEach(it -> stringBuilder.append(it.getDefaultMessage()));
            resultMap.put("message", stringBuilder.toString());
            return resultMap;
        }
        ···
        return resultMap;
    }
  • PostMan 反饋信息
    postMan控制檯信息

接下來再考慮一個問題,不能每個接口都增加BindingResult bindingResult對象吧,很雞肋的事情,可以在全局異常進行捕獲輸出

@ControllerAdvice
public class ValidateExceptionHandle {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    public RestResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        StringBuilder stringBuilder = new StringBuilder("基礎參數驗證失敗:");
        if (ex.getBindingResult().getAllErrors().size() > 0) {
            for (ObjectError allError : ex.getBindingResult().getAllErrors()) {
                stringBuilder.append("[").append(allError.getDefaultMessage()).append("]  ");
            }
        }
        return RestResponse.failedMessage(stringBuilder.toString());
    }

    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    public RestResponse resolveConstraintViolationException(ConstraintViolationException ex) {
        StringBuilder stringBuilder = new StringBuilder("基礎參數驗證失敗:");
        Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
        if (!CollectionUtils.isEmpty(constraintViolations)) {
            for (ConstraintViolation constraintViolation : constraintViolations) {
                stringBuilder.append("[").append(constraintViolation.getMessage()).append("]");
            }
        }
        return RestResponse.failedMessage(stringBuilder.toString());
    }

接下來考慮另外一種場景,上述實體類中,如果需要校驗很多組別,按順序校驗,如果前面的某個組別驗證失敗,就不再校驗後面的組別了

@GroupSequence 定義組別之間校驗的順序

  • 實體類
@Data
@AgeSalaryType(groups = AddGroup.class)
@GroupSequence({UpdateGroup.class, AddGroup.class, Student.class})
public class Student {

    @NotNull(message = "id主鍵不能爲空", groups = UpdateGroup.class)
    private Long id;

    @Size(min = 5, max = 10, message = "姓名長度在5-10")
    private String name;

    @NotNull
    @Min(value = 5, message = "年齡不能低於5歲")
    private int age;

    @IdentifyFieldValue(enumClass = OrderType.class)
    private String orderType;

    @NotNull
    @Digits(integer = 10, fraction = 2, message = "請保留小數點後2位")
    private BigDecimal salary;

}
  • 測試類
def testStudent() {
        Student student = new Student();
        student.setName("你哈")
        student.setAge(20);
        student.setSalary(new BigDecimal(50));

        Set<ConstraintViolation<Student>> result = validator.validate(student);
        printfError(result);

        expect:
        true
    }
  • 測試結果(UpdateGroup只校驗了Id有錯誤就直接返回了)
id主鍵不能爲空

將實體類上組別更換爲@GroupSequence({AddGroup.class, UpdateGroup.class, Student.class})

  • 測試結果(只校驗了AddGroup,就返回結果)
當年齡大於18歲時,每月薪水不得低於100元

@GroupSequenceProvider 根據對象狀態動態重定義默認分組

public class StudentGsProvider implements DefaultGroupSequenceProvider<Student> {
    @Override
    public List<Class<?>> getValidationGroups(Student student) {
        List<Class<?>> defaultGroupSequence = new ArrayList<>();
        defaultGroupSequence.add(Student.class);

        if (student != null && student.getAge() < 18) {
            defaultGroupSequence.add(AddGroup.class);
        }
        return defaultGroupSequence;
    }
}

可以根據對象條件,將分組信息加入到默認的Default分組裏面去,網上有博客用這種方式來解決對象屬性之間依賴的問題,也是可行的
好了,關於Hibernate Validator的知識點,先介紹這麼多,下一節講述如何運用Hibernate Validator實現Excel全表校驗的邏輯


Java is the besat language in the world
最近北京新肺疫情應急響應級別又上升爲二級,希望在帝都的朋友都多多注意,提高防範意識,下播…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章