Hibernate Validator custom validation

Keywords: Java Hibernate Lombok network

Preface

Under my own exploration, I have a preliminary understanding of Hibernate Validator. We can use existing constraints to restrict fields, reduce the occurrence of no code, and make the code more concise.
However, in recent practical use, there have been some problems that can not be handled by the framework. For example, when a third party requests my interface, it distinguishes different business logic according to status field; status=1 carries on A logic processing, status=2 carries on B logic processing; after retrieving relevant information on the network, it makes the following summary.

step

  1. Generate entity classes and mappers using generic mapper plug-ins. In order to focus on the use of Hibernate Validator, we focus on the entity class Brand. In the entity class, we add a custom constraint @StatusConstraint to the status field.

     package com.codeup.mybatisjoin.model;
    
    import com.codeup.mybatisjoin.validation.StatusConstraint;
    import lombok.Data;
    import javax.persistence.Column;
    import javax.persistence.Id;
    import javax.validation.constraints.NotBlank;
    import javax.validation.constraints.NotNull;
    
    @Data
    public class Brand {
       
        @Id
        @Column(name = "brand_ID")
        private Long brandId;
    
    
        /**
         * Use `@NotNull'to add constraints
         */
        @Column(name = "vendor_ID")
        @NotNull(message = "[vendorId]Not empty")
        private Long vendorId;
    
    
        /**
         * Add constraints using `@NotBlank'
         */
        @Column(name = "brand_name")
        @NotBlank(message = "[brandName]Field cannot be empty")
        private String brandName;
    
        private String description;
    
    
        /**
         * Custom Constraints
         */
        @StatusConstraint(message = "[status]1 or 2")
        private String status;
    }
  2. @ StatusConstrain implementation steps are similar to custom annotations

    package com.codeup.mybatisjoin.validation;
    
    import javax.validation.Constraint;
    import javax.validation.Payload;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * @ProjectName: mybatisjoin
     * @Package: com.codeup.mybatisjoin.validation
     * @ClassName: StatusConstraint
     * @Author: lhc
     * @Description: State constraints
     * @Date: 2019/8/22 3:36 p.m.
     */
    @Target({ElementType.FIELD, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    /**
     * It is pointed out that the current constraints are implemented by `StatusConstraintValidator.class'.
     */
    @Constraint(validatedBy = StatusConstraintValidator.class)
    public @interface StatusConstraint {
    
        /**
         * Configure message information
         * @return
         */
        String message() default "Illegal parameters";
    
        /**
         * Grouping
         * @return
         */
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    }
  3. By looking at the documentation in the source code, you know that you want to implement the ConstraintValidator interface

    @Documented
    @Target({ ANNOTATION_TYPE })
    @Retention(RUNTIME)
    public @interface Constraint {
    
        /**
         * {@link ConstraintValidator} classes implementing the constraint. The given 
    classes
         * must reference distinct target types for a given {@link ValidationTarget}. 
    If two
         * {@code ConstraintValidator}s refer to the same type, an exception will 
    occur.
         * <p>
         * At most one {@code ConstraintValidator} targeting the array of parameters of
         * methods or constructors (aka cross-parameter) is accepted. If two or more
         * are present, an exception will occur.
         *
         * @return array of {@code ConstraintValidator} classes implementing the 
    constraint
         */
        Class<? extends ConstraintValidator<?, ?>>[] validatedBy();
    }
  4. Implement ConstraintValidator interface, rewrite isValid() method, and implement its own judgment logic

    package com.codeup.mybatisjoin.validation;
    
    import lombok.extern.slf4j.Slf4j;
    
    import javax.validation.ConstraintValidator;
    import javax.validation.ConstraintValidatorContext;
    
    /**
     * @ProjectName: mybatisjoin
     * @Package: com.codeup.mybatisjoin.validation
     * @ClassName: StatusConstraintValidator
     * @Author: lhc
     * @Description: TODO
     * @Date: 2019/8/22 3:38 p.m.
     */
    @Slf4j
    public class StatusConstraintValidator implements 
    ConstraintValidator<StatusConstraint,Object> {
    
        @Override
        public boolean isValid(Object value, ConstraintValidatorContext context) {
            String statCode = (String) value;
            // Equivalent to 1 or 2 returns true, and vice versa
            if ("1".equals(statCode) || "2".equals(statCode)) {
                return true;
            }
            return false;
        }
    }
  5. At this point, the addition of constraints to the field has been completed. There is also an important operation for checking constraints, which is implemented by the tool class.

    package com.codeup.mybatisjoin.validation;
    
    import org.hibernate.validator.HibernateValidator;
    
    import javax.validation.ConstraintViolation;
    import javax.validation.Validation;
    import javax.validation.Validator;
    import java.util.Set;
    
    /**
     * @ProjectName: mybatisjoin
     * @Package: com.codeup.mybatisjoin.validation
     * @ClassName: ValidatorConfig
     * @Author: lhc
     * @Description: TODO
     * @Date: 2019/8/22 4:31 p.m.
     */
    public class ValidatorUtil {
    
    
        /**
         * Configuring hibernate_validator and fast failure mode
         */
        private static Validator validator = 
    Validation.byProvider(HibernateValidator.class)
               .configure()
               .failFast(true)
               .buildValidatorFactory()
               .getValidator();
    
    
        /**
         * Parametric checking, if no constraints are matched, throws the previously defined `message'.
         * @param object parameter
         * @param groups Belong to group
         */
        public static void result(Object object, Class<?>... groups) {
            Set<ConstraintViolation<Object>> constraintViolations = 
    validator.validate(object, groups);
             if (constraintViolations.size() > 0) {
                String message = constraintViolations.iterator().next().getMessage();
                throw new MissingParameterException(message);
            }
        }
    }
  6. After the message information is thrown out, it needs to be returned to the third party in a more unified way and solved by the unified exception handling mechanism.

    • Custom exception class MissingParameterException

      package com.codeup.mybatisjoin.validation;
      
      public class MissingParameterException extends RuntimeException {
      
          public MissingParameterException(String message) {
              super(message);
          }
      }
      
    • Unified exception handling

      package com.codeup.mybatisjoin.validation;
      
      import org.springframework.web.bind.annotation.ExceptionHandler;
      import org.springframework.web.bind.annotation.RestControllerAdvice;
      
      import java.util.HashMap;
      import java.util.Map;
      
      
      @RestControllerAdvice
      public class GlobalExceptionHandler {
      
          @ExceptionHandler(MissingParameterException.class)
          public Map<String, Object> invalidParameter(MissingParameterException e) {
              Map<String, Object> map = new HashMap<>();
              map.put("code", 500);
              map.put("message", e.getMessage());
              return map;
          }
      }
  • View the returned results by request

    package com.codeup.mybatisjoin.controller;
    
    import com.codeup.mybatisjoin.model.Brand;
    import com.codeup.mybatisjoin.validation.ValidatorUtil;
    import lombok.extern.slf4j.Slf4j;
    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 java.util.LinkedHashMap;
    import java.util.Map;
    
    
    @Slf4j
    @RestController
    @RequestMapping(value = "/brandConroller")
    public class BrandConroller {
    
        @PostMapping(value = "/go")
        public Map<String, Object> go(@RequestBody Brand brand) {
            ValidatorUtil.result(brand);
            Map<String, Object> map = new LinkedHashMap<>();
            log.info("brand:{}", brand);
            return map;
        }
    }
  1. request

    // request
     {
        "brandId": 1,
        "vendorId": 1,
        "brandName": "demoData",
        "description": "demoData",
           "status": "3"
    }
    // response
    {
        "code": 500,
        "message": "[status]1 or 2"
    }

If there is anything wrong, please Tucao me.

Posted by redarrow on Thu, 22 Aug 2019 20:14:21 -0700