1
16 package org.springframework.data.jpa.repository.query;
17
18 import static org.springframework.data.jpa.repository.query.QueryUtils.*;
19 import static org.springframework.data.repository.query.parser.Part.Type.*;
20
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.stream.Collectors;
26
27 import javax.persistence.criteria.CriteriaBuilder;
28 import javax.persistence.criteria.CriteriaQuery;
29 import javax.persistence.criteria.Expression;
30 import javax.persistence.criteria.ParameterExpression;
31 import javax.persistence.criteria.Path;
32 import javax.persistence.criteria.Predicate;
33 import javax.persistence.criteria.Root;
34 import javax.persistence.criteria.Selection;
35 import javax.persistence.metamodel.SingularAttribute;
36
37 import org.springframework.data.domain.Sort;
38 import org.springframework.data.jpa.repository.query.ParameterMetadataProvider.ParameterMetadata;
39 import org.springframework.data.mapping.PropertyPath;
40 import org.springframework.data.repository.query.ReturnedType;
41 import org.springframework.data.repository.query.parser.AbstractQueryCreator;
42 import org.springframework.data.repository.query.parser.Part;
43 import org.springframework.data.repository.query.parser.Part.Type;
44 import org.springframework.data.repository.query.parser.PartTree;
45 import org.springframework.lang.Nullable;
46 import org.springframework.util.Assert;
47
48
59 public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<? extends Object>, Predicate> {
60
61 private final CriteriaBuilder builder;
62 private final Root<?> root;
63 private final CriteriaQuery<? extends Object> query;
64 private final ParameterMetadataProvider provider;
65 private final ReturnedType returnedType;
66 private final PartTree tree;
67 private final EscapeCharacter escape;
68
69
77 public JpaQueryCreator(PartTree tree, ReturnedType type, CriteriaBuilder builder,
78 ParameterMetadataProvider provider) {
79
80 super(tree);
81 this.tree = tree;
82
83 CriteriaQuery<?> criteriaQuery = createCriteriaQuery(builder, type);
84
85 this.builder = builder;
86 this.query = criteriaQuery.distinct(tree.isDistinct());
87 this.root = query.from(type.getDomainType());
88 this.provider = provider;
89 this.returnedType = type;
90 this.escape = provider.getEscape();
91 }
92
93
100 protected CriteriaQuery<? extends Object> createCriteriaQuery(CriteriaBuilder builder, ReturnedType type) {
101
102 Class<?> typeToRead = type.getTypeToRead();
103
104 return typeToRead == null || tree.isExistsProjection() ? builder.createTupleQuery()
105 : builder.createQuery(typeToRead);
106 }
107
108
113 public List<ParameterMetadata<?>> getParameterExpressions() {
114 return provider.getExpressions();
115 }
116
117
121 @Override
122 protected Predicate create(Part part, Iterator<Object> iterator) {
123
124 return toPredicate(part, root);
125 }
126
127
131 @Override
132 protected Predicate and(Part part, Predicate base, Iterator<Object> iterator) {
133 return builder.and(base, toPredicate(part, root));
134 }
135
136
140 @Override
141 protected Predicate or(Predicate base, Predicate predicate) {
142 return builder.or(base, predicate);
143 }
144
145
150 @Override
151 protected final CriteriaQuery<? extends Object> complete(Predicate predicate, Sort sort) {
152 return complete(predicate, sort, query, builder, root);
153 }
154
155
165 @SuppressWarnings({ "unchecked", "rawtypes" })
166 protected CriteriaQuery<? extends Object> complete(@Nullable Predicate predicate, Sort sort,
167 CriteriaQuery<? extends Object> query, CriteriaBuilder builder, Root<?> root) {
168
169 if (returnedType.needsCustomConstruction()) {
170
171 List<Selection<?>> selections = new ArrayList<>();
172
173 for (String property : returnedType.getInputProperties()) {
174
175 PropertyPath path = PropertyPath.from(property, returnedType.getDomainType());
176 selections.add(toExpressionRecursively(root, path, true).alias(property));
177 }
178
179 query = query.multiselect(selections);
180
181 } else if (tree.isExistsProjection()) {
182
183 if (root.getModel().hasSingleIdAttribute()) {
184
185 SingularAttribute<?, ?> id = root.getModel().getId(root.getModel().getIdType().getJavaType());
186 query = query.multiselect(root.get((SingularAttribute) id).alias(id.getName()));
187
188 } else {
189
190 query = query.multiselect(root.getModel().getIdClassAttributes().stream()
191 .map(it -> (Selection<?>) root.get((SingularAttribute) it).alias(it.getName()))
192 .collect(Collectors.toList()));
193 }
194
195 } else {
196 query = query.select((Root) root);
197 }
198
199 CriteriaQuery<? extends Object> select = query.orderBy(QueryUtils.toOrders(sort, root, builder));
200 return predicate == null ? select : select.where(predicate);
201 }
202
203
210 private Predicate toPredicate(Part part, Root<?> root) {
211 return new PredicateBuilder(part, root).build();
212 }
213
214
220 @SuppressWarnings({ "unchecked", "rawtypes" })
221 private class PredicateBuilder {
222
223 private final Part part;
224 private final Root<?> root;
225
226
232 public PredicateBuilder(Part part, Root<?> root) {
233
234 Assert.notNull(part, "Part must not be null!");
235 Assert.notNull(root, "Root must not be null!");
236 this.part = part;
237 this.root = root;
238 }
239
240
245 public Predicate build() {
246
247 PropertyPath property = part.getProperty();
248 Type type = part.getType();
249
250 switch (type) {
251 case BETWEEN:
252 ParameterMetadata<Comparable> first = provider.next(part);
253 ParameterMetadata<Comparable> second = provider.next(part);
254 return builder.between(getComparablePath(root, part), first.getExpression(), second.getExpression());
255 case AFTER:
256 case GREATER_THAN:
257 return builder.greaterThan(getComparablePath(root, part),
258 provider.next(part, Comparable.class).getExpression());
259 case GREATER_THAN_EQUAL:
260 return builder.greaterThanOrEqualTo(getComparablePath(root, part),
261 provider.next(part, Comparable.class).getExpression());
262 case BEFORE:
263 case LESS_THAN:
264 return builder.lessThan(getComparablePath(root, part), provider.next(part, Comparable.class).getExpression());
265 case LESS_THAN_EQUAL:
266 return builder.lessThanOrEqualTo(getComparablePath(root, part),
267 provider.next(part, Comparable.class).getExpression());
268 case IS_NULL:
269 return getTypedPath(root, part).isNull();
270 case IS_NOT_NULL:
271 return getTypedPath(root, part).isNotNull();
272 case NOT_IN:
273
274 return upperIfIgnoreCase(getTypedPath(root, part)).in((Expression<Collection<?>>) provider.next(part, Collection.class).getExpression()).not();
275 case IN:
276
277 return upperIfIgnoreCase(getTypedPath(root, part)).in((Expression<Collection<?>>) provider.next(part, Collection.class).getExpression());
278 case STARTING_WITH:
279 case ENDING_WITH:
280 case CONTAINING:
281 case NOT_CONTAINING:
282
283 if (property.getLeafProperty().isCollection()) {
284
285 Expression<Collection<Object>> propertyExpression = traversePath(root, property);
286 ParameterExpression<Object> parameterExpression = provider.next(part).getExpression();
287
288
289 return type.equals(NOT_CONTAINING) ? isNotMember(builder, parameterExpression, propertyExpression)
290 : isMember(builder, parameterExpression, propertyExpression);
291 }
292
293 case LIKE:
294 case NOT_LIKE:
295 Expression<String> stringPath = getTypedPath(root, part);
296 Expression<String> propertyExpression = upperIfIgnoreCase(stringPath);
297 Expression<String> parameterExpression = upperIfIgnoreCase(provider.next(part, String.class).getExpression());
298 Predicate like = builder.like(propertyExpression, parameterExpression, escape.getEscapeCharacter());
299 return type.equals(NOT_LIKE) || type.equals(NOT_CONTAINING) ? like.not() : like;
300 case TRUE:
301 Expression<Boolean> truePath = getTypedPath(root, part);
302 return builder.isTrue(truePath);
303 case FALSE:
304 Expression<Boolean> falsePath = getTypedPath(root, part);
305 return builder.isFalse(falsePath);
306 case SIMPLE_PROPERTY:
307 ParameterMetadata<Object> expression = provider.next(part);
308 Expression<Object> path = getTypedPath(root, part);
309 return expression.isIsNullParameter() ? path.isNull()
310 : builder.equal(upperIfIgnoreCase(path), upperIfIgnoreCase(expression.getExpression()));
311 case NEGATING_SIMPLE_PROPERTY:
312 return builder.notEqual(upperIfIgnoreCase(getTypedPath(root, part)),
313 upperIfIgnoreCase(provider.next(part).getExpression()));
314 case IS_EMPTY:
315 case IS_NOT_EMPTY:
316
317 if (!property.getLeafProperty().isCollection()) {
318 throw new IllegalArgumentException("IsEmpty / IsNotEmpty can only be used on collection properties!");
319 }
320
321 Expression<Collection<Object>> collectionPath = traversePath(root, property);
322 return type.equals(IS_NOT_EMPTY) ? builder.isNotEmpty(collectionPath) : builder.isEmpty(collectionPath);
323
324 default:
325 throw new IllegalArgumentException("Unsupported keyword " + type);
326 }
327 }
328
329 private <T> Predicate isMember(CriteriaBuilder builder, Expression<T> parameter,
330 Expression<Collection<T>> property) {
331 return builder.isMember(parameter, property);
332 }
333
334 private <T> Predicate isNotMember(CriteriaBuilder builder, Expression<T> parameter,
335 Expression<Collection<T>> property) {
336 return builder.isNotMember(parameter, property);
337 }
338
339
346 private <T> Expression<T> upperIfIgnoreCase(Expression<? extends T> expression) {
347
348 switch (part.shouldIgnoreCase()) {
349
350 case ALWAYS:
351
352 Assert.state(canUpperCase(expression), "Unable to ignore case of " + expression.getJavaType().getName()
353 + " types, the property '" + part.getProperty().getSegment() + "' must reference a String");
354 return (Expression<T>) builder.upper((Expression<String>) expression);
355
356 case WHEN_POSSIBLE:
357
358 if (canUpperCase(expression)) {
359 return (Expression<T>) builder.upper((Expression<String>) expression);
360 }
361
362 case NEVER:
363 default:
364
365 return (Expression<T>) expression;
366 }
367 }
368
369 private boolean canUpperCase(Expression<?> expression) {
370 return String.class.equals(expression.getJavaType());
371 }
372
373
380 private Expression<? extends Comparable> getComparablePath(Root<?> root, Part part) {
381 return getTypedPath(root, part);
382 }
383
384 private <T> Expression<T> getTypedPath(Root<?> root, Part part) {
385 return toExpressionRecursively(root, part.getProperty());
386 }
387
388 private <T> Expression<T> traversePath(Path<?> root, PropertyPath path) {
389
390 Path<Object> result = root.get(path.getSegment());
391 return (Expression<T>) (path.hasNext() ? traversePath(result, path.next()) : result);
392 }
393 }
394 }
395