Skip to content

Commit 8a1ce0e

Browse files
author
veasion
committed
支持通用查询映射转换
1 parent 7baaa35 commit 8a1ce0e

File tree

4 files changed

+204
-30
lines changed

4 files changed

+204
-30
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package cn.veasion.db.criteria;
2+
3+
import java.lang.annotation.Retention;
4+
import java.lang.annotation.Target;
5+
6+
import static java.lang.annotation.ElementType.FIELD;
7+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
8+
9+
/**
10+
* 自动动态查询(约定规则)
11+
* <pre>
12+
* 注解建议使用在字段类型为 Map 上,key 为字段名称(前端传),value 为值 (前端传);
13+
* 为防止存在SQL注入 key 正则匹配 [_0-9a-zA-Z]+ 才会生效。
14+
* 约定规则一:value 数值、字符串等基础类型统一 Operator.EQ 处理
15+
* 约定规则二:value 字符串类型前缀为 % 后缀为 % 全模糊,前缀 % 走左模糊,后缀 % 走右模糊
16+
* 约定规则三:value 为集合、数组类型走 Operator.IN
17+
* 约定规则四:key 以 start_ 开头走 Operator.GTE,如 start_createTime 走 createTime >= value
18+
* 约定规则五:key 以 end_ 开头走 Operator.LTE,如 end_createTime 走 createTime <= value
19+
* 约定规则六:日期类型自动转 yyyy-MM-dd HH:mm:ss 字符串
20+
* </pre>
21+
*
22+
* @author luozhuowei
23+
* @date 2021/12/15
24+
*/
25+
@Target(FIELD)
26+
@Retention(RUNTIME)
27+
public @interface AutoCriteria {
28+
29+
/**
30+
* 是否跳过空值,如空字符串、空集合、空数组等(默认跳过)
31+
*/
32+
boolean skipEmpty() default true;
33+
34+
/**
35+
* 关联类,触发动态关联(默认当前操作实体类)
36+
*/
37+
Class<?> relation() default Void.class;
38+
39+
}

src/main/java/cn/veasion/db/criteria/QueryCriteriaConvert.java

Lines changed: 107 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,17 @@
1010
import cn.veasion.db.utils.FieldUtils;
1111

1212
import java.lang.reflect.Field;
13+
import java.text.SimpleDateFormat;
1314
import java.util.Arrays;
1415
import java.util.Collection;
16+
import java.util.Date;
1517
import java.util.HashMap;
1618
import java.util.HashSet;
1719
import java.util.Iterator;
1820
import java.util.Map;
1921
import java.util.Objects;
2022
import java.util.Set;
23+
import java.util.regex.Pattern;
2124

