1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.data.jpa;
17
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.List;
21
22 import javax.persistence.EntityManager;
23 import javax.persistence.TypedQuery;
24 import javax.persistence.criteria.CriteriaBuilder;
25 import javax.persistence.criteria.CriteriaQuery;
26 import javax.persistence.criteria.Path;
27 import javax.persistence.criteria.Predicate;
28 import javax.persistence.criteria.Root;
29
30 import org.apache.commons.lang.StringUtils;
31 import org.kuali.rice.core.api.criteria.QueryByCriteria;
32
33
34
35
36
37
38 class NativeJpaQueryTranslator extends QueryTranslatorBase<NativeJpaQueryTranslator.TranslationContext, TypedQuery> {
39
40
41
42
43 protected static final String[] LOOKUP_WILDCARDS = { "*", "?" };
44
45
46
47
48 protected static final String[] ESCAPED_LOOKUP_WILDCARDS = { "\\*", "\\?" };
49
50
51
52
53 protected static final char[] JPQL_WILDCARDS = { '%', '_' };
54
55
56
57
58 protected EntityManager entityManager;
59
60
61
62
63 public static class TranslationContext {
64
65
66
67
68 CriteriaBuilder builder;
69
70
71
72
73 CriteriaQuery query;
74
75
76
77
78 Root root;
79
80
81
82
83 List<Predicate> predicates = new ArrayList<Predicate>();
84
85
86
87
88
89
90
91 TranslationContext(EntityManager entityManager, Class queryClass) {
92 builder = entityManager.getCriteriaBuilder();
93 query = builder.createQuery(queryClass);
94
95
96 root = query.from(query.getResultType());
97 }
98
99
100
101
102
103
104 TranslationContext(TranslationContext parent) {
105 builder = parent.builder;
106 query = parent.query;
107 root = parent.root;
108 }
109
110
111
112
113
114
115 void addPredicate(Predicate predicate) {
116 predicates.add(predicate);
117 }
118
119
120
121
122
123
124 void and(TranslationContext predicate) {
125 addPredicate(predicate.getCriteriaPredicate());
126 }
127
128
129
130
131
132
133 void or(TranslationContext predicate) {
134 List<Predicate> newpredicates = new ArrayList<Predicate>();
135
136
137 Predicate criteriaPredicate = getCriteriaPredicate();
138 Predicate orPredicate = null;
139 if(criteriaPredicate != null){
140 orPredicate = builder.or(new Predicate[] { predicate.getCriteriaPredicate(), getCriteriaPredicate() });
141 } else {
142 orPredicate = builder.or(predicate.getCriteriaPredicate());
143 }
144 newpredicates.add(orPredicate);
145 predicates = newpredicates;
146 }
147
148
149
150
151
152
153 Predicate getCriteriaPredicate() {
154 if (predicates.size() == 1) {
155 return predicates.get(0);
156 } else if(predicates.size() > 1){
157 return builder.and(predicates.toArray(new Predicate[predicates.size()]));
158 } else {
159 return null;
160 }
161 }
162
163
164
165
166
167
168
169 Path attr(String attr) {
170 if (StringUtils.isBlank(attr)) {
171 throw new IllegalArgumentException("Encountered an empty attribute path");
172 }
173
174 Path path = root;
175
176
177 String[] attrArray = attr.split("\\.");
178 for (String attrElement : attrArray) {
179 if (StringUtils.isBlank(attrElement)) {
180 throw new IllegalArgumentException("Encountered an empty path element in property path: " + attr);
181 }
182 path = path.get(attrElement);
183 }
184 return path;
185 }
186 }
187
188
189
190
191
192
193 public NativeJpaQueryTranslator(EntityManager entityManager) {
194 this.entityManager = entityManager;
195 }
196
197
198
199
200 @Override
201 public TypedQuery createQuery(Class queryClazz, TranslationContext criteria) {
202 CriteriaQuery jpaQuery = criteria.query;
203
204 if (!criteria.predicates.isEmpty()) {
205 jpaQuery = jpaQuery.where(criteria.getCriteriaPredicate());
206 }
207 return entityManager.createQuery(jpaQuery);
208 }
209
210
211
212
213 @Override
214 protected TranslationContext createCriteria(Class queryClazz) {
215 return new TranslationContext(entityManager, queryClazz);
216 }
217
218
219
220
221 @Override
222 protected TranslationContext createInnerCriteria(TranslationContext parent) {
223
224
225 return new TranslationContext(parent);
226 }
227
228
229
230
231 @Override
232 public void convertQueryFlags(QueryByCriteria qbc, TypedQuery query) {
233 final int startAtIndex = qbc.getStartAtIndex() != null ? qbc.getStartAtIndex() : 0;
234
235 query.setFirstResult(startAtIndex);
236
237 if (qbc.getMaxResults() != null) {
238
239
240 query.setMaxResults(qbc.getMaxResults());
241 }
242 }
243
244
245
246
247 @Override
248 protected void addAnd(TranslationContext criteria, TranslationContext inner) {
249 criteria.and(inner);
250 }
251
252
253
254
255 @Override
256 protected void addNotNull(TranslationContext criteria, String propertyPath) {
257 criteria.addPredicate(criteria.builder.isNotNull(criteria.attr(propertyPath)));
258 }
259
260
261
262
263 @Override
264 protected void addIsNull(TranslationContext criteria, String propertyPath) {
265 criteria.addPredicate(criteria.builder.isNull(criteria.attr(propertyPath)));
266 }
267
268
269
270
271 @Override
272 protected void addEqualTo(TranslationContext criteria, String propertyPath, Object value) {
273 criteria.addPredicate(criteria.builder.equal(criteria.attr(propertyPath), value));
274 }
275
276
277
278
279 @Override
280 protected void addEqualToIgnoreCase(TranslationContext criteria, String propertyPath, String value) {
281 criteria.addPredicate(criteria.builder.equal(criteria.builder.upper(criteria.attr(propertyPath)), value.toUpperCase()));
282 }
283
284
285
286
287 @Override
288 protected void addGreaterOrEqualTo(TranslationContext criteria, String propertyPath, Object value) {
289 criteria.addPredicate(criteria.builder.greaterThanOrEqualTo(criteria.attr(propertyPath), (Comparable) value));
290 }
291
292
293
294
295 @Override
296 protected void addGreaterThan(TranslationContext criteria, String propertyPath, Object value) {
297 criteria.addPredicate(criteria.builder.greaterThan(criteria.attr(propertyPath), (Comparable) value));
298 }
299
300
301
302
303 @Override
304 protected void addLessOrEqualTo(TranslationContext criteria, String propertyPath, Object value) {
305 criteria.addPredicate(criteria.builder.lessThanOrEqualTo(criteria.attr(propertyPath), (Comparable) value));
306 }
307
308
309
310
311 @Override
312 protected void addLessThan(TranslationContext criteria, String propertyPath, Object value) {
313 criteria.addPredicate(criteria.builder.lessThan(criteria.attr(propertyPath), (Comparable) value));
314 }
315
316
317
318
319 @Override
320 protected void addLike(TranslationContext criteria, String propertyPath, Object value) {
321
322 criteria.addPredicate(criteria.builder.like(criteria.attr(propertyPath), fixSearchPattern(value.toString())));
323 }
324
325
326
327
328 @Override
329 protected void addLikeIgnoreCase(TranslationContext criteria, String propertyPath, String value){
330 criteria.addPredicate(criteria.builder.like(criteria.builder.upper(criteria.attr(propertyPath)),
331 fixSearchPattern(value.toUpperCase())));
332 }
333
334
335
336
337
338
339
340
341
342
343
344 protected String fixSearchPattern(String value) {
345 StringBuilder fixedPattern = new StringBuilder(value);
346
347 for (int i = 0; i < LOOKUP_WILDCARDS.length; i++) {
348 String lookupWildcard = LOOKUP_WILDCARDS[i];
349 String escapedLookupWildcard = ESCAPED_LOOKUP_WILDCARDS[i];
350 char jpqlWildcard = JPQL_WILDCARDS[i];
351 int wildcardIndex = fixedPattern.indexOf(lookupWildcard);
352 int escapedWildcardIndex = fixedPattern.indexOf(escapedLookupWildcard);
353 while (wildcardIndex != -1) {
354 if (wildcardIndex == 0 || escapedWildcardIndex != wildcardIndex - 1) {
355 fixedPattern.setCharAt(wildcardIndex, jpqlWildcard);
356 wildcardIndex = fixedPattern.indexOf(lookupWildcard, wildcardIndex);
357 } else {
358 fixedPattern.replace(escapedWildcardIndex, wildcardIndex + 1, lookupWildcard);
359 wildcardIndex = fixedPattern.indexOf(lookupWildcard, wildcardIndex);
360 escapedWildcardIndex = fixedPattern.indexOf(escapedLookupWildcard, wildcardIndex);
361 }
362 }
363 }
364 return fixedPattern.toString();
365 }
366
367
368
369
370 @Override
371 protected void addNotEqualTo(TranslationContext criteria, String propertyPath, Object value) {
372 criteria.addPredicate(criteria.builder.notEqual(criteria.attr(propertyPath), value));
373 }
374
375
376
377
378 @Override
379 protected void addNotEqualToIgnoreCase(TranslationContext criteria, String propertyPath, String value) {
380 criteria.addPredicate(criteria.builder.notEqual(criteria.builder.upper(criteria.attr(propertyPath)), value.toUpperCase()));
381 }
382
383
384
385
386 @Override
387 protected void addNotLike(TranslationContext criteria, String propertyPath, Object value) {
388
389 criteria.addPredicate(criteria.builder.notLike(criteria.attr(propertyPath), fixSearchPattern(value.toString())));
390 }
391
392
393
394
395 @Override
396 protected void addIn(TranslationContext criteria, String propertyPath, Collection values) {
397 criteria.addPredicate(criteria.attr(propertyPath).in(values));
398 }
399
400
401
402
403 @Override
404 protected void addNotIn(TranslationContext criteria, String propertyPath, Collection values) {
405 criteria.addPredicate(criteria.builder.not(criteria.attr(propertyPath).in(values)));
406 }
407
408
409
410
411 @Override
412 protected void addOr(TranslationContext criteria, TranslationContext inner) {
413 criteria.or(inner);
414 }
415
416
417
418
419 @Override
420 protected String genUpperFunc(String pp) {
421 throw new IllegalStateException("genUpperFunc should not have been invoked for NativeJpaQueryTranslator");
422 }
423
424 }