Spring-data-jpa动态条件查询简化

动态查询是一个非常常见的需求,Spring Data JPA 提供了一套强大的工具集,包括 Specification、CriteriaBuilder 和 Predicate,可以帮助我们构建复杂的动态查询。

  • Specification:Specification 是 Spring Data JPA 提供的一个接口,用于构建 JPA Criteria 查询。它通常与 CriteriaBuilder 和 Predicate 一起使用。
  • CriteriaBuilder:CriteriaBuilder 是 JPA 提供的一个接口,用于构建查询的各个部分,如条件(Predicate)、排序(Order)等。
  • Predicate:Predicate 是 JPA Criteria 查询中的一个条件表达式,用于构建复杂的查询条件。

使用JPA原生的实现构建动态条件比较复杂,因此也有一些开源的框架实现,如:

使用原生语法构建动态条件查询示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public List<SaleOrderEntity> getOrderList(SaleOrderRequest request){
Specification<SaleOrderEntity> specif = new Specification<SaleOrderEntity>() {
@Override
public Predicate toPredicate(Root<SaleOrderEntity> root, CriteriaQuery<?> query,
CriteriaBuilder criteriaBuilder) {
List<Predicate> list = new ArrayList<Predicate>();
if (request.getOrderId() != null) {
list.add(criteriaBuilder.equal(root.get("orderId").as(String.class), request.getOrderId()));
}
if (request.getUserId() != null) {
list.add(criteriaBuilder.equal(root.get("userId").as(String.class), request.getUserId()));
}
return criteriaBuilder.and(list.toArray(new Predicate[list.size()]));
}
};
return saleOrderRepository.findAll(specif);
}

从上面的代码可以看到使用Specification会有大量的条件判断与相似代码,因此可以进行简化。

简化实现

PredicateBuilder<T>

PredicateBuilder类用于构建查询的条件,如:

  • equals:创建一个等于条件
  • notEquals:创建一个不等于条件
  • greaterThan:创建一个大于
  • greaterThanOrEquals:创建一个大于等于条件
  • lessThan:创建一个小于条件
  • lessThanOrEquals:创建一个小于等于条件
  • like:创建一个Like条件
  • contains:创建一个包含条件(like %value%)
  • startsWith:创建一个开始于条件(like value%)
  • endsWith:创建一个结束于条件(like %value)
  • in:创建一个In条件
  • between:创建一个Between条件
  • isNull:创建一个IsNull条件
  • isNotNull:创建一个IsNotNull条件
  • and:创建一个And条件
  • or:创建一个Or条件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
