為什么使用分組?
場景:
比如:
package com.springbootvalidation.entity; import com.springbootvalidation.common.BaseModel; import com.springbootvalidation.common.Group; import lombok.Data; import javax.validation.constraints.NotBlank; /** * Created by Administrator on 2021/9/20. */ @Data public class UserEntity extends BaseModel{ private static final long serialVersionUID = -6967633787950779639L; /* 沒有加分組groups的屬於Default默認分組。 添加分組groups的屬於自定義分組,不屬於默認分組。 validation校驗默認使用默認分組校驗,不會校驗自定義分組。 因此如果需要校驗自定義分組的字段,需要校驗的時候加上自定義分組,如: ValidationUtil.validate(userEntity, Default.class, Group.AddGroup.class); 或者在controller方法體加上@RequestBody @Validated({Default.class, Group.AddGroup.class}) UserEntity userEntity, BindingResult bindingResult */ /** * 用戶名 */ @NotBlank(message = "用戶名不能為空!",groups = Group.AddGroup.class) private String userName; /** * 密碼 */ @NotBlank(message = "密碼不能為空!") private String password; }
在用戶注冊的時候或者登錄的時候,我需要用戶名和密碼都進行非空校驗。但是我在對用戶進行分組的時候,我有一個分組實體,里面有分組名稱和List<UserEntity>。
那么我在保存的時候,我會保存一個group和user的關系表,里面存分組id和userName。
這時候我在保存關系的時候,我只需要對用戶名進行非空校驗,而不需要對密碼進行非空校驗。這時候該怎么辦呢?再定義一個user實體password不加注解校驗?或者單獨程序校驗而不用注解校驗?
其實javax.validation已經為我們考慮好了,不用那么麻煩。只需要對要校驗的字段進行分組即可。
javax.validation分組校驗:
注意:
沒有加分組groups的屬於Default默認分組。
添加分組groups的屬於自定義分組,不屬於默認分組。
validation校驗默認使用默認分組校驗,不會校驗自定義分組。
因此如果需要校驗自定義分組的字段,需要校驗的時候加上分組,如:
ValidationUtil.validate(userEntity, Default.class, Group.AddGroup.class);
或者在controller方法入參加上@RequestBody @Validated({Default.class, Group.AddGroup.class}) UserEntity userEntity, BindingResult bindingResult
不想要校驗某個分組的字段,把某個分組給去掉就好了。
1.pom依賴:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.springbootvalidation</groupId> <artifactId>springboot-validation</artifactId> <version>1.0-SNAPSHOT</version> <properties> <pagehelper.version>5.1.2</pagehelper.version> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.7.RELEASE</version> </parent> <dependencies> <!-- 集成web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 集成lombok 框架 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- Mybatis分頁插件包 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>${pagehelper.version}</version> </dependency> </dependencies> </project>
2.項目結構:

