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
-
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; }
-
@ 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 {}; }
-
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(); }
-
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; } }
-
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); } } }
-
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; } }
-
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.