public class PredicateBuilder<T> {

private final Root<T> root;
private final CriteriaQuery<?> query;
private final CriteriaBuilder criteriaBuilder;

public PredicateBuilder(Root<T> root, CriteriaQuery<?> query,
CriteriaBuilder criteriaBuilder) {
this.root = root;
this.query = query;
this.criteriaBuilder = criteriaBuilder;
}

/**
* 创建一个等于条件
*
* @param attribute 属性名
* @param value 值
* @return Predicate
*/
public Predicate equals(String attribute, Object value) {
return criteriaBuilder.equal(root.get(attribute), value);
}

/**
* 创建一个不等于条件
*
* @param attribute 属性名
* @param value 值
* @return Predicate
*/
public Predicate notEquals(String attribute, Object value) {
return criteriaBuilder.notEqual(root.get(attribute), value);
}

/**
* 创建一个大于条件
*
* @param attribute 属性名
* @param value 值
* @param <Y> 比较值类型
* @return Predicate
*/
public <Y extends Comparable<? super Y>> Predicate greaterThan(String attribute, Y value) {
return criteriaBuilder.greaterThan(root.get(attribute), value);
}

/**
* 创建一个大于等于条件
*
* @param attribute 属性名
* @param value 值
* @param <Y> 比较值类型
* @return Predicate
*/
public <Y extends Comparable<? super Y>> Predicate greaterThanOrEquals(String attribute,
Y value) {
return criteriaBuilder.greaterThanOrEqualTo(root.get(attribute), value);
}

/**
* 创建一个小于条件
*
* @param attribute 属性名
* @param value 值
* @param <Y> 比较值类型
* @return Predicate
*/
public <Y extends Comparable<? super Y>> Predicate lessThan(String attribute, Y value) {
return criteriaBuilder.lessThan(root.get(attribute), value);
}

/**
* 创建一个小于等于条件
*
* @param attribute 属性名
* @param value 值
* @param <Y> 比较值类型
* @return Predicate
*/
public <Y extends Comparable<? super Y>> Predicate lessThanOrEquals(String attribute, Y value) {
return criteriaBuilder.lessThanOrEqualTo(root.get(attribute), value);
}

/**
* 创建一个Like条件
*
* @param attribute 属性名
* @param value 值
* @return Predicate
*/
public Predicate like(String attribute, String value) {
return criteriaBuilder.like(root.get(attribute), value);
}

/**
* 创建一个包含条件(like %value%)
*
* @param attribute 属性名
* @param value 值
* @return Predicate
*/
public Predicate contains(String attribute, String value) {
return criteriaBuilder.like(root.get(attribute), "%" + value + "%");
}

/**
* 创建一个开始于条件(like value%)
*
* @param attribute 属性名
* @param value 值
* @return Predicate
*/
public Predicate startsWith(String attribute, String value) {
return criteriaBuilder.like(root.get(attribute), value + "%");
}

/**
* 创建一个结束于条件(like %value)
*
* @param attribute 属性名
* @param value 值
* @return Predicate
*/
public Predicate endsWith(String attribute, String value) {
return criteriaBuilder.like(root.get(attribute), "%" + value);
}

/**
* 创建一个In条件
*
* @param attribute 属性名
* @param values 值集合
* @return Predicate
*/
public Predicate in(String attribute, Collection<?> values) {
CriteriaBuilder.In<Object> inClause = criteriaBuilder.in(root.get(attribute));
for (Object value : values) {
inClause.value(value);
}
return inClause;
}

/**
* 创建一个Between条件
*
* @param attribute 属性名
* @param start 起始值
* @param end 结束值
* @param <Y> 比较值类型
* @return Predicate
*/
public <Y extends Comparable<? super Y>> Predicate between(String attribute, Y start, Y end) {
return criteriaBuilder.between(root.get(attribute), start, end);
}

/**
* 创建一个IsNull条件
*
* @param attribute 属性名
* @return Predicate
*/
public Predicate isNull(String attribute) {
return criteriaBuilder.isNull(root.get(attribute));
}

/**
* 创建一个IsNotNull条件
*
* @param attribute 属性名
* @return Predicate
*/
public Predicate isNotNull(String attribute) {
return criteriaBuilder.isNotNull(root.get(attribute));
}

/**
* 创建一个And条件
*
* @param predicates Predicate列表
* @return Predicate
*/
public Predicate and(Predicate... predicates) {
return criteriaBuilder.and(predicates);
}

/**
* 创建一个Or条件
*
* @param predicates Predicate列表
* @return Predicate
*/
public Predicate or(Predicate... predicates) {
return criteriaBuilder.or(predicates);
}
}

JpaQueryHelper 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270

