validator中文文档地址和英文地址
https://docs.jboss.org/hibernate/validator/4.2/reference/zh-CN/html/validator-gettingstarted.html
https://docs.jboss.org/hibernate/validator/6.0/reference/en-US/html_single/#preface
@Service注解为什么不能使用在接口上,而是写在实现类上
接口中只有抽象方法、默认方法、静态方法,不包含构造器,不能实例化。而@Service标注这是一个spring管理的bean,需要实例化,相互矛盾,所以只能在实现类上使用@service注解。
@Validated可以加在接口上,实现类就可以不需要
@Validated
public interface DemoService {
void demo();
// 注解只需要加载接口上 // 2 <= value <= 10
void demotwo(@Size(min = 2, max = 10, message = "字段长度必须在2到10之间") String num);
}
自定义hibernate-validator校验
工具类ValidatorUtils
/**
* hibernate-validator校验工具类
* 参考文档:http://docs.jboss.org/hibernate/validator/6.0/reference/en-US/html_single/
*
* @author Mark sunlightcs@gmail.com
* @since 1.0.0
*/
public class ValidatorUtils {
private static ResourceBundleMessageSource getMessageSource() {
ResourceBundleMessageSource bundleMessageSource = new ResourceBundleMessageSource();
bundleMessageSource.setDefaultEncoding("UTF-8");
bundleMessageSource.setBasenames("i18n/validation", "i18n/validation_common");
return bundleMessageSource;
}
/**
* 校验对象
* @param object 待校验对象
* @param groups 待校验的组
* @throws RenException 校验不通过,则报RenException异常
*/
public static void validateEntity(Object object, Class<?>... groups)
throws RenException {
Locale.setDefault(LocaleContextHolder.getLocale());
Validator validator = Validation.byDefaultProvider().configure().messageInterpolator(
new ResourceBundleMessageInterpolator(new MessageSourceResourceBundleLocator(getMessageSource())))
.buildValidatorFactory().getValidator();
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
if (!constraintViolations.isEmpty()) {
ConstraintViolation<Object> constraint = constraintViolations.iterator().next();
throw new RenException(constraint.getMessage());
}
}
}
// 自定义资源绑定指定的properties文件
ResourceBundleMessageSource bundleMessageSource = new ResourceBundleMessageSource();
bundleMessageSource.setDefaultEncoding("UTF-8");
bundleMessageSource.setBasenames("i18n/validation", "i18n/validation_common");
添加约束
/**
* 用户管理
*/
@Data
@ApiModel(value = "用户管理")
public class SysUserDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "id")
@Null(message="{id.null}", groups = AddGroup.class)
@NotNull(message="{id.require}", groups = UpdateGroup.class)
private Long id;
@ApiModelProperty(value = "用户名", required = true)
@NotBlank(message="{sysuser.username.require}", groups = DefaultGroup.class)
private String username;
@ApiModelProperty(value = "密码")
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@NotBlank(message="{sysuser.password.require}", groups = AddGroup.class)
private String password;
@ApiModelProperty(value = "姓名", required = true)
@NotBlank(message="{sysuser.realname.require}", groups = DefaultGroup.class)
private String realName;
@ApiModelProperty(value = "头像")
private String headUrl;
@ApiModelProperty(value = "性别 0:男 1:女 2:保密", required = true)
@Range(min=0, max=2, message = "{sysuser.gender.range}", groups = DefaultGroup.class)
private Integer gender;
@ApiModelProperty(value = "邮箱", required = true)
@NotBlank(message="{sysuser.email.require}", groups = DefaultGroup.class)
@Email(message="{sysuser.email.error}", groups = DefaultGroup.class)
private String email;
@ApiModelProperty(value = "手机号", required = true)
@NotBlank(message="{sysuser.mobile.require}", groups = DefaultGroup.class)
private String mobile;
@ApiModelProperty(value = "部门ID", required = true)
@NotNull(message="{sysuser.deptId.require}", groups = DefaultGroup.class)
private Long deptId;
@ApiModelProperty(value = "超级管理员 0:否 1:是")
@Range(min=0, max=1, message = "{sysuser.superadmin.range}", groups = DefaultGroup.class)
private Integer superAdmin;
@ApiModelProperty(value = "状态 0:停用 1:正常", required = true)
@Range(min=0, max=1, message = "{sysuser.status.range}", groups = DefaultGroup.class)
private Integer status;
@ApiModelProperty(value = "备注")
private String remark;
@ApiModelProperty(value = "创建时间")
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@JsonFormat(pattern = DateUtils.DATE_TIME_PATTERN)
private Date createDate;
@ApiModelProperty(value = "角色ID列表")
private List<Long> roleIdList;
@ApiModelProperty(value = "岗位ID列表")
private List<Long> postIdList;
@ApiModelProperty(value = "部门名称")
private String deptName;
}
校验约束
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
Car car = new Car(null, "DD-AB-123", 4);
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(car);
// validator.validate(car, DefaultGroup.class);
if (!constraintViolations.isEmpty()) {
ConstraintViolation<Object> constraint = constraintViolations.iterator().next();
throw new RenException(constraint.getMessage());
}
// validate()方法会返回一个set的ConstraintViolation的实例的集合, 我们可以通过遍历它来查看有哪些验证错误, 如果一个对象没有校验出问题的话,那么validate() 会返回一个空的set对象
assertEquals(1, constraintViolations.size());
assertEquals("may not be null", constraintViolations.iterator().next().getMessage());
Controller使用
新增和修改都需要校验对象数据使用 ValidatorUtils.validateEntity
校验工具,如果是校验单个值使用AssertUtils.isArrayEmpty
断言工具
@RestController
@RequestMapping("post")
@Api(tags="岗位管理")
public class SysPostController {
@Autowired
private SysPostService sysPostService;
@PostMapping
@ApiOperation("保存")
@LogOperation("保存")
@PreAuthorize("hasAuthority('sys:post:save')")
public Result save(@RequestBody SysPostDTO dto){
//效验数据
ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class);
sysPostService.save(dto);
return new Result();
}
}
@PutMapping
@ApiOperation("修改")
@LogOperation("修改")
@PreAuthorize("hasAuthority('sys:post:update')")
public Result update(@RequestBody SysPostDTO dto){
//效验数据
ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
sysPostService.update(dto);
return new Result();
}
@DeleteMapping
@ApiOperation("删除")
@LogOperation("删除")
@PreAuthorize("hasAuthority('sys:post:delete')")
public Result delete(@RequestBody Long[] ids){
//效验数据
AssertUtils.isArrayEmpty(ids, "id");
sysPostService.delete(ids);
return new Result();
}
添加约束的常见取舍
@NotNull
@NotNull 常用在数据类型为Long,Integer的基础数据类型上,可以配合@Size、@Max、@Min对数值进行大小的控制
主键和数字校验
@ApiModelProperty(value = "编号")
@NotNull(message = "编号不能为空")
@JsonFormat(shape = Shape.STRING)
private Long id;
@ApiModelProperty(value = "价格")
@NotNull(message = "价格不能为空")
@Min(value = 100, message = "价格不能低于100")
@Max(value = 5000, message = "价格不能超过5000")
private Integer price;
@ApiModelProperty(value = "数量")
@NotNull(message = "数量不能为空")
@Size(min = 10, max = 50, message = "数量必须不低于10不超过50")
private Integer num;
@NotEmpty 数组集合校验
@NotEmpty 常用在集合类和数组类型上,也可以配合@Size进行大小的控制。
@ApiModelProperty(value = "材料保存列表", required = true)
@NotEmpty(message = "存货清单列表不能为空")
@Size(min = 1, message = "至少需要一条数据")
private List<MaterialAcceptanceItemsSaveVo> itemsSaveList;
@NotBlank 字符串校验
@NotBlank 只用于String数据类型上,可以和@Len配合使用限制字符长度。
@ApiModelProperty(value = "供应商", required = true)
@NotBlank(message = "供应商不能为空")
@Len(min = 1, max = 500)
private String provider;
@ApiModelProperty(value = "发票号", required = true)
@Len(min = 0, max = 250)
private String invoiceNo;
BigDecimal数据类型校验
Long数据类型防止精度丢失,添加注解:@JsonFormat(shape = Shape.STRING)
BigDecimal数据类型的限制注解使用:@DecimalMin、@DecimalMax、@Digits
@ApiModelProperty(value = "报账总金额")
@DecimalMin(value = "0", inclusive = false, message = "报账总金额必须大于0")
@Digits(integer = 10, fraction = 2, message = "报账总金额 最大长度:10,允许精度:2")
private BigDecimal financeMoney;
status值校验
// @Pattern(regexp = "^[0,1]{1}$",message = "帐号启用状态:0->禁用;1->启用")
@Range(min=0, max=1,message = "帐号启用状态:0->禁用;1->启用")
@NotNull(message = "帐号启用状态不能为空")
@ApiModelProperty(value = "帐号启用状态:0->禁用;1->启用")
private Integer status;
@正则匹配
@ApiModelProperty(value = "是否有符号位 Y:有 N:无")
@Pattern(regexp = "^[YN]{1}$", message = "是否有符号位 Y:有 N:无", groups = DefaultGroup.class)
private String sign;
@Null 被注解的元素必须为 null
@NotNull 被注解的元素必须不为 null
@AssertTrue 被注解的元素必须为 true
@AssertFalse 被注解的元素必须为 false
@Min(value) 被注解的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注解的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注解的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注解的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min) 被注解的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注解的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注解的元素必须是一个过去的日期
@Future 被注解的元素必须是一个将来的日期
@Pattern(value) 被注解的元素必须符合指定的正则表达式
Hibernate Validator 扩展注解
@Email 被注解的元素必须是电子邮箱地址
@Length 被注解的字符串的大小必须在指定的范围内
@NotEmpty 被注解的字符串的必须非空
@Range 被注解的元素必须在合适的范围内
javax.validation 和 Hibernate-Validator区别
参考: https://www.ngui.cc/el/1183725.html?action=onClick
javax.validation
自定校验
@Data
@ApiModel(value = "测量参数的构型")
public class MeasureQarTypeDTO {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "构型名称")
private String qarType;
private Integer qarTypeId;
@ApiModelProperty(value = "基础参数路径可以有型号")
private List<String> paths;
private List<Integer> ids;
}
import javax.validation.constraints.NotNull;
import java.util.List;
@Data
@ApiModel(value = "测量参数的构型")
public class MeasureQarTypeDTO {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "构型名称")
@NotNull(message = "qarType cannot be null") // 非空校验
private String qarType;
private Integer qarTypeId;
@ApiModelProperty(value = "基础参数路径可以有型号")
@NotNull(message = "paths cannot be null") // 非空校验
private List<String> paths; // 注意:这里仅校验paths本身不能为null,如果需要校验paths中的元素也不能为null,则需要自定义校验注解或方法
private List<Integer> ids;
}
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Documented
@Constraint(validatedBy = NotEmptyListValidator.class)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NotEmptyList {
String message() default "{NotEmptyList.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface List {
NotEmptyList[] value();
}
}
class NotEmptyListValidator implements ConstraintValidator<NotEmptyList, List<?>> {
@Override
public boolean isValid(List<?> list, ConstraintValidatorContext context) {
if (list == null) return true; // 或者根据需求处理null情况
for (Object element : list) {
if (element == null || "".equals(element)) {
return false;
}
}
return true;
}
}
@ApiModelProperty(value = "基础参数路径可以有型号")
@NotEmptyList(message = "Each path in paths cannot be empty")
private List<String> paths;