参数校验器和整合Mybatis&MybatisPlus

您所在的位置:网站首页 肯德基添加地址请求数据不合法 参数校验器和整合Mybatis&MybatisPlus

参数校验器和整合Mybatis&MybatisPlus

2024-07-13 06:39| 来源: 网络整理| 查看: 265

参数校验器和整合Mybatis&MybatisPlus

在日常的开发中,服务端对象的校验是非常重要的一个环节,比如:注册的时候:校验用户名,密码,身份证,邮箱等信息是否为空,以及格式是否正确,但是这种在日常的开发中进行校验太繁琐了,代码繁琐而且很多。Validator框架应运而生,它的出现就是为了解决开发人员在开发的时候减少代码的,提升开发效率。它专门用来做接口的参数校验,比如:密码长度、是否为空等等。

1.1SpringBoot的validator校验框架支持

​ JSR303特征:JSR303是一项标准,只提供规范不提供实现。规定一些校验规范即校验注解。比如:@Null、@NotNull、@Pattern。这些类都位于:javax.validation.constraints包下。

hibernate validation特征:hibernate validation是对JSR303规范的实现并且进行了增强和扩展。并增加了注解:@Email、@Length、@Range等等。

Spring Validation:Spring Validation是对Hibernate Validation的二次封装。在SpringMvc模块中添加了自动校验器。并将校验信息封装到特定的类中。

1.2常用的注解 @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(regex=,flag=) 被注释的元素必须符合指定的正则表达式 Hibernate Validator提供的校验注解: @NotBlank(message =) 验证字符串非null,且trim后长度必须大于0 @Email 被注释的元素必须是电子邮箱地址 @Length(min=,max=) 被注释的字符串的大小必须在指定的范围内 @NotEmpty 被注释的字符串的必须非空 @Range(min=,max=,message=) 被注释的元素必须在合适的范围内 2.Spring整合validator

​ 1.添加依赖

