当前位置:网站首页>Bean validation custom container validation chapter ----06
Bean validation custom container validation chapter ----06
2022-07-24 00:37:00 【Big flicker love flicker】
Bean Validation Custom container validation ----06
Preface
This article continues with the above description , Continue Bean Validation Declarative verification of the four levels of : Container element validation ( Custom container type ) And class level validation ( Also called multi field joint validation ).
Custom container type element validation
We already know from the above Bean Validation It can be used for the shape of List、Set、Map The elements in such a container type are verified , The container with built-in support can cover Most of the usage scenarios , But some scenes still can't be covered , And this may be very common .
For example, we are not unfamiliar with the method return value container Result< T >, The structure is like this ( The simplest form , For reference only ):
@Data
public final class Result<T> implements Serializable {
private boolean success = true;
private T data = null;
private String errCode;
private String errMsg;
}
Controller It's packed in layers ( load ) data data, It's like this :
@GetMapping("/room")
Result<Room> room() {
... }
public class Room {
@NotNull
public String name;
@AssertTrue
public boolean finished;
}
At this time, I hope to be right Result< Room > Inside Room Verify the validity : With the help of BV Do declarative validation instead of hard coding .
I hope it's OK :Result<@Notnull @Valid LoggedAccountResp>. obviously , By default, even if the constraint annotation is declared, it is invalid , After all Bean Validation At all “ incognizance ”Result This “ Containers ”, Not to mention verifying its elements .
Fortunately Bean Validation This provides an extension point . I'm going to provide a step-by-step implementation of this , Let verification elegance rise again .
- You can customize one from Result< T > Rita took it out T It's worth it ValueExtractor Value extractor
Bean Validation Allow us to support custom container element types .
To support custom container types , Need to register a custom ValueExtractor For value extraction .
public class ResultValueExtractor implements ValueExtractor<Result<@ExtractedValue ?>> {
@Override
public void extractValues(Result<?> originalValue, ValueReceiver receiver) {
receiver.value(null, originalValue.getData());
}
}
- Register this custom value extractor with the validator Validator in , And provide test code :
hold Result As a Filed Fields are loaded into Java Bean in :
public class ResultDemo {
public Result<@Valid Room> roomResult;
}
Test code :
public static void main(String[] args) {
Room room = new Room();
room.name = "DHY";
Result<Room> result = new Result<>();
result.setData(room);
// hold Result Put it in as an attribute
ResultDemo resultDemo = new ResultDemo();
resultDemo.roomResult = result;
// Register a custom value extractor
Validator validator = ValidatorUtil.obtainValidatorFactory()
.usingContext()
.addValueExtractor(new ResultValueExtractor())
.getValidator();
ValidatorUtil.printViolations(validator.validate(resultDemo));
}
Run the test program , Output :
roomResult.finished Only for true, But your value is : false
It's perfect. Yes Result“ Containers ” The elements in are verified .
Tips: : This example is to put Result As Java Bean To test the properties of . In fact, in most cases, it is verified as the return value of the method . Similar way , Interested students can draw inferences by themselves
Here is a weak complement to , If in Spring Boot In the scene, you imagine doing this to Result< T > Provide support , Then you need to provide a verifier to cover the automatically assembled , May refer to ValidationAutoConfiguration.
Class level validation ( Multi field joint validation )
Constraints can also be placed at the class level ( In other words, annotations are marked on the class ). under these circumstances , The subject of validation is not a single attribute , It's the whole object . If verification depends on the correlation between several attributes of the object , Then class level constraints can do it all .
This requirement scenario is also very common in normal development , I'll give you a scenario :
Room It means a classroom ,maxStuNum Represents the maximum number of students allowed in the classroom ,studentNames The students in the classroom . It's obvious that there is such a rule here : The total number of students cannot be greater than the maximum allowed in the classroom , namely studentNames.size() <= maxStuNum. If you use transaction scripts to implement this validation rule , So your code must be interspersed with code like this :
if (room.getStudentNames().size() > room.getMaxStuNum()) {
throw new RuntimeException("...");
}
Although this can also achieve the effect of verification , But obviously it's not elegant enough . Expect this case You can still use Bean Validation To achieve elegance , Now I'll take a walk .
Compared to the previous but field / The use of attribute validation case, What needs to be verified is the whole object ( Multiple fields ). Down here , I give two ways to implement it , For reference .
Mode one : Based on the built-in @ScriptAssert Realization
although Bean Validation There are no built-in class level annotations , but Hibernate-Validator But it provides an enhancement to this , To make up for its shortcomings [email protected] Namely HV Built in a very powerful 、 Can be used to validate annotations at the class level , It's easy to handle this kind of case:
@ScriptAssert(lang = "javascript", alias = "_", script = "_.maxStuNum >= _.studentNames.length")
@Data
public class Room {
@Positive
private int maxStuNum;
@NotNull
private List<String> studentNames;
}
@ScriptAssert Support to write scripts to complete the verification logic , What we use here is javascript( The only choice by default , It's also the default choice )
The test case :
public static void main(String[] args) {
Room room = new Room();
ValidatorUtil.printViolations(ValidatorUtil.obtainValidator().validate(room));
}
Run the program , Throw the wrong :
Caused by: <eval>:1 TypeError: Cannot get property "length" of null
at jdk.nashorn.internal.runtime.ECMAErrors.error(ECMAErrors.java:57)
at jdk.nashorn.internal.runtime.ECMAErrors.typeError(ECMAErrors.java:213)
...
This error means _.studentNames The value is null, That is to say room.studentNames The value of the field is null.
what? It's not clearly marked on its head @NotNull Comments , How could it be for null Well ? This actually involves a little knowledge point mentioned above , Here's a word : All constraint annotations are executed , There is no short circuit effect ( Unless the verification program throws an exception ), As long as you dare to bid , I dare to carry out , So here's why it's wrong. You get it .
Tips: :@ScriptAssert Yes null Value is not immune to , In any case, it will carry out , Therefore, when writing scripts, pay attention to empty sentence
Of course , The execution of multiple constraints can also be sorted ( Orderly ), This involves the execution order of multiple constraints ( Sequence ) problem , For the time being, this paper bypasses . For example, fill in a value first , In the following special article, we will explain several constraint annotation execution sequence problems and case analysis .
Modify the test script ( Add a student , Let it not be null):
public static void main(String[] args) {
Room room = new Room();
room.setStudentNames(Collections.singletonList("DHY"));
ValidatorUtil.printViolations(ValidatorUtil.obtainValidator().validate(room));
}
Run again :
Execute script expression "_.maxStuNum >= _.studentNames.length" No return of expected results , But your value is : Room(maxStuNum=0, studentNames=[DHY])
maxStuNum It must be a positive number , But your value is : 0
The verification results are in line with the expectation :0(maxStuNum) < 1(studentNames.length).
Tips: : If you add a sentence to the test script room.setMaxStuNum(1);, So what's the result ?
Mode two : Custom annotation method to achieve
although BV Custom annotations have not been mentioned before , But it's not hard , So here's a face , You can also come back after reading the following article .
- Customize a constraint annotation , And it provides the implementation of constraint logic
@Target({
TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = {
ValidStudentCountConstraintValidator.class})
public @interface ValidStudentCount {
String message() default " The number of students exceeds the maximum ";
Class<?>[] groups() default {
};
Class<? extends Payload>[] payload() default {
};
}
public class ValidStudentCountConstraintValidator implements ConstraintValidator<ValidStudentCount, Room> {
@Override
public void initialize(ValidStudentCount constraintAnnotation) {
}
@Override
public boolean isValid(Room room, ConstraintValidatorContext context) {
if (room == null) {
return true;
}
boolean isValid = false;
if (room.getStudentNames().size() <= room.getMaxStuNum()) {
isValid = true;
}
// Custom prompt ( Of course, you can not customize , Then use the... In the note message Value of field )
if (!isValid) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(" Check failed xxx")
.addPropertyNode("studentNames")
.addConstraintViolation();
}
return isValid;
}
}
Write test scripts
public static void main(String[] args) {
Room room = new Room();
room.setStudentNames(Collections.singletonList("YourBatman"));
ValidatorUtil.printViolations(ValidatorUtil.obtainValidator().validate(room));
}
Run the program , Output :
maxStuNum It must be a positive number , But your value is : 0
studentNames Check failed xxx, But your value is : Room(maxStuNum=0, studentNames=[YourBatman])
perfect , It's exactly as expected .
Both methods can implement class level validation , Both of them have their own advantages and disadvantages , It is mainly reflected in the following aspects :
- @ScriptAssert It's built-in , So it's very convenient and versatile . But the disadvantage is that it is too general , So it's not semantically obvious , You need to read the script to know . Recommend a small amount of ( Non reusable )、 Use when the logic is simple
- Custom annotation method . The disadvantage, of course, is “ Use it out of the box ” It's a little troublesome to get up , But it has the advantage of being semantically clear , Flexible and error free , Even complex verification logic can be easily handled
All in all , If your verification logic is only once ( Only one place to use ) And simple ( For example, it's just a simple judgment ), Recommended @ScriptAssert It's lighter . otherwise , You'll see ~
边栏推荐
- Gbase 8C mode visibility query function (2)
- Development of main applet for business card traffic near the map
- GBase 8c系统表信息函数(一)
- Interviewer: if the order is not paid within 30 minutes after it is generated, it will be automatically cancelled. How to realize it?
- GBase 8c 二进制字符串操作符
- The prediction of domestic AI protein structure reproduced a breakthrough and solved the 3D structure with a single sequence. Peng Jian's team: "the last piece of puzzle since alphafold2 has been comp
- Intelligent OCR identification of express documents helps the logistics industry to upgrade Digitalization
- What is promise? What are the benefits of promise
- Customize an object
- Gbase 8C session information function (4)
猜你喜欢

Printf function - conversion description

Summary of polynomial commitment schemes

win10下基于anaconda的detectron2安装

Comparison of the shortcomings of redis master-slave, sentinel and cluster architectures

数仓数据标准详解-2022

网络系统实验:ping不通的问题解决

Network system experiment: solve the problem of Ping failure

Method of C language annotation

php实现 Stripe订阅

Classic example of C language - convert the input two digits into English
随机推荐
GBase 8c 模式可见性查询函数(一)
数据模型设计方法概述
Detectron2 installation based on Anaconda under win10
GBase 8c 位串操作符(一)
GBase 8c 访问权限查询函数(一)
PHP implements stripe subscription
EFCore高级Saas系统下单DbContext如何支持不同数据库的迁移
泛型机制和增强for循环
GBase 8c系统表信息函数(二)
Classic examples of C language - use 4 × The matrix displays all integers from 1 to 16 and calculates the sum of each row, column, and diagonal
Detailed explanation of data warehouse standard -2022
如何使用 SAP Intelligent Robotic Process Automation 自动操作 Excel
English grammar_ Demonstrative pronoun -such / the same
How to realize 485 wireless communication between multiple sensors and Siemens PLC?
Tencent cloud was affirmed by international professional streaming media evaluation: video coding performance is the best in all three scenarios
Gbase 8C session information function (III)
Comparison of the shortcomings of redis master-slave, sentinel and cluster architectures
Gbase 8C string Operator
The most basic code interpretation of C language
Gbase 8C session information function (4)