public class JpaQueryHelper {

/**
* 创建空的Specification
*
* @param <T> 实体类型
* @return 空的Specification
*/
public static <T> Specification<T> none() {
return (root, query, criteria) -> null;
}

/**
* 创建一个Specification
*
* @param func predicate构建函数
* @param <T> 实体类型
* @return Specification
*/
public static <T> Specification<T> specPredicate(
Function<FilterPredicateBuilder<T>, Predicate> func) {
return (root, query, criteria) -> {
FilterPredicateBuilder<T> builder = new FilterPredicateBuilder<>(root, query, criteria);
return func.apply(builder);
};
}

/**
* 创建一个可选的Specification,仅当条件成立时才应用
*
* @param condition 条件
* @param spec 当条件成立时应用的Specification
* @param <T> 实体类型
* @return Specification
*/
private static <T> Specification<T> optional(boolean condition, Specification<T> spec) {
return condition ? spec : none();
}

/**
* 组合多个Specification,使用AND连接
*
* @param specs Specification列表
* @param <T> 实体类型
* @return 组合后的Specification
*/
@SafeVarargs
public static <T> Specification<T> and(Specification<T>... specs) {
return (root, query, criteria) -> {
List<Predicate> predicates = Arrays.stream(specs)
.filter(Objects::nonNull)
.map(spec -> spec.toPredicate(root, query, criteria))
.filter(Objects::nonNull)
.toList();
return predicates.isEmpty() ? null : criteria.and(predicates.toArray(new Predicate[0]));
};
}

/**
* 组合多个Specification,使用OR连接¬
*
* @param specs Specification列表
* @param <T> 实体类型
* @return 组合后的Specification
*/
@SafeVarargs
public static <T> Specification<T> or(Specification<T>... specs) {
return (root, query, criteria) -> {
List<Predicate> predicates = Arrays.stream(specs)
.filter(Objects::nonNull)
.map(spec -> spec.toPredicate(root, query, criteria))
.filter(Objects::nonNull)
.toList();
return predicates.isEmpty() ? null : criteria.or(predicates.toArray(new Predicate[0]));
};
}

/**
* 创建一个等于条件的Specification
*
* @param attribute 属性名
* @param value 值
* @param <T> 实体类型
* @return Specification
*/
public static <T> Specification<T> equals(String attribute, Object value) {
return optional(value != null, specPredicate(builder -> builder.equals(attribute, value)));
}

/**
* 创建一个不等于条件的Specification
*
* @param attribute 属性名
* @param value 值
* @param <T> 实体类型
* @return Specification
*/
public static <T> Specification<T> notEquals(String attribute, Object value) {
return optional(value != null, specPredicate(builder -> builder.notEquals(attribute, value)));
}

/**
* 创建一个大于条件的Specification
*
* @param attribute 属性名
* @param value 值
* @param <T> 实体类型
* @param <Y> 比较值类型
* @return Specification
*/
public static <T, Y extends Comparable<? super Y>> Specification<T> greaterThan(String attribute,
Y value) {
return optional(value != null, specPredicate(builder -> builder.greaterThan(attribute, value)));
}

/**
* 创建一个大于等于条件的Specification
*
* @param attribute 属性名
* @param value 值
* @param <T> 实体类型
* @param <Y> 比较值类型
* @return Specification
*/
public static <T, Y extends Comparable<? super Y>> Specification<T> greaterThanOrEquals(
String attribute, Y value) {
return optional(value != null,
specPredicate(builder -> builder.greaterThanOrEquals(attribute, value)));
}

/**
* 创建一个小于条件的Specification
*
* @param attribute 属性名
* @param value 值
* @param <T> 实体类型
* @param <Y> 比较值类型
* @return Specification
*/
public static <T, Y extends Comparable<? super Y>> Specification<T> lessThan(String attribute,
Y value) {
return optional(value != null, specPredicate(builder -> builder.lessThan(attribute, value)));
}

/**
* 创建一个小于等于条件的Specification
*
* @param attribute 属性名
* @param value 值
* @param <T> 实体类型
* @param <Y> 比较值类型
* @return Specification
*/
public static <T, Y extends Comparable<? super Y>> Specification<T> lessThanOrEquals(
String attribute, Y value) {
return optional(value != null,
specPredicate(builder -> builder.lessThanOrEquals(attribute, value)));
}

/**
* 创建一个Like条件的Specification
*
* @param attribute 属性名
* @param value 值
* @param <T> 实体类型
* @return Specification
*/
public static <T> Specification<T> like(String attribute, String value) {
return optional(value != null && !value.isEmpty(),
specPredicate(builder -> builder.like(attribute, value)));
}

/**
* 创建一个包含条件的Specification(like %value%)
*
* @param attribute 属性名
* @param value 值
* @param <T> 实体类型
* @return Specification
*/
public static <T> Specification<T> contains(String attribute, String value) {
return optional(value != null && !value.isEmpty(),
specPredicate(builder -> builder.contains(attribute, value)));
}

/**
* 创建一个开始于条件的Specification(like value%)
*
* @param attribute 属性名
* @param value 值
* @param <T> 实体类型
* @return Specification
*/
public static <T> Specification<T> startsWith(String attribute, String value) {
return optional(value != null && !value.isEmpty(),
specPredicate(builder -> builder.startsWith(attribute, value)));
}

/**
* 创建一个结束于条件的Specification(like %value)
*
* @param attribute 属性名
* @param value 值
* @param <T> 实体类型
* @return Specification
*/
public static <T> Specification<T> endsWith(String attribute, String value) {
return optional(value != null && !value.isEmpty(),
specPredicate(builder -> builder.endsWith(attribute, value)));
}

/**
* 创建一个In条件的Specification
*
* @param attribute 属性名
* @param values 值集合
* @param <T> 实体类型
* @return Specification
*/
public static <T> Specification<T> in(String attribute, Collection<?> values) {
return optional(values != null && !values.isEmpty(),
specPredicate(builder -> builder.in(attribute, Objects.requireNonNull(values))));
}

/**
* 创建一个Between条件的Specification
*
* @param attribute 属性名
* @param start 起始值
* @param end 结束值
* @param <T> 实体类型
* @param <Y> 比较值类型
* @return Specification
*/
public static <T, Y extends Comparable<? super Y>> Specification<T> between(String attribute,
Y start, Y end) {
if (start != null && end != null) {
return specPredicate(builder -> builder.between(attribute, start, end));
} else if (start != null) {
return specPredicate(builder -> builder.greaterThanOrEquals(attribute, start));
} else if (end != null) {
return specPredicate(builder -> builder.lessThanOrEquals(attribute, end));
} else {
return none();
}
}

/**
* 创建一个IsNull条件的Specification
*
* @param attribute 属性名
* @param <T> 实体类型
* @return Specification
*/
public static <T> Specification<T> isNull(String attribute) {
return specPredicate(builder -> builder.isNull(attribute));
}

/**
* 创建一个IsNotNull条件的Specification
*
* @param attribute 属性名
* @param <T> 实体类型
* @return Specification
*/
public static <T> Specification<T> isNotNull(String attribute) {
return specPredicate(builder -> builder.isNotNull(attribute));
}
}