org.springframework.boot spring-boot-starter-validation 2.新建用户实体并结合验证注解 @Data public class UserVo { @NotNull(message = "用户id不能为空") private Long userId; @NotBlank(message = "用户名不能为空") @Length(max = 20, message = "用户名不能超过20个字符") @Pattern(regexp = "^[\\u4E00-\\u9FA5A-Za-z0-9\\*]*$", message = "用户昵称限制:最多20字符,包含文字、字母和数字") private String username; @NotBlank(message = "手机号不能为空") @Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误") private String mobile; @NotBlank(message = "联系邮箱不能为空") @Email(message = "邮箱格式不对") private String email; @Future(message = "时间必须是将来时间") private Date createTime; } 3.添加UserValidationController进行校验 @RestController @Api(description = "用户校验") @RequestMapping("/user") public class UserValiatorController { @PostMapping("/valiator/reg") public UserVo createUser(@RequestBody @Validated UserVo userVo) { return userVo; } } 返回结果

xxxxxxxxxx { "code": 500, "data": null, "message": "服务器忙,请稍后在试" }

**问题来了:**不想让控制台显示这种异常,想要显示我们自己配置好的异常 我们现在要做的事情就是: 1、怎么把验证的异常进行捕获? 2、怎么把验证信息进行提取?

/** * 对验证的统一异常进行统一处理 */ @ExceptionHandler(MethodArgumentNotValidException.class) public ErrorHandler handlerValiator(MethodArgumentNotValidException e, HttpServletRequest request) throws JsonProcessingException { // 1: 从MethodArgumentNotValidException提取验证失败的所有的信息。返回一个List List fieldErrors = e.getBindingResult().getFieldErrors(); // 2: 把fieldErrors中,需要的部分提出出来进行返回 List mapList = toValidatorMsg(fieldErrors); // 3: 把需要的异常信息转换成json进行返回 ObjectMapper objectMapper = new ObjectMapper(); String mapJson = objectMapper.writeValueAsString(mapList); ErrorHandler errorHandler = ErrorHandler.fail(ResultCodeEnum.PARAM_ERROR, e, mapJson); return errorHandler; } /** * 对验证异常进行统一处理提取需要的部分 * * @param fieldErrorList * @return */ private List toValidatorMsg(List fieldErrorList) { List mapList = new ArrayList(); // 循环提取 for (FieldError fieldError : fieldErrorList) { Map map = new HashMap(); // 获取验证失败的属性 map.put("field", fieldError.getField()); // 获取验证失败的的提示信息 map.put("msg", fieldError.getDefaultMessage()); mapList.add(map); } return mapList; } 3.自定义校验器

​ 1.首先定义验证异常注解

@Documented // 这里是核心:告诉当前的注解最后会选中上面类进行去校验你的规则 @Constraint(validatedBy = PhoneValidator.class) // 这个注解在那些范围上生效,在方法和属性上是可以去定义 @Target({ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Phone { // 默认提示 String message() default "xxx手机格式不正确!"; Class[] groups() default {}; Class[] payload() default {}; @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface List { Phone[] value(); } }

说明:参照@Email的格式,只需要写String message() default “xxx手机格式不正确!”;这一行即可,其他的都是固定格式,CV即可。

2.定义校验器 public class PhoneValidator implements ConstraintValidator { @Override public boolean isValid(String phonevalue, ConstraintValidatorContext constraintValidatorContext) { // 1: 如果用户没输入直接返回不校验,因为空的判断应该交给@NotNull去做就行了 if (StringUtils.isEmpty(phonevalue)) { return true; } // 2: 如果填写手机号码就进行正则校验 Pattern p = Pattern.compile("^(13[0-9]|14[5|7|9]|15[0|1|2|3|5|6|7|8|9]|17[0|1|6|7|8]|18[0-9])\\d{8}$"); // 2:如果校验通过就返回true,否则返回false; Matcher matcher = p.matcher(phonevalue); return matcher.matches(); //return ValidateUtil.validateMobile(phonevalue); } @Override public void initialize(Phone constraintAnnotation) { } } 3.在对应实体的属性上进行校验即可 @Data public class UserVo { @NotBlank(message = "请输入phone") @Phone private String phone; }

说明: // 1: 如果用户没输入直接返回不校验,因为空的判断应该交给@NotNull去做就行了 if (StringUtils.isEmpty(phonevalue)) { return true; }

这个地方返回true,是为了确保校验器的独立性,在phone输入和不输入的情况下都能够校验。而验证是否时空交给@NotNull。

4.其他常用的校验规则

自定义其他校验器时,只需要把校验规则更改即可。

/** * 常用的一些验证,如手机、移动号码、联通号码、电信号码、密码、座机、 邮政编码、邮箱、年龄、身份证、URL、QQ、汉字、字母、数字等 */ public class ValidateUtil { /** * 手机号规则 */ public static final String MOBILE_PATTERN = "^((13[0-9])|(14[0-9])|(15[0-9])|(17[0-9])|(18[0-9]))(\\d{8})$"; /** * 中国电信号码格式验证 手机段: 133,153,180,181,189,177,1700,173 **/ private static final String CHINA_TELECOM_PATTERN = "(?:^(?:\\+86)?1(?:33|53|7[37]|8[019])\\d{8}$)|(?:^(?:\\+86)?1700\\d{7}$)"; /** * 中国联通号码格式验证 手机段:130,131,132,155,156,185,186,145,176,1707,1708,1709,175 **/ private static final String CHINA_UNICOM_PATTERN = "(?:^(?:\\+86)?1(?:3[0-2]|4[5]|5[56]|7[56]|8[56])\\d{8}$)|(?:^(?:\\+86)?170[7-9]\\d{7}$)"; /** * 中国移动号码格式验证 手机段:134,135,136,137,138,139,150,151,152,157,158,159,182,183,184,187,188,147,178,1705 **/ private static final String CHINA_MOVE_PATTERN = "(?:^(?:\\+86)?1(?:3[4-9]|4[7]|5[0-27-9]|7[8]|8[2-478])\\d{8}$)|(?:^(?:\\+86)?1705\\d{7}$)"; /** * 密码规则(6-16位字母、数字) */ public static final String PASSWORD_PATTERN = "^[0-9A-Za-z]{6,16}$"; /** * 固号(座机)规则 */ public static final String LANDLINE_PATTERN = "^(?:\\(\\d{3,4}\\)|\\d{3,4}-)?\\d{7,8}(?:-\\d{1,4})?$"; /** * 邮政编码规则 */ public static final String POSTCODE_PATTERN = "[1-9]\\d{5}"; /** * 邮箱规则 */ public static final String EMAIL_PATTERN = "^([a-z0-9A-Z]+[-|_|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"; /** * 年龄规则 1-120之间 */ public static final String AGE_PATTERN = "^(?:[1-9][0-9]?|1[01][0-9]|120)$"; /** * 身份证规则 */ public static final String IDCARD_PATTERN = "^\\d{15}|\\d{18}$"; /** * URL规则,http、www、ftp */ public static final String URL_PATTERN = "http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w- ./?%&=]*)?"; /** * QQ规则 */ public static final String QQ_PATTERN = "^[1-9][0-9]{4,13}$"; /** * 全汉字规则 */ public static final String CHINESE_PATTERN = "^[\u4E00-\u9FA5]+$"; /** * 全字母规则 */ public static final String STR_ENG_PATTERN = "^[A-Za-z]+$"; /** * 整数规则 */ public static final String INTEGER_PATTERN = "^-?[0-9]+$"; /** * 正整数规则 */ public static final String POSITIVE_INTEGER_PATTERN = "^\\+?[1-9][0-9]*$"; /** * @param mobile 手机号码 * @return boolean * @Description: 验证手机号码格式 */ public static boolean validateMobile(String mobile) { if (StringUtils.isEmpty(mobile)) { return Boolean.FALSE; } return mobile.matches(MOBILE_PATTERN); } /** * 验证是否是电信手机号,133、153、180、189、177 * * @param mobile 手机号 * @return boolean */ public static boolean validateTelecom(String mobile) { if (StringUtils.isEmpty(mobile)) { return Boolean.FALSE; } return mobile.matches(CHINA_TELECOM_PATTERN); } /** * 验证是否是联通手机号 130,131,132,155,156,185,186,145,176,1707,1708,1709,175 * * @param mobile 电话号码 * @return boolean */ public static boolean validateUnionMobile(String mobile) { if (StringUtils.isEmpty(mobile)) { return Boolean.FALSE; } return mobile.matches(CHINA_UNICOM_PATTERN); } /** * 验证是否是移动手机号 * * @param mobile 手机号 134,135,136,137,138,139,150,151,152,157,158,159,182,183,184,187,188,147,178,1705 * @return boolean */ public static boolean validateMoveMobile(String mobile) { if (StringUtils.isEmpty(mobile)) { return Boolean.FALSE; } return mobile.matches(CHINA_MOVE_PATTERN); } /** * @param pwd 密码 * @return boolean * @Description: 验证密码格式 6-16 位字母、数字 */ public static boolean validatePwd(String pwd) { if (StringUtils.isEmpty(pwd)) { return Boolean.FALSE; } return Pattern.matches(PASSWORD_PATTERN, pwd); } /** * 验证座机号码,格式如:58654567,023-58654567 * * @param landline 固话、座机 * @return boolean */ public static boolean validateLandLine(final String landline) { if (StringUtils.isEmpty(landline)) { return Boolean.FALSE; } return landline.matches(LANDLINE_PATTERN); } /** * 验证邮政编码 * * @param postCode 邮政编码 * @return boolean */ public static boolean validatePostCode(final String postCode) { if (StringUtils.isEmpty(postCode)) { return Boolean.FALSE; } return postCode.matches(POSTCODE_PATTERN); } /** * 验证邮箱(电子邮件) * * @param email 邮箱(电子邮件) * @return boolean */ public static boolean validateEamil(final String email) { if (StringUtils.isEmpty(email)) { return Boolean.FALSE; } return email.matches(EMAIL_PATTERN); } /** * 判断年龄,1-120之间 * * @param age 年龄 * @return boolean */ public static boolean validateAge(final String age) { if (StringUtils.isEmpty(age)) { return Boolean.FALSE; } return age.matches(AGE_PATTERN); } /** * 身份证验证 * * @param idCard 身份证 * @return boolean */ public static boolean validateIDCard(final String idCard) { if (StringUtils.isEmpty(idCard)) { return Boolean.FALSE; } return idCard.matches(IDCARD_PATTERN); } /** * URL地址验证 * * @param url URL地址 * @return boolean */ public static boolean validateUrl(final String url) { if (StringUtils.isEmpty(url)) { return Boolean.FALSE; } return url.matches(URL_PATTERN); } /** * 验证QQ号 * * @param qq QQ号 * @return boolean */ public static boolean validateQq(final String qq) { if (StringUtils.isEmpty(qq)) { return Boolean.FALSE; } return qq.matches(QQ_PATTERN); } /** * 验证字符串是否全是汉字 * * @param str 字符串 * @return boolean */ public static boolean validateChinese(final String str) { if (StringUtils.isEmpty(str)) { return Boolean.FALSE; } return str.matches(CHINESE_PATTERN); } /** * 判断字符串是否全字母 * * @param str 字符串 * @return boolean */ public static boolean validateStrEnglish(final String str) { if (StringUtils.isEmpty(str)) { return Boolean.FALSE; } return str.matches(STR_ENG_PATTERN); } /** * 判断是否是整数,包括负数 * * @param str 字符串 * @return boolean */ public static boolean validateInteger(final String str) { if (StringUtils.isEmpty(str)) { return Boolean.FALSE; } return str.matches(INTEGER_PATTERN); } /** * 判断是否是大于0的正整数 * * @param str 字符串 * @return boolean */ public static boolean validatePositiveInt(final String str) { if (StringUtils.isEmpty(str)) { return Boolean.FALSE; } return str.matches(POSITIVE_INTEGER_PATTERN); } } 5.小结

对于上述的问题,仍然存在缺陷,只能验证实体相关的校验。但是对于下面的可能就不能进行验证处理

@PostMapping("/valiator/reg2") public UserVo createUser2(String name, Integer flag) { return null; } 4.SpringBoot-Assert和自定义KAssert

Assert它是Spring提供的一个工具类。Web 应用在接受表单提交的数据后都需要对其进行合法性检查,如果表单数据不合法,请求将被驳回。类似的,当我们在编写类的方法时,也常常需要对方法入参进行合法性检查,如果入参不符合要求,方法将通过抛出异常的方式拒绝后续处理。 例如:

@PostMapping("/valiator/reg2") public UserVo createUser2(String name,Integer flag) { Assert.isNull(name,"用户名不允许为空!");// 但是这个不明确不利于扩展 return null; }

结果:

{ "code": 500, "data": null, "message": "服务器忙,请稍后在试" }

缺点:返回的结果不够明确。

4.2自定义KAssert参数校验

步骤:

1.自定义KAssert public class KAssert { public static void isEmpty(Object object,Integer code,String message) { if (object == null || "".equals(object)) { throw new ValidationException(code,message); } } public static void isEmpty(Object object, ResultCodeEnum resultCodeEnum) { if (object == null || "".equals(object)) { throw new ValidationException(resultCodeEnum.getCode(),resultCodeEnum.getMessage()); } } } 2.自定义KAssert对应的异常 @Data public class ValidationException extends RuntimeException { private Integer code; private String message; public ValidationException(ResultCodeEnum resultCodeEnum) { this.code = resultCodeEnum.getCode(); this.message = resultCodeEnum.getMessage(); } public ValidationException(Integer code, String message) { this.code = code; this.message = message; } }

3.全局异常统一处理

@RestControllerAdvice @Slf4j public class GlobalExceptionHandler { /** * 对自定义异常的统一处理 * 缺点:明确异常信息 */ @ExceptionHandler(ValidationException.class) public ErrorHandler makevalidationException(ValidationException validationException, HttpServletRequest request) { ErrorHandler errorHandler = ErrorHandler.builder() .message(validationException.getMessage()) .status(validationException.getCode()) .build(); log.error("请求的地址是:{},出现的异常是:{}", request.getRequestURL(), validationException); return errorHandler; } } 4.测试结果 { "code": 401, "data": null, "message": "请输入用户名!" } 5.整合Mybatis&MybatisPlus 5.1整合mybatis 1.引入mybatis-plus依赖 mysql mysql-connector-java 8.0.17 com.baomidou mybatis-plus-boot-starter 3.4.0

如果不指定数据源,就会报以下这个错误

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bNixCYQi-1625019865412)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210630091401025.png)]

2.在application.yml进行配置数据源 # 环境隔离 spring: profiles: active: dev server: compression: # 请求gzip压缩 enabled: true mime-types: text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json,application/xml min-response-size: 1024 application: name: edu-front-web # 模板视图 thymeleaf thymeleaf: cache: false prefix: classpath:/templates/ mode: HTML encoding: UTF-8 # json的转换配置 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 locale: zh_CN generator: write-numbers-as-strings: true write-bigdecimal-as-plain: true ##mybatis的原生态支持 mybatis-plus: mapper-locations: classpath*:/mapper/*.xml type-aliases-package: com.kuangstudy.entity logging: level: root: debug

applicaiton-dev.yml:

# 数据源的配置 spring: datasource: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/kuangstudydb?serverTimezone=GMT%2b8&useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: mkxiaoer1986 hikari: connection-timeout: 60000 validation-timeout: 3000 idle-timeout: 60000 login-timeout: 5 max-lifetime: 60000 maximum-pool-size: 30 minimum-idle: 10 read-only: false 3.新建一个数据库和表 SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for kss_user -- ---------------------------- DROP TABLE IF EXISTS `kss_user`; CREATE TABLE `kss_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `nickname` varchar(100) DEFAULT NULL COMMENT '用户名', `password` varchar(100) DEFAULT NULL COMMENT '密码', `age` int(3) DEFAULT NULL COMMENT '年龄', `male` int(1) DEFAULT NULL COMMENT '性别 0 女 1 男 2保密', `user_intro` varchar(1000) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 4.创建实体映射 @Data @NoArgsConstructor @AllArgsConstructor @ToString @TableName("kss_user") public class User { // 用户编号 @TableId(type = IdType.AUTO) private Integer id; // 用户昵称 private String nickname; // 密码 private String password; // 年龄 private Integer age; // 性别 0 女 1 男 2保密 private Integer male; // 用户介绍 private String userIntro; } 5.新建一个mapper public interface UserMapper extends BaseMapper { } 6.使mybatis和spring融合(重点)

在启动类上增加@MapperScan("com.kuangstudy.mapper")

@SpringBootApplication @MapperScan("com.kuangstudy.mapper") public class KuangstudyBootsMybatisApplication { public static void main(String[] args) { SpringApplication.run(KuangstudyBootsMybatisApplication.class, args); } } 7.定义一个用户Service接口和实现类 @Service public class UserServiceImpl extends ServiceImpl implements UserService{ } 8.测试 @SpringBootTest class KuangstudyBootsMybatisApplicationTests { @Autowired private UserService userService; @Test public void saveUser(){ User user = new User(); user.setId(1); user.setNickname("yykkxxxx"); user.setPassword("1121455"); user.setAge(34); user.setMale(1); user.setUserIntro("我是一个xxxx"); userService.saveOrUpdate(user); } @Test public void getuserById(){ User user = userService.getByUserId(1); System.out.println(user); } }

说明: 上述方式主要利用mybatis-plus的方式,利用其中的方法,省去了写简单SQL语句的过程,但是如果遇到多表查询,还是要按照传统方式写SQL语句。

6.基于xml的原生态的mybatis的支持

在application.yml增加支持xml的方式

##mybatis的原生态支持 mybatis-plus: mapper-locations: classpath*:/mapper/*.xml type-aliases-package: com.kuangstudy.entity 1.在UserMapper中定义方法 public interface UserMapper extends BaseMapper { User getByUserId(@Param("id") Integer id); } 2.新建一个mapper.xml文件 SELECT id,nickname,password,age,male,user_intro as userIntro FROM kss_user WHERE id = #{id} 3.UserService接口新建一个自定义的方法 public interface UserService extends IService { /** * @Author xuke * @Description 根据用户id查询用户信息 * @Date 22:18 2021/6/26 * @Param [id] * @return com.kuangstudy.entity.User **/ User getByUserId(Integer id); } @Service public class UserServiceImpl extends ServiceImpl implements UserService{ /** * @Author xuke * @Description 根据用户id查询用户信息 * @Date 22:18 2021/6/26 * @Param [id] * @return com.kuangstudy.entity.User **/ public User getByUserId(Integer id){ return this.baseMapper.getByUserId(id); } } 4.测试 @SpringBootTest class KuangstudyBootsMybatisApplicationTests { @Autowired private UserService userService; @Test public void saveUser(){ User user = new User(); user.setId(1); user.setNickname("yykkxxxx"); user.setPassword("1121455"); user.setAge(34); user.setMale(1); user.setUserIntro("我是一个xxxx"); userService.saveOrUpdate(user); } @Test public void getuserById(){ User user = userService.getByUserId(1); System.out.println(user); } } * @Author xuke * @Description 根据用户id查询用户信息 * @Date 22:18 2021/6/26 * @Param [id] * @return com.kuangstudy.entity.User **/ public User getByUserId(Integer id){ return this.baseMapper.getByUserId(id); }

}

#### 4.测试 ```java @SpringBootTest class KuangstudyBootsMybatisApplicationTests { @Autowired private UserService userService; @Test public void saveUser(){ User user = new User(); user.setId(1); user.setNickname("yykkxxxx"); user.setPassword("1121455"); user.setAge(34); user.setMale(1); user.setUserIntro("我是一个xxxx"); userService.saveOrUpdate(user); } @Test public void getuserById(){ User user = userService.getByUserId(1); System.out.println(user); } }

说明:mybatis-plus即支持注解+反射的实现方式,也支持原生基于xml的方式。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3