2225
/**
2326
* QueryCriteriaConvert
@@ -27,6 +30,8 @@
2730
*/
2831
public class QueryCriteriaConvert {
2932

33+
public static final Pattern FIELD_PATTERN = Pattern.compile("[_0-9a-zA-Z]+");
34+
3035
private Object object;
3136
private EntityQuery query;
3237
private JoinCriteria[] array;
@@ -69,6 +74,7 @@ public Map<Class<?>, EntityQuery> getJoinClassMap() {
6974
return joinClassMap;
7075
}
7176

77+
@SuppressWarnings("unchecked")
7278
private void handleFilters() {
7379
JoinCriteriaMulti joinCriteriaMulti = object.getClass().getAnnotation(JoinCriteriaMulti.class);
7480
if (joinCriteriaMulti != null) {
@@ -77,53 +83,100 @@ private void handleFilters() {
7783
initJoinClassMap();
7884
Map<String, Field> fields = FieldUtils.fields(object.getClass());
7985
for (Field field : fields.values()) {
80-
QueryCriteria annotation = field.getAnnotation(QueryCriteria.class);
81-
if (annotation == null) {
86+
QueryCriteria queryCriteria = field.getAnnotation(QueryCriteria.class);
87+
AutoCriteria autoCriteria = field.getAnnotation(AutoCriteria.class);
88+
if (queryCriteria == null && autoCriteria == null) {
8289
continue;
8390
}
8491
Object value = FieldUtils.getValue(object, field.getName(), true);
8592
if (value == null) {
8693
continue;
8794
}
88-
boolean skipEmpty = annotation.skipEmpty();
89-
if (skipEmpty) {
90-
if (value instanceof String && "".equals(value)) {
91-
continue;
92-
} else if (value instanceof Collection && ((Collection<?>) value).isEmpty()) {
93-
continue;
94-
} else if (value instanceof Object[] && ((Object[]) value).length == 0) {
95-
continue;
96-
}
95+
if (queryCriteria != null) {
96+
handleQueryCriteria(field, queryCriteria, value);
97+
} else if (value instanceof Map) {
98+
handleAutoCriteria(autoCriteria, (Map<String, Object>) value);
99+
} else {
100+
handleAutoCriteria(autoCriteria, new HashMap<String, Object>() {{
101+
put(field.getName(), value);
102+
}});
97103
}
98-
String fieldName = "".equals(annotation.field()) ? field.getName() : annotation.field();
99-
Operator operator = annotation.value();
100-
Class<?> relationClass = annotation.relation();
104+
}
105+
}
101106

107+
private void handleAutoCriteria(AutoCriteria annotation, Map<String, Object> filters) {
108+
Class<?> relationClass = annotation.relation();
109+
for (Map.Entry<String, Object> entry : filters.entrySet()) {
110+
String key = entry.getKey();
111+
Object value = entry.getValue();
112+
if (value == null) {
113+
continue;
114+
}
115+
if (annotation.skipEmpty() && isEmpty(value)) {
116+
return;
117+
}
118+
if (!FIELD_PATTERN.matcher(key).matches()) {
119+
throw new FilterException("非法字段:" + key);
120+
}
102121
if (relationClass != Void.class) {
103122
checkJoin(relationClass);
104123
}
124+
Operator operator = Operator.EQ;
125+
if (value instanceof Collection || value instanceof Object[]) {
126+
operator = Operator.IN;
127+
} else if (key.startsWith("start_")) {
128+
key = key.substring(6);
129+
operator = Operator.GTE;
130+
} else if (key.startsWith("end_")) {
131+
key = key.substring(4);
132+
operator = Operator.LTE;
133+
} else if (value instanceof String &&
134+
(String.valueOf(value).startsWith("%") || String.valueOf(value).endsWith("%"))) {
135+
operator = Operator.LIKE;
136+
}
137+
if (value instanceof Date) {
138+
value = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) value);
139+
}
140+
Filter filter = getFilter(key, operator, value);
141+
if (relationClass != Void.class) {
142+
filter.fieldAs(joinClassMap.get(relationClass).getTableAs());
143+
}
144+
query.addFilter(filter);
145+
}
146+
}
105147

106-
String[] orFields = annotation.orFields();
107-
if (orFields.length > 0) {
108-
query.addFilter(Filter.leftBracket());
109-
for (int i = 0; i < orFields.length; i++) {
110-
Filter filter = getFilter(orFields[i], operator, value);
111-
if (relationClass != Void.class) {
112-
filter.fieldAs(joinClassMap.get(relationClass).getTableAs());
113-
}
114-
query.addFilter(filter);
115-
if (i < orFields.length - 1) {
116-
query.addFilter(Filter.or());
117-
}
118-
}
119-
query.addFilter(Filter.rightBracket());
120-
} else {
121-
Filter filter = getFilter(fieldName, operator, value);
148+
private void handleQueryCriteria(Field field, QueryCriteria annotation, Object value) {
149+
if (annotation.skipEmpty() && isEmpty(value)) {
150+
return;
151+
}
152+
String fieldName = "".equals(annotation.field()) ? field.getName() : annotation.field();
153+
Operator operator = annotation.value();
154+
Class<?> relationClass = annotation.relation();
155+
156+
if (relationClass != Void.class) {
157+
checkJoin(relationClass);
158+
}
159+
160+
String[] orFields = annotation.orFields();
161+
if (orFields.length > 0) {
162+
query.addFilter(Filter.leftBracket());
163+
for (int i = 0; i < orFields.length; i++) {
164+
Filter filter = getFilter(orFields[i], operator, value);
122165
if (relationClass != Void.class) {
123166
filter.fieldAs(joinClassMap.get(relationClass).getTableAs());
124167
}
125168
query.addFilter(filter);
169+
if (i < orFields.length - 1) {
170+
query.addFilter(Filter.or());
171+
}
126172
}
173+
query.addFilter(Filter.rightBracket());
174+
} else {
175+
Filter filter = getFilter(fieldName, operator, value);
176+
if (relationClass != Void.class) {
177+
filter.fieldAs(joinClassMap.get(relationClass).getTableAs());
178+
}
179+
query.addFilter(filter);
127180
}
128181
}
129182

@@ -170,6 +223,20 @@ private void checkJoin(Class<?> joinClass) {
170223
} while (joinClass != Void.class && !joined.contains(joinClass));
171224
}
172225

226+
private boolean isEmpty(Object value) {
227+
if (value == null) {
228+
return true;
229+
}
230+
if (value instanceof String && "".equals(value)) {
231+
return true;
232+
} else if (value instanceof Collection && ((Collection<?>) value).isEmpty()) {
233+
return true;
234+
} else if (value instanceof Object[] && ((Object[]) value).length == 0) {
235+
return true;
236+
}
237+
return false;
238+
}
239+
173240
public static Filter getFilter(String field, Operator operator, Object value) {
174241
if (Operator.EQ.equals(operator)) {
175242
return Filter.eq(field, value);
@@ -200,6 +267,16 @@ public static Filter getFilter(String field, Operator operator, Object value) {
200267
throw new FilterException(field + " 字段 Operator.IN 类型必须是集合或者数组");
201268
}
202269
} else if (Operator.LIKE.equals(operator)) {
270+
if (value instanceof String) {
271+
String str = (String) value;
272+
if (str.startsWith("%") && str.endsWith("%")) {
273+
return Filter.like(field, str.substring(1, str.length() - 1));
274+
} else if (str.startsWith("%")) {
275+
return Filter.likeLeft(field, str.substring(1));
276+
} else if (str.endsWith("%")) {
277+
return Filter.likeRight(field, str.substring(0, str.length() - 1));
278+
}
279+
}
203280
return Filter.like(field, value);
204281
} else if (Operator.BETWEEN.equals(operator)) {
205282
if (value instanceof Collection) {

src/test/java/cn/veasion/db/criteria/QueryCriteriaTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import cn.veasion.db.query.EntityQuery;
88

99
import java.util.Arrays;
10+
import java.util.HashMap;
1011

1112
/**
1213
* QueryCriteriaTest
@@ -49,6 +50,29 @@ public static void main(String[] args) {
4950
student.setSnoOrName("熊");
5051
queryList(student);
5152

53+
// 根据班级id区间查询
54+
student = new StudentInVO();
55+
student.setFilters(new HashMap<String, Object>() {{
56+
put("start_classId", 1);
57+
put("end_classId", 10);
58+
}});
59+
queryList(student);
60+
61+
// 根据班级id区间查询(关联班级)
62+
student = new StudentInVO();
63+
student.setClassFilters(new HashMap<String, Object>() {{
64+
put("start_id", 1);
65+
put("end_id", 10);
66+
}});
67+
queryList(student);
68+
69+
// 根据老师名称模糊查询(关联班级)
70+
student = new StudentInVO();
71+
student.setTeacherFilters(new HashMap<String, Object>() {{
72+
put("name", "罗%");
73+
}});
74+
queryList(student);
75+
5276
}
5377

5478
private static void queryList(StudentInVO student) {

src/test/java/cn/veasion/db/criteria/StudentInVO.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import cn.veasion.db.model.po.TeacherPO;
66

77
import java.util.List;
8+
import java.util.Map;
89

910
/**
1011
* StudentInVO
@@ -18,6 +19,7 @@
1819
})
1920
public class StudentInVO {
2021

22+
// 简单字段查询映射
2123
@QueryCriteria
2224
private String sno;
2325
@QueryCriteria(value = Operator.IN, field = "id")
@@ -35,6 +37,14 @@ public class StudentInVO {
3537
@QueryCriteria(value = Operator.LIKE, field = "name", relation = TeacherPO.class)
3638
private String teacherName;
3739

40+
// 通用查询映射
41+
@AutoCriteria
42+
private Map<String, Object> filters;
43+
@AutoCriteria(relation = ClassesPO.class)
44+
private Map<String, Object> classFilters;
45+
@AutoCriteria(relation = TeacherPO.class)
46+
private Map<String, Object> teacherFilters;
47+
3848
public String getSno() {
3949
return sno;
4050
}
@@ -98,5 +108,29 @@ public String getTeacherName() {
98108
public void setTeacherName(String teacherName) {
99109
this.teacherName = teacherName;
100110
}
111+
112+
public Map<String, Object> getFilters() {
113+
return filters;
114+
}
115+
116+
public void setFilters(Map<String, Object> filters) {
117+
this.filters = filters;
118+
}
119+
120+
public Map<String, Object> getClassFilters() {
121+
return classFilters;
122+
}
123+
124+
public void setClassFilters(Map<String, Object> classFilters) {
125+
this.classFilters = classFilters;
126+
}
127+
128+
public Map<String, Object> getTeacherFilters() {
129+
return teacherFilters;
130+
}
131+
132+
public void setTeacherFilters(Map<String, Object> teacherFilters) {
133+
this.teacherFilters = teacherFilters;
134+
}
101135
}
102136

0 commit comments

Comments
 (0)