BaseModel:
package com.springbootvalidation.common; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import java.io.Serializable; import java.util.Date; /** * Created by Administrator on 2021/9/20. */ @Data public class BaseModel implements Serializable{ private static final long serialVersionUID = 4559817637980468562L; /** * 創建人 */ private String createUser; /** * 創建時間 */ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date createTime; /** * 修改人 */ private String updateUser; /** * 修改時間 */ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date updateTime; }
BaseResponse:
package com.springbootvalidation.common; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.github.pagehelper.PageInfo; import lombok.Getter; import java.util.HashMap; import java.util.Map; /** * 返回實體 * @className: BaseResponse * @author: admin * @date: 2021/9/20 12:36 */ @Getter @JsonInclude(JsonInclude.Include.NON_NULL) public class BaseResponse { /** * 成功返回值 */ private final static String SUCCESS_CODE = CodeConts.SUCCESS; /** * 失敗默認返回值 */ private final static String FAILED_CODE = CodeConts.FAILURE; /** * 成功回復報文包含的對象 */ @JsonProperty("data") private final Object data; /** * 返回值 */ @JsonProperty("status") private final String status; /** * 返回msg */ @JsonProperty("message") private final String message; /** * 返回msg */ @JsonProperty("pages") private final Integer pages; /** * 返回msg */ @JsonProperty("pageNum") private final Integer pageNum; /** * 返回msg */ @JsonProperty("pageSize") private final Integer pageSize; /** * 其余參數 */ @JsonProperty("params") private final Map<String, Object> params; private BaseResponse(final Object data, final String status, final String message, final Integer pages, final Integer pageNum, final Integer pageSize, final Map<String, Object> params) { this.data = data; this.status = status; this.message = message; this.params = params; this.pages = pages; this.pageNum = pageNum; this.pageSize = pageSize; } /** * 構造成功報文builder * * @return 成功報文builder builder */ public static Builder successCustom() { return successCustom("操作成功!"); } /** * 構造成功報文builder * * @param message the message * @return builder */ public static Builder successCustom(final String message) { return new Builder(SUCCESS_CODE, message); } /** * 請求成功返回狀態碼和提示 * * @param successCode 狀態碼 * @param message 提示信息 * @return builder */ public static Builder successCustom(final String successCode, final String message) { return new Builder(successCode, message); } /** * 構造錯誤返回報文builder * * @param errorCode 錯誤碼 * @param errorMsg 錯誤信息 * @return 錯誤返回報文builder builder */ public static Builder failedCustom(final String errorCode, final String errorMsg) { return new Builder(errorCode, errorMsg); } /** * Failed custom builder. * * @param errorMsg the error msg * @return the builder */ public static Builder failedCustom(final String errorMsg) { return new Builder(FAILED_CODE, errorMsg); } /** * The type Builder. */ public static final class Builder { /** * 返回值 */ private final String status; /** * msg */ private final String message; /** * 返回msg */ private Integer pages; /** * 返回msg */ private Integer pageNum; /** * 返回msg */ private Integer pageSize; /** * 其他參數 */ private final Map<String, Object> params = new HashMap<>(); /** * 任意可json化的對象 */ private Object data; private Builder(final String status, final String message) { this.status = status; this.message = message; } /** * 添加參數信息 * * @param key the key * @param value the value * @return builder */ public Builder addParam(final String key, final String value) { this.params.put(key, value); return this; } /** * 設置result data * * @param data the data * @return data */ public Builder setData(final Object data) { this.data = data; return this; } /** * 設置result 分頁 * * @param pageInfo the page info * @return data */ public Builder setData(final PageInfo pageInfo) { this.data = pageInfo.getList(); //當前頁 this.pageNum = pageInfo.getPageNum(); this.pageSize = pageInfo.getPageSize(); this.pages = pageInfo.getPages(); return this; } /** * build BaseResponse * * @return base response */ public BaseResponse build() { return new BaseResponse(this.data == null ? "" : this.data, this.status, this.message, this.pages, this.pageNum, this.pageSize, this.params); } } }
CodeConts:
package com.springbootvalidation.common; /** * Created by Administrator on 2021/9/20. */ public class CodeConts { public static String SUCCESS = "1000"; public static String FAILURE = "1001"; public static String SYSTEM_EXCEPTION = "4000"; }
Group:
package com.springbootvalidation.common; /** * 驗證分組 * @className: Group * @author: liuyachao * @date: 2021/9/20 12:56 */ public class Group { public interface AddGroup{} }
UserController:
package com.springbootvalidation.controller; import com.springbootvalidation.common.BaseResponse; import com.springbootvalidation.common.CodeConts; import com.springbootvalidation.entity.UserEntity; import com.springbootvalidation.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.BindingResult; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.validation.groups.Default; /** * Created by Administrator on 2021/9/20. */ @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @PostMapping("/addUser") public BaseResponse addUser(@RequestBody @Validated({Default.class}) UserEntity userEntity, BindingResult bindingResult){ try { if(bindingResult.hasErrors()){ return BaseResponse.failedCustom(bindingResult.getFieldError().getDefaultMessage()).build(); } return userService.addUser(userEntity); }catch (Exception e){ e.printStackTrace(); return BaseResponse.failedCustom(CodeConts.SYSTEM_EXCEPTION,"系統異常").build(); } } @PostMapping("/updUser") public BaseResponse updUser(@RequestBody UserEntity userEntity){ try { return userService.updUser(userEntity); }catch (Exception e){ e.printStackTrace(); return BaseResponse.failedCustom(CodeConts.SYSTEM_EXCEPTION,"系統異常").build(); } } }
UserEntity:
package com.springbootvalidation.entity; import com.springbootvalidation.common.BaseModel; import com.springbootvalidation.common.Group; import lombok.Data; import javax.validation.constraints.NotBlank; /** * Created by Administrator on 2021/9/20. */ @Data public class UserEntity extends BaseModel{ private static final long serialVersionUID = -6967633787950779639L; /* 沒有加分組groups的屬於Default默認分組。 添加分組groups的屬於分組,不屬於默認分組。 validation校驗使用默認分組校驗,不會校驗自定義分組。 因此如果需要校驗自定義分組的字段,需要校驗的時候加上分組,如: ValidationUtil.validate(userEntity, Default.class, Group.AddGroup.class); 或者在controller方法體加上@RequestBody @Validated({Default.class, Group.AddGroup.class}) UserEntity userEntity, BindingResult bindingResult */ /** * 用戶名 */ @NotBlank(message = "用戶名不能為空!",groups = Group.AddGroup.class) private String userName; /** * 密碼 */ @NotBlank(message = "密碼不能為空!") private String password; }
UserService:
package com.springbootvalidation.service; import com.springbootvalidation.common.BaseResponse; import com.springbootvalidation.entity.UserEntity; /** * Created by Administrator on 2021/9/20. */ public interface UserService { BaseResponse addUser(UserEntity userEntity); BaseResponse updUser(UserEntity userEntity); }
UserServiceImpl:
package com.springbootvalidation.service.impl; import com.springbootvalidation.common.BaseResponse; import com.springbootvalidation.common.CodeConts; import com.springbootvalidation.common.Group; import com.springbootvalidation.entity.UserEntity; import com.springbootvalidation.service.UserService; import com.springbootvalidation.utils.ValidationUtil; import org.springframework.stereotype.Service; import javax.validation.groups.Default; import java.util.List; /** * Created by Administrator on 2021/9/20. */ @Service public class UserServiceImpl implements UserService{ @Override public BaseResponse addUser(UserEntity userEntity) { // BaseResponse validateResult = validateParam(userEntity); // if(!validateResult.getStatus().equals(CodeConts.SUCCESS)){ // return validateResult; // } System.out.println("插入成功"); return BaseResponse.successCustom().build(); } private BaseResponse validateParam(UserEntity userEntity) { List<String> validateError = ValidationUtil.validate(userEntity, Default.class, Group.AddGroup.class); if (validateError != null && validateError.size() > 0) { // validateError.get(0) return BaseResponse.failedCustom(validateError.get(0)).build(); } return BaseResponse.successCustom().build(); } @Override public BaseResponse updUser(UserEntity userEntity) { System.out.println("更新成功"); return BaseResponse.successCustom().build(); } }
ValidationUtil:
package com.springbootvalidation.utils; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; import javax.xml.bind.ValidationException; import java.util.ArrayList; import java.util.List; import java.util.Set; /** * @description: 校驗工具類 * @author liuyachao * @date 2021/9/18 16:38 */ public class ValidationUtil { private static Validator validator; static { ValidatorFactory vf = Validation.buildDefaultValidatorFactory(); validator = vf.getValidator(); } /** * javax.validation注解校驗 * @throws ValidationException * @throws ValidationException * @Description: 校驗方法 * @param t 將要校驗的對象 * @throws ValidationException * void * @throws */ public static <T> List<String> validate(T t){ return validate(t,null); } public static <T> List<String> validate(T t, Class<?>... groups){ Set<ConstraintViolation<T>> set; if(groups != null && groups.length > 0){ set = validator.validate(t,groups); }else{ set = validator.validate(t); } List<String> validateError = new ArrayList<>(); if(set.size()>0){ for(ConstraintViolation<T> val : set){ validateError.add(val.getMessage()); } } return validateError; } }
App:
package com.springbootvalidation; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * Created by Administrator on 2021/9/20. */ @SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class,args); } }
我們看下在controller的方法中校驗:
@PostMapping("/addUser")
public BaseResponse addUser(@RequestBody @Validated({Default.class}) UserEntity userEntity, BindingResult bindingResult){
try {
if(bindingResult.hasErrors()){
return BaseResponse.failedCustom(bindingResult.getFieldError().getDefaultMessage()).build();
}
return userService.addUser(userEntity);
}catch (Exception e){
e.printStackTrace();
return BaseResponse.failedCustom(CodeConts.SYSTEM_EXCEPTION,"系統異常").build();
}
}

javax.validation默認是Default默認分組。上面@Validated({Default.class})表示默認分組,也可以只用@Validated或者@Valid表示,去掉({Default.class}),這樣效果等同於@Validated({Default.class})表示只是用默認分組校驗。
默認分組校驗不會校驗AddGroup分組的字段。即不會校驗userName字段不能為空。
這樣的話如果其他地方用到此實體,而不想要校驗userName,那么只用默認分組校驗就可以了。因為默認分組校驗只會校驗默認分組的字段(即不加任何分組的字段),不會校驗自定義分組的字段(如:加了groups = Group.AddGroup.class)。
如果注冊或者添加的時候,既要校驗userName,又要校驗password。那么只需要再controller的分組上加上自定義分組的class即可。如下:
@RequestBody @Validated({Default.class, Group.AddGroup.class}) UserEntity userEntity, BindingResult bindingResult
總結:
那些不管在哪兒使用都要校驗的字段,我們用默認分組(不加任何分組)即可。如果有些字段在這里需要校驗,而在其他地方不需要校驗,那么把這些字段加上分組即可,
如果要校驗自定義分組的字段,只需要在校驗的時候加上Group.AddGroup.class即可。
只校驗默認分組的字段:只需要Default.class。
只校驗自定義分組的字段:只需要Group.AddGroup.class。
需要校驗默認分組又需要校驗自定義分組的字段:加上Default.class,Group.AddGroup.class。
分組的作用就是區分有些字段我需要校驗,而有些字段我不需要校驗,那么我校驗的時候選擇要校驗的分組即可,不校驗的字段的分組不選擇。
校驗字段的方式有兩種:
方式1:在controller的方法入參中加上@Validated({Default.class, Group.AddGroup.class})和BindingResult bindingResult。
方式2:用ValidationUtil工具類校驗,ValidationUtil工具類的校驗使得校驗不局限在controller的方法入參中。想在任何時候,任何地方校驗,就在哪里添加工具類的校驗。
方式1:這是controller方法中加的分組校驗方式:
@PostMapping("/addUser")
public BaseResponse addUser(@RequestBody @Validated({Default.class, Group.AddGroup.class}) UserEntity userEntity, BindingResult bindingResult){
try {
if(bindingResult.hasErrors()){
return BaseResponse.failedCustom(bindingResult.getFieldError().getDefaultMessage()).build();
}
return userService.addUser(userEntity);
}catch (Exception e){
e.printStackTrace();
return BaseResponse.failedCustom(CodeConts.SYSTEM_EXCEPTION,"系統異常").build();
}
}
在@Validated({Default.class, Group.AddGroup.class})中自行添加和刪除要校驗的分組。
方式2:使用ValidationUtil工具類校驗(ValidationUtil工具類的代碼已貼在上面),下面是工具類的使用:
BaseResponse validateResult = validateParam(userEntity); if(!validateResult.getStatus().equals(CodeConts.SUCCESS)){ return validateResult; }
private BaseResponse validateParam(UserEntity userEntity) { List<String> validateError = ValidationUtil.validate(userEntity, Default.class, Group.AddGroup.class); if (validateError != null && validateError.size() > 0) { // validateError.get(0) return BaseResponse.failedCustom(validateError.get(0)).build(); } return BaseResponse.successCustom().build(); }
在ValidationUtil.validate(userEntity, Default.class, Group.AddGroup.class)自行添加或刪除需要校驗的分組。
校驗分組方便區分了同一個實體中哪些字段我們想要校驗,哪些字段在另外一些地方又不想要校驗。
我們只需要將實體中的字段分組,然后校驗的時候選擇分組即可。
3.測試結果:
1.只想要校驗Default默認分組(不加任何分組的字段,@NotBlank(message = "密碼不能為空!")):
List<String> validateError = ValidationUtil.validate(userEntity, Default.class);//, Group.AddGroup.class

userName是自定義分組的字段:
@NotBlank(message = "用戶名不能為空!",groups = Group.AddGroup.class)
這個字段不在默認分組中,不會校驗。
password在默認分組中,會校驗:

2.只想要校驗AddGroup的字段(@NotBlank(message = "用戶名不能為空!",groups = Group.AddGroup.class),帶groups的):
List<String> validateError = ValidationUtil.validate(userEntity, Group.AddGroup.class);

userName是自定義分組的字段,為空,進行了非空校驗。
password是默認分組的字段,不是自定義分組的字段,不會校驗:

3.既想要校驗默認分組字段又想要校驗自定義分組字段,那么把要校驗的字段的分組都加上:
List<String> validateError = ValidationUtil.validate(userEntity, Default.class, Group.AddGroup.class);
userName為空:

password為空:

兩個字段都進行了校驗。
不同的分組,校驗的字段不同。這樣實現了對是否需要校驗的字段的區分。即有些地方我需要校驗某些字段,有些地方我不需要校驗那些字段。
