Skip to content

Commit 7baaa35

Browse files
author
veasion
committed
动态查询、动态关联QueryCriteria机制
1 parent 02cba8b commit 7baaa35

File tree

8 files changed

+514
-0
lines changed

8 files changed

+514
-0
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package cn.veasion.db.criteria;
2+
3+
import cn.veasion.db.base.JoinTypeEnum;
4+
5+
/**
6+
* 定义动态触发或静态关联查询
7+
*
8+
* @author luozhuowei
9+
* @date 2021/12/15
10+
*/
11+
public @interface JoinCriteria {
12+
13+
/**
14+
* join 左边的类(被join的类,默认当前操作实体类)
15+
*/
16+
Class<?> value() default Void.class;
17+
18+
/**
19+
* 关联对象类型(关联后 field 作用会映射到关联对象条件上)
20+
*/
21+
Class<?> join() default Void.class;
22+
23+
/**
24+
* 关联类型,默认 join
25+
*/
26+
JoinTypeEnum joinType() default JoinTypeEnum.JOIN;
27+
28+
/**
29+
* 当前实体类和关联类的 on 字段 <br><br>
30+
* 示例一(学生关联班级): {"classId", "id"} <br>
31+
* 示例二(班级关联班主任教师): {"masterTno", "tno"} <br>
32+
* 示例三(班级关联班主任课程,多个字段关联):{"id", "classId", "masterTno", "tno"} <br>
33+
* 解释(成对出现):{"当前字段1", "关联字段1", "当前字段2", "关联字段2", "当前字段3", "关联字段3"}
34+
*/
35+
String[] onFields() default {};
36+
37+
/**
38+
* 是否静态关联(默认false): true 静态的,任意条件都会关联,false 动态的,只有字段触发关联才关联
39+
*/
40+
boolean staticJoin() default false;
41+
42+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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.TYPE;
7+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
8+
9+
/**
10+
* 定义动态触发或静态关联查询集合
11+
*
12+
* @author luozhuowei
13+
* @date 2021/12/15
14+
*/
15+
@Target(TYPE)
16+
@Retention(RUNTIME)
17+
public @interface JoinCriteriaMulti {
18+
19+
JoinCriteria[] value() default {};
20+
21+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package cn.veasion.db.criteria;
2+
3+
import cn.veasion.db.base.Operator;
4+
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.Target;
7+
8+
import static java.lang.annotation.ElementType.FIELD;
9+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
10+
11+
/**
12+
* 定义字段动态查询
13+
*
14+
* @author luozhuowei
15+
* @date 2021/12/15
16+
*/
17+
@Target(FIELD)
18+
@Retention(RUNTIME)
19+
public @interface QueryCriteria {
20+
21+
/**
22+
* 过滤类型(默认EQ)
23+
*/
24+
Operator value() default Operator.EQ;
25+
26+
/**
27+
* 指定字段(默认当前字段名)
28+
*/
29+
String field() default "";
30+
31+
/**
32+
* 指定多个字段 OR 关系
33+
*/
34+
String[] orFields() default {};
35+
36+
/**
37+
* 是否跳过空值,如空字符串、空集合、空数组等(默认跳过)
38+
*/
39+
boolean skipEmpty() default true;
40+
41+
/**
42+
* 关联类,触发动态关联(默认当前操作实体类)
43+
*/
44+
Class<?> relation() default Void.class;
45+
46+
}
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
package cn.veasion.db.criteria;
2+
3+
import cn.veasion.db.DbException;
4+
import cn.veasion.db.FilterException;
5+
import cn.veasion.db.base.Filter;
6+
import cn.veasion.db.base.Operator;
7+
import cn.veasion.db.query.EQ;
8+
import cn.veasion.db.query.EntityQuery;
9+
import cn.veasion.db.query.JoinQueryParam;
10+
import cn.veasion.db.utils.FieldUtils;
11+
12+
import java.lang.reflect.Field;
13+
import java.util.Arrays;
14+
import java.util.Collection;
15+
import java.util.HashMap;
16+
import java.util.HashSet;
17+
import java.util.Iterator;
18+
import java.util.Map;
19+
import java.util.Objects;
20+
import java.util.Set;
21+
22+
/**
23+
* QueryCriteriaConvert
24+
*
25+
* @author luozhuowei
26+
* @date 2021/12/15
27+
*/
28+
public class QueryCriteriaConvert {
29+
30+
private Object object;
31+
private EntityQuery query;
32+
private JoinCriteria[] array;
33+
private Set<Class<?>> joined = new HashSet<>();
34+
private Map<Class<?>, EntityQuery> joinClassMap = new HashMap<>();
35+
36+
public QueryCriteriaConvert(Object object) {
37+
this(object, null);
38+
}
39+
40+
public QueryCriteriaConvert(Object object, Class<?> entityClass) {
41+
Objects.requireNonNull(object, "参数不能为空");
42+
this.object = object;
43+
this.query = new EQ(entityClass, "t");
44+
this.handleFilters();
45+
}
46+
47+
/**
48+
* 获取查询对象
49+
*/
50+
public EntityQuery getEntityQuery() {
51+
return this.query;
52+
}
53+
54+
/**
55+
* 判断类型是否存在关联
56+
*/
57+
public boolean hasJoin(Class<?> clazz) {
58+
return joined.contains(clazz);
59+
}
60+
61+
/**
62+
* 获取关联类型查询对象
63+
*/
64+
public EntityQuery getJoinEntityQuery(Class<?> clazz) {
65+
return joinClassMap.get(clazz);
66+
}
67+
68+
public Map<Class<?>, EntityQuery> getJoinClassMap() {
69+
return joinClassMap;
70+
}
71+
72+
private void handleFilters() {
73+
JoinCriteriaMulti joinCriteriaMulti = object.getClass().getAnnotation(JoinCriteriaMulti.class);
74+
if (joinCriteriaMulti != null) {
75+
array = joinCriteriaMulti.value();
76+
}
77+
initJoinClassMap();
78+
Map<String, Field> fields = FieldUtils.fields(object.getClass());
79+
for (Field field : fields.values()) {
80+
QueryCriteria annotation = field.getAnnotation(QueryCriteria.class);
81+
if (annotation == null) {
82+
continue;
83+
}
84+
Object value = FieldUtils.getValue(object, field.getName(), true);
85+
if (value == null) {
86+
continue;
87+
}
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+
}
97+
}
98+
String fieldName = "".equals(annotation.field()) ? field.getName() : annotation.field();
99+
Operator operator = annotation.value();
100+
Class<?> relationClass = annotation.relation();
101+
102+
if (relationClass != Void.class) {
103+
checkJoin(relationClass);
104+
}
105+
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);
122+
if (relationClass != Void.class) {
123+
filter.fieldAs(joinClassMap.get(relationClass).getTableAs());
124+
}
125+
query.addFilter(filter);
126+
}
127+
}
128+
}
129+
130+
private void initJoinClassMap() {
131+
if (array == null || array.length == 0) return;
132+
for (JoinCriteria joinCriteria : array) {
133+
Class<?> join = joinCriteria.join();
134+
if (join != Void.class) {
135+
joinClassMap.put(join, new EQ(join, FieldUtils.firstCase(join.getSimpleName(), true)));
136+
}
137+
}
138+
joinClassMap.put(Void.class, query);
139+
for (JoinCriteria joinCriteria : array) {
140+
if (joinCriteria.staticJoin()) {
141+
checkJoin(joinCriteria.join());
142+
}
143+
}
144+
}
145+
146+
private void checkJoin(Class<?> joinClass) {
147+
if (joined.contains(joinClass)) {
148+
return;
149+
}
150+
if (array == null || array.length == 0) {
151+
throw new DbException("@JoinCriteriaMulti 中关联类未找到:" + joinClass.getSimpleName());
152+
}
153+
Set<JoinCriteria> joinCriteriaSet = new HashSet<>(Arrays.asList(array));
154+
do {
155+
for (JoinCriteria joinCriteria : joinCriteriaSet) {
156+
if (joinClass == joinCriteria.join()) {
157+
EntityQuery main = joinClassMap.get(joinCriteria.value());
158+
EntityQuery join = joinClassMap.get(joinCriteria.join());
159+
JoinQueryParam joinQueryParam = main.join(joinCriteria.joinType(), join);
160+
joined.add(joinClass);
161+
String[] onFields = joinCriteria.onFields();
162+
for (int i = 0; i < onFields.length; i += 2) {
163+
joinQueryParam.on(onFields[i], onFields[i + 1]);
164+
}
165+
joinClass = joinCriteria.value();
166+
joinCriteriaSet.remove(joinCriteria);
167+
break;
168+
}
169+
}
170+
} while (joinClass != Void.class && !joined.contains(joinClass));
171+
}
172+
173+
public static Filter getFilter(String field, Operator operator, Object value) {
174+
if (Operator.EQ.equals(operator)) {
175+
return Filter.eq(field, value);
176+
} else if (Operator.NEQ.equals(operator)) {
177+
return Filter.neq(field, value);
178+
} else if (Operator.GT.equals(operator)) {
179+
return Filter.gt(field, value);
180+
} else if (Operator.GTE.equals(operator)) {
181+
return Filter.gte(field, value);
182+
} else if (Operator.LT.equals(operator)) {
183+
return Filter.lt(field, value);
184+
} else if (Operator.LTE.equals(operator)) {
185+
return Filter.lte(field, value);
186+
} else if (Operator.IN.equals(operator)) {
187+
if (value instanceof Collection) {
188+
return Filter.in(field, (Collection<?>) value);
189+
} else if (value instanceof Object[]) {
190+
return Filter.in(field, (Object[]) value);
191+
} else {
192+
throw new FilterException(field + " 字段 Operator.IN 类型必须是集合或者数组");
193+
}
194+
} else if (Operator.NOT_IN.equals(operator)) {
195+
if (value instanceof Collection) {
196+
return Filter.notIn(field, (Collection<?>) value);
197+
} else if (value instanceof Object[]) {
198+
return Filter.notIn(field, (Object[]) value);
199+
} else {
200+
throw new FilterException(field + " 字段 Operator.IN 类型必须是集合或者数组");
201+
}
202+
} else if (Operator.LIKE.equals(operator)) {
203+
return Filter.like(field, value);
204+
} else if (Operator.BETWEEN.equals(operator)) {
205+
if (value instanceof Collection) {
206+
Iterator<?> iterator = ((Collection<?>) value).iterator();
207+
return Filter.between(field, iterator.next(), iterator.next());
208+
} else if (value instanceof Object[]) {
209+
Object[] objects = (Object[]) value;
210+
return Filter.between(field, objects[0], objects[1]);
211+
} else {
212+
throw new FilterException(field + " 字段 Operator.BETWEEN 类型必须是集合或者数组");
213+
}
214+
} else if (Operator.NULL.equals(operator)) {
215+
return Filter.isNull(field);
216+
} else if (Operator.NOT_NULL.equals(operator)) {
217+
return Filter.isNotNull(field);
218+
} else {
219+
throw new FilterException(field + " 不支持 Operator." + operator.name());
220+
}
221+
}
222+
223+
}