示例

1
2
3
4
5
6
7
public List<SaleOrderEntity> getOrderList(SaleOrderRequest request){
Specification<SaleOrderEntity> spec= JpaQueryHelper.and(
JpaQueryHelper.equals("orderId", request.getOrderId()),
JpaQueryHelper.equals("userId", request.getUserId())
);
return saleOrderRepository.findAll(spec);
}

相比于之前的的代码少了很多if条件判断,但这里使用硬编码的写入属性名并不优雅,下面使用进一步简化。

FieldReference 实现

  • SerializableFunction
1
2
3
@FunctionalInterface
public interface SerializableFunction<T, R> extends Function<T, R>, Serializable {
}
  • FieldReferenceFunction
1
2
3
public interface FieldReferenceFunction<T, R> extends SerializableFunction<T, R> {
String name();
}
  • FieldReference
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class FieldReference {

private static final ConcurrentMap<String, String> FIELD_NAME_CACHE = new ConcurrentHashMap<>();

public static <T, R> FieldReferenceFunction<T, R> of(SerializableFunction<T, R> function) {
return new FieldReferenceFunction<>() {
@Override
public R apply(T t) {
return function.apply(t);
}

@Override
public String name() {
return extractFieldName(function);
}
};
}

private static String extractFieldName(SerializableFunction<?, ?> function) {
String key = function.getClass().getName();
return FIELD_NAME_CACHE.computeIfAbsent(key, k -> {
try {
Method writeReplace = function.getClass().getDeclaredMethod("writeReplace");
writeReplace.setAccessible(true);
SerializedLambda lambda = (SerializedLambda) writeReplace.invoke(function);
String methodName = lambda.getImplMethodName();

if (methodName.startsWith("get") && methodName.length() > 3) {
return Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
} else if (methodName.startsWith("is") && methodName.length() > 2) {
return Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3);
} else {
return methodName;
}

} catch (ReflectiveOperationException e) {
throw new RuntimeException(
"Failed to extract field name from method reference. Make sure you're using a method reference (::)",
e);
}
});
}
}

最终示例

1
2
3
4
5
6
7
public List<SaleOrderEntity> getOrderList(SaleOrderRequest request){
Specification<SaleOrderEntity> spec = JpaQueryHelper.and(
JpaQueryHelper.equals(FieldReference.of(SaleOrderEntity::getOrderId).name(), request.getOrderId()),
JpaQueryHelper.equals(FieldReference.of(SaleOrderEntity::getUserId).name(), request.getUserId())
);
return saleOrderRepository.findAll(spec);
}

总结

上面的实现方式不仅提高了代码的灵活性和可维护性,还增强了系统的扩展性。Specification、CriteriaBuilder 和 Predicate 是 JPA 提供的强大工具,熟练掌握它们的使用可以极大地提升我们的开发效率。


Spring-data-jpa动态条件查询简化
http://example.com/2025/06/11/spring-data-jpa动态条件查询简化/
作者
ares
发布于
2025年6月11日
许可协议