好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

SpringBoot+Hibernate实现自定义数据验证及异常处理

前言

在进行 SpringBoot 项目开发中,经常会碰到属性合法性问题,而面对这个问题通常的解决办法就是通过大量的 if 和 else 判断来解决的,例如:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

@PostMapping ( "/verify" )

     @ResponseBody

     public Object verify( @Valid User user){

         if (StringUtils.isEmpty(user.getName())){

             return "姓名不能为空" ;

         }

         if (StringUtils.isEmpty(user.getAge())){

             return "姓名不能为空" ;

         }

         if (!StringUtils.isEmpty(user.getSex())&&user.getSex().equals( "男" )&&user.getSex().equals( "女" )){

             return "性别有误" ;

         }

         return user;

     }

这种代码写法十分麻烦,试想一下如果你有10个、20个字段属性,你也要跟着写十几二十几个 if 和 else 判断?

So,本文讲解一下使用Hibernate框架来去验证字段属性,使用相应的注解即可实现字段合法性校验,以及如何自定义注解进行校验,包括出现异常的几种处理方式。

Hibernate实现字段校验

Maven依赖

?

1

2

3

4

< dependency >

     < groupId >org.springframework.boot</ groupId >

      < artifactId >spring-boot-starter-validation</ artifactId >

</ dependency >

常用的校验注解

注解 释义
@Null 必须为null
@NotNull 不能为null
@AssertTrue 必须为true
@AssertFalse 必须为false
@Min 必须为数字,其值大于或等于指定的最小值
@Max 必须为数字,其值小于或等于指定的最大值
@DecimalMin 必须为数字,其值大于或等于指定的最小值
@DecimalMax 必须为数字,其值小于或等于指定的最大值
@Size 集合的长度
@Digits 必须为数字,其值必须再可接受的范围内
@Past 必须是过去的日期
@Future 必须是将来的日期
@Pattern 必须符合正则表达式
@Email 必须是邮箱格式
@Length 长度范围
@NotEmpty 不能为null,长度大于0
@Range 元素的大小范围
@NotBlank 不能为null,字符串长度大于0(限字符串)

定义User实体类

?

1

2

3

4

5

6

7

@Data

public class User {

     @NotBlank (message = "姓名不能为空" )

     private String name;

     @NotBlank (message = "年龄不能为空" )

     private String age;

}

定义UserController

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

@Controller

public class UserController {

 

     @PostMapping ( "/verify" )

     @ResponseBody

     public Object verify( @Valid User user, BindingResult result){

         //字段校验有错误

         if (result.hasErrors()){

             //获取错误字段信息

             List<FieldError> fieldErrors = result.getFieldErrors();

             if (fieldErrors!= null ){

                 //创建一个map用来封装字段错误信息

                 HashMap<String, String> map = new HashMap<>();

                 //遍历错误字段

                 fieldErrors.forEach(x->{

                     //获取字段名称

                     String field = x.getField();

                     //获取字段错误提示信息

                     String msg = x.getDefaultMessage();

                     //存入map

                     map.put(field, msg);

                 });

                 return map;

             }

         }

         return user;

     }

 

}

启动项目进行测试

可以看到name和age的错误信息已经封装好传回来了

自定义校验注解

自定义一个校验性别的注解Sex

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

/**

  * 性别约束

  * @Target用于指定使用范围,该处限定只能在字段上使用

  * @Retention(RetentionPolicy.RUNTIME)表示注解在运行时可以通过反射获取到

  * @Constraint(validatedBy = xxx.class)指定该注解校验逻辑

  */

@Target ({ ElementType.FIELD})

@Retention (RetentionPolicy.RUNTIME)

@Constraint (validatedBy = SexConstraintValidator. class )

public @interface Sex {

 

     String message() default "性别有误" ;

 

     Class<?>[] groups() default { };

 

     Class<? extends Payload>[] payload() default { };

}

创建SexConstraintValidator校验逻辑类

?

1

2

3

4

5

6

7

8

9

10

11

/**

  * 性别约束逻辑判断

  */

public class SexConstraintValidator implements ConstraintValidator<Sex, String> {

 

     @Override

     public boolean isValid(String value, ConstraintValidatorContext context) {

         return value != null && (value.equals( "男" ) || value.equals( "女" ));

     }

 

}

修改User实体类

?

1

2

3

4

5

6

7

8

9

@Data

public class User {

     @NotBlank (message = "姓名不能为空" )

     private String name;