src/test/java/cn/veasion/db/DefaultDataSourceProvider.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ public class DefaultDataSourceProvider implements DataSourceProvider {
2222

2323
@Override
2424
public DataSource getDataSource(EntityDao<?, ?> entityDao, JdbcTypeEnum jdbcTypeEnum) {
25+
// 根据 jdbcTypeEnum 判断读写数据库获取数据源
26+
// 集成 spring 可用通过 SpringUtils.getBean(DataSource.class) 获取数据源
2527
try {
2628
return getDataSource(BaseTest.jdbcUrl, BaseTest.user, BaseTest.password);
2729
} catch (SQLException e) {
@@ -31,11 +33,13 @@ public DataSource getDataSource(EntityDao<?, ?> entityDao, JdbcTypeEnum jdbcType
3133

3234
@Override
3335
public Connection getConnection(DataSource dataSource) throws SQLException {
36+
// 集成 spring 可用通过 org.springframework.jdbc.datasource.DataSourceUtils.getConnection(dataSource) 获取连接
3437
return dataSource.getConnection();
3538
}
3639

3740
@Override
3841
public boolean autoClose() {
42+
// 集成 spring 时这里请设置为 false,由 spring 连接池和事务管理
3943
return true;
4044
}
4145

0 commit comments

Comments
 (0)