     @NotBlank (message = "年龄不能为空" )

     private String age;

     @Sex (message = "性别不能为空或有误" )

     private String sex;

}

重启项目测试效果

使用AOP处理校验异常

Maven依赖

?

1

2

3

4

< dependency >

     < groupId >org.springframework.boot</ groupId >

     < artifactId >spring-boot-starter-aop</ artifactId >

</ dependency >

这里我们用注解作为AOP的切入点,新建一个注解 BindingResultAnnotation

?

1

2

3

4

5

@Target ({ElementType.METHOD})

@Retention (RetentionPolicy.RUNTIME)

@Documented

public @interface BindingResultAnnotation {

}

定义参数校验切面类

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

/**

  * 参数校验切面类

  */

@Aspect

@Component

public class BindingResultAspect {

 

     /**

      * 校验切入点

      */

     @Pointcut ( "@annotation(com.hsqyz.hibernate.config.aop.BindingResultAnnotation)" )

     public void BindingResult() {

     }

 

     /**

      * 环绕通知

      * @param joinPoint

      * @return

      * @throws Throwable

      */

     @Around ( "BindingResult()" )

     public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {

         System.out.println( "参数校验切面..." );

         Object[] args = joinPoint.getArgs();

         for (Object arg : args) {

             if (arg instanceof BindingResult) {

                 BindingResult result = (BindingResult) arg;

                 if (result.hasErrors()){

                     List<FieldError> fieldErrors = result.getFieldErrors();

                     if (fieldErrors!= null ){

                         HashMap<String, String> map = new HashMap<>();

                         fieldErrors.forEach(x->{

                             String field = x.getField();

                             String msg = x.getDefaultMessage();

                             map.put(field, msg);

                         });

                         return map;

                     }

                 }

 

             }

         }

         return joinPoint.proceed();

     }

 

 

}

修改UserController

注意:这里将新建的切面注解添加到方法上@BindingResultAnnotation,必须携带BindingResult result在参数后面,否则AOP无法获取错误信息,导致AOP无法处理异常。

?

1

2

3

4

5

6

7

8

9

10

11

@Controller

public class UserController {

 

     @PostMapping ( "/verify" )

     @ResponseBody

     @BindingResultAnnotation

     public Object verify( @Valid User user, BindingResult result) {

         return user;

     }

 

}

重启项目查看效果

全局异常类处理异常

创建GlobelExceptionHandler来处理全局异常,使用@ExceptionHandle来拦截指定异常,由于参数校验抛出的异常是BindException,所以我们需要拦截BindException异常,而BindException内部封装这错误信息,这样就可以用全局异常处理类来封装字段错误信息返回。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

/**

  * 全局异常处理

  */

@ControllerAdvice

public class GlobelExceptionHandler {

 

     /**

      * 参数验证异常处理

      * @param result

      * @return

      */

     @ResponseBody

     @ExceptionHandler (BindException. class )

     public Object bindExceptionHandler(BindingResult result) {

         System.out.println( "参数验证异常处理..." );

         if (result.hasErrors()){

             List<FieldError> fieldErrors = result.getFieldErrors();

             if (fieldErrors!= null ){

                 HashMap<String, String> map = new HashMap<>();

                 fieldErrors.forEach(x->{

                     String field = x.getField();

                     String msg = x.getDefaultMessage();

                     map.put(field, msg);

                 });

                 return map;

             }

         }

         return result.getAllErrors();

     }

 

 

}

修改UserController

注意:这个时候我们就需要去掉verify()方法中的BindingResult result参数,因为不去掉的话,出现错误信息不会抛出异常,会被收集起来封装到BindingResult中去,所以要想使用全局异常处理类来处理校验异常,就必须去掉BindingResult参数,让其抛出异常,我们再使用全局异常处理类进行异常处理,封装异常信息并返回。

?

1

2

3

4

5

6

7

8

9

10

@Controller

public class UserController {

 

     @PostMapping ( "/verify" )

     @ResponseBody

     public Object verify( @Valid User user) {

         return user;

     }

 

}

重启项目查看运行效果

到此这篇关于SpringBoot+Hibernate实现自定义数据验证及异常处理的文章就介绍到这了,更多相关SpringBoot Hibernate数据验证 异常处理内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

原文链接:https://blog.csdn.net/qq_31762741/article/details/124344496

查看更多关于SpringBoot+Hibernate实现自定义数据验证及异常处理的详细内容...

  阅读:10次