1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package ch.elca.el4j.services.persistence.jpa.criteria;
18
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collection;
22 import java.util.List;
23
24 import javax.persistence.EntityManager;
25 import javax.persistence.NonUniqueResultException;
26 import javax.persistence.Query;
27
28 import org.apache.commons.collections.CollectionUtils;
29 import org.apache.commons.lang.ArrayUtils;
30 import org.apache.commons.lang.StringUtils;
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 public final class QueryBuilder implements Expression, SelectQuery, CountQuery {
75
76 private static final String PARAM_KEY = "{p}";
77 private static final String PARAM = "param";
78
79
80 private static final String AND = " AND ";
81
82
83 private static final String OR = " OR ";
84
85
86 private static final String COMMA = " , ";
87
88
89 private static final String INNER_JOIN = "join";
90
91
92 private static final String LEFT_JOIN = "left outer join";
93
94
95 private static final String RIGHT_JOIN = "right outer join";
96
97
98 private static final String DB_TRUE_VALUE = "T";
99
100
101 private static final String DB_FALSE_VALUE = "F";
102
103
104
105
106 private String selectQuery;
107
108
109
110
111 private List<String> froms = new ArrayList<String>();
112
113
114
115
116 private List<String> orderBy = new ArrayList<String>();
117
118
119
120
121 private List<String> joins = new ArrayList<String>();
122
123
124
125
126 private List<QueryBuilder> subUnionQueryBuilders = new ArrayList<QueryBuilder>();
127
128
129
130
131 private String where;
132
133
134
135
136 private boolean completed = false;
137
138
139
140
141 private List<Object> bodyParameters = new ArrayList<Object>();
142
143
144
145
146 private List<Object> selectParameters = new ArrayList<Object>();
147
148
149
150
151 private Query query;
152
153
154
155 private QueryBuilder(String... select) {
156 this.selectQuery = StringUtils.join(select, COMMA);
157 }
158
159
160
161 private QueryBuilder(QueryBuilder... query) {
162
163 }
164
165
166
167
168
169
170 public List<Object> getSelectParameters() {
171 return selectParameters;
172 }
173
174
175
176
177
178
179 public static QueryBuilder select(String... select) {
180 return new QueryBuilder(select);
181 }
182
183
184
185
186 public QueryBuilder addSelectParameter(Object... parameters) {
187 for (Object parameter : parameters) {
188 if (isParameterAccepted(parameter)) {
189 selectParameters.add(parameter);
190 } else {
191 throw new RuntimeException("Parameters for select statement must be not null or not empty.");
192 }
193 }
194 return this;
195 }
196
197
198
199
200
201
202 public QueryBuilder from(Class<?>... entityClasses) {
203 String[] classNames = new String[entityClasses.length];
204 for (int i = 0; i < entityClasses.length; i++) {
205 classNames[i] = entityClasses[i].getSimpleName();
206 }
207
208 return from(classNames);
209 }
210
211
212
213
214
215
216 public QueryBuilder from(String... from) {
217 froms.addAll(Arrays.asList(from));
218 return this;
219 }
220
221
222
223
224
225
226
227
228 public QueryBuilder fromIf(String fromExp, boolean cond) {
229 if (cond) {
230 return from(fromExp);
231 }
232 return this;
233 }
234
235
236
237
238 private QueryBuilder join(String join, String type, String on, Object... params) {
239 if (ArrayUtils.isEmpty(params)) {
240 doJoin(join, type, on);
241 } else {
242
243 boolean allParamsAccepted = true;
244 for (Object param : params) {
245 if (!isParameterAccepted(param)) {
246 allParamsAccepted = false;
247 break;
248 }
249 }
250 if (allParamsAccepted) {
251 doJoin(join, type, on);
252 bodyParameters.addAll(Arrays.asList(params));
253 }
254 }
255
256 return this;
257 }
258
259
260
261
262
263
264
265
266
267 public QueryBuilder innerJoin(String join, String on, Object... params) {
268 return join(join, INNER_JOIN, on, params);
269 }
270
271
272
273
274
275
276
277
278
279 public QueryBuilder innerJoin(String join, String on) {
280 return innerJoin(join, on, new Object[] {});
281 }
282
283
284
285
286
287
288
289
290
291 public QueryBuilder leftJoin(String join, String on, Object... params) {
292 return join(join, LEFT_JOIN, on, params);
293 }
294
295 public QueryBuilder leftJoin(String join, String on) {
296 return leftJoin(join, on, new Object[] {});
297 }
298
299 public QueryBuilder union(QueryBuilder builder) {
300 if (builder != null) {
301 subUnionQueryBuilders.add(builder);
302 }
303 return this;
304 }
305
306
307
308
309
310
311
312
313
314 public QueryBuilder rightJoin(String join, String on, Object... params) {
315 return join(join, RIGHT_JOIN, on, params);
316 }
317
318 public QueryBuilder rightJoin(String join, String on) {
319 return rightJoin(join, on, null, new Object[] {});
320 }
321
322
323
324
325
326
327
328
329 private void doJoin(String join, String type, String on) {
330 String joinStr = " " + type + " " + join;
331 if (StringUtils.isNotBlank(on)) {
332 joinStr += " on (" + on + ")";
333 }
334 joins.add(joinStr);
335 }
336
337
338
339
340
341
342
343
344
345
346 public QueryBuilder joinIf(String join, String on, boolean cond, Object... params) {
347 if (cond) {
348 return join(join, INNER_JOIN, on, params);
349 }
350 return this;
351 }
352
353
354
355
356
357
358
359
360 public QueryBuilder join(String join) {
361 doJoin(join, INNER_JOIN, null);
362 return this;
363 }
364
365
366
367
368
369
370
371
372 public QueryBuilder joinIf(String join, boolean cond) {
373 if (cond) {
374 join(join);
375 }
376 return this;
377 }
378
379
380
381
382
383 public ConditionList<QueryBuilder> startAnd() {
384 return new ConditionList<QueryBuilder>(QueryBuilder.this, AND);
385 }
386
387
388
389
390
391 public ConditionList<QueryBuilder> startOr() {
392 return new ConditionList<QueryBuilder>(QueryBuilder.this, OR);
393 }
394
395
396
397
398
399
400
401 public QueryBuilder endBuilder() {
402 completed = true;
403 return this;
404 }
405
406
407
408
409
410
411
412
413
414 public QueryBuilder orderBy(SortOrder sortOrder, String orderBy) {
415 if (StringUtils.isNotBlank(orderBy) && !isAlreadyOrderBy(orderBy)) {
416 this.orderBy.add(orderBy + " " + sortOrder);
417 }
418 return this;
419 }
420
421
422
423
424
425
426
427
428 private boolean isAlreadyOrderBy(final String column) {
429 for (String order : orderBy) {
430 if (StringUtils.startsWithIgnoreCase(order, column + " ")) {
431 return true;
432 }
433 }
434 return false;
435 }
436
437
438
439
440
441
442
443
444
445
446 public QueryBuilder orderByIf(SortOrder sortOrder, String orderBy, boolean cond) {
447 if (cond) {
448 return orderBy(sortOrder, orderBy);
449 }
450
451 return this;
452 }
453
454
455
456
457
458
459
460 public class ConditionList<T extends Expression> implements Expression {
461
462
463
464
465
466 protected T parent;
467
468
469
470
471 private String operation;
472
473
474
475
476 private List<String> conds = new ArrayList<String>();
477
478
479
480
481
482
483 public ConditionList(T parent, String operation) {
484 this.parent = parent;
485 this.operation = operation;
486 }
487
488
489
490
491
492
493 public ConditionList<T> ifCond(String predicate) {
494 conds.add(predicate);
495 return this;
496 }
497
498
499
500
501
502
503
504 public ConditionList<T> ifCond(String queryPredicate, boolean cond) {
505 if (cond) {
506 return ifCond(queryPredicate);
507 }
508 return this;
509 }
510
511 public ConditionList<T> ifNotNull(String condition, Object parameter) {
512 if (isParameterAccepted(parameter)) {
513 getBodyParameters().add(parameter);
514 conds.add(condition);
515 }
516 return this;
517 }
518
519
520
521
522
523
524
525
526 public ConditionList<T> ifNotNull(String queryPredicate, Object parameter, boolean cond) {
527 if (cond) {
528 return ifNotNull(queryPredicate, parameter);
529 }
530 return this;
531 }
532
533
534
535
536
537
538
539 public ConditionList<T> existIf(QueryBuilder subQueryBuilder, boolean cond) {
540 if (cond) {
541 return exist(subQueryBuilder);
542 }
543 return this;
544 }
545
546
547
548
549
550
551 public ConditionList<T> existIfNotNull(QueryBuilder subQueryBuilder, Object obj) {
552 return existIf(subQueryBuilder, obj != null);
553 }
554
555
556
557
558
559
560 public ConditionList<T> exist(QueryBuilder subQueryBuilder) {
561 if (!subQueryBuilder.completed) {
562 throw new RuntimeException("Sub query must be completed first before being set in");
563 }
564 bodyParameters.addAll(subQueryBuilder.getSelectParameters());
565 bodyParameters.addAll(subQueryBuilder.getBodyParameters());
566 conds.add(" exists (" + subQueryBuilder.getQuerySelectStr() + ")");
567
568 return this;
569 }
570
571
572
573
574
575 public T end() {
576 if (!conds.isEmpty()) {
577 parent.append(StringUtils.join(conds, operation));
578 }
579 return parent;
580 }
581
582
583
584
585
586 public ConditionList<ConditionList<T>> startAnd() {
587 return new ConditionList<ConditionList<T>>(this, AND);
588 }
589
590
591
592
593
594 public ConditionList<ConditionList<T>> startOr() {
595 return new ConditionList<ConditionList<T>>(this, OR);
596 }
597
598
599
600
601 @Override
602 public void append(String query) {
603 if (!query.isEmpty()) {
604 conds.add("(" + query + ")");
605 }
606 }
607 }
608
609
610
611
612
613
614 @SuppressWarnings("unchecked")
615 private boolean isParameterAccepted(Object parameter) {
616 if (parameter == null) {
617 return false;
618 }
619 boolean paramAccepted = true;
620 if (parameter instanceof String) {
621 paramAccepted = (StringUtils.isNotBlank((String) parameter));
622 } else if (parameter instanceof Collection) {
623 paramAccepted = (CollectionUtils.isNotEmpty((Collection) parameter));
624 }
625 return paramAccepted;
626 }
627
628
629
630
631
632
633
634 public SelectQuery applySelect(EntityManager entityManager) {
635 createJpaQuery(getQuerySelectStr(), entityManager, false, null, false);
636 return this;
637 }
638
639
640
641
642
643
644
645
646 public SelectQuery applySelect(EntityManager entityManager, Class<?> resultClass) {
647 createJpaQuery(getQuerySelectStr(), entityManager, false, resultClass, false);
648 return this;
649 }
650
651
652
653
654
655
656
657 public CountQuery applyCount(EntityManager entityManager) {
658 createJpaQuery(getQueryCountStr(), entityManager, false, null, true);
659
660 return this;
661 }
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695 private void createJpaQuery(String queryString, EntityManager entityManager, boolean nativeQuery,
696 Class<?> resultClass, boolean isCountQuery) {
697 initQuery(queryString, entityManager, nativeQuery, resultClass);
698 applyParameters(getAllParameters(queryString, isCountQuery), nativeQuery);
699 }
700
701
702
703
704
705
706
707
708 private void initQuery(String queryString, EntityManager entityManager, boolean nativeQuery, Class<?> resultClass) {
709 String queryStr = positionizeParameters(queryString);
710 if (nativeQuery) {
711 if (resultClass != null) {
712 query = entityManager.createNativeQuery(queryStr, resultClass);
713 } else {
714 query = entityManager.createNativeQuery(queryStr);
715 }
716 } else {
717 query = entityManager.createQuery(queryStr);
718 }
719 }
720
721 private List<Object> getAllParameters(String queryString, boolean isCountQuery) {
722
723 List<Object> allParameters = new ArrayList<Object>();
724 if (isCountQuery) {
725 allParameters.addAll(selectParameters);
726 }
727 for (QueryBuilder unionQuery : this.subUnionQueryBuilders) {
728 allParameters.addAll(unionQuery.getSelectParameters());
729 allParameters.addAll(unionQuery.getBodyParameters());
730 }
731 allParameters.addAll(bodyParameters);
732 return allParameters;
733 }
734
735 private void applyParameters(List<Object> allParameters, boolean nativeQuery) {
736 for (int i = 0; i < allParameters.size(); i++) {
737 Object param = null;
738 Object parameter = allParameters.get(i);
739 if (nativeQuery && parameter instanceof Boolean) {
740 param = ((Boolean) parameter).booleanValue() ? DB_TRUE_VALUE : DB_FALSE_VALUE;
741 } else {
742 param = parameter;
743 }
744 query.setParameter(PARAM + i, param);
745 }
746 }
747
748 private String positionizeParameters(String queryString) {
749 if (StringUtils.isBlank(queryString)) {
750 return queryString;
751 }
752 String result = queryString;
753 int paramCount = 0;
754 int curIdx = result.indexOf(PARAM_KEY);
755 while (curIdx >= 0) {
756 result = StringUtils.replaceOnce(result, PARAM_KEY, ":" + PARAM + String.valueOf(paramCount));
757 paramCount++;
758 curIdx = result.indexOf(PARAM_KEY, curIdx + 1);
759 }
760
761 return result;
762 }
763
764
765
766
767
768
769
770 public void setParameters(List<Object> parameters) {
771 this.bodyParameters = parameters;
772 }
773
774
775
776
777
778
779
780
781
782
783 @SuppressWarnings("unchecked")
784 public <T> List<T> getResultList(Class<T> clazz, int firstRow, int maxNumOfRows) {
785 query.setFirstResult(firstRow);
786 query.setMaxResults(maxNumOfRows);
787 return (List<T>) query.getResultList();
788 }
789
790
791
792
793
794
795
796 @Override
797 public List<?> getResultList(int firstRow, int maxNumOfRows) {
798 query.setFirstResult(firstRow);
799 query.setMaxResults(maxNumOfRows);
800 return query.getResultList();
801 }
802
803
804
805
806
807
808
809 @Override
810 @SuppressWarnings("unchecked")
811 public <T> List<T> getResultList(Class<T> clazz) {
812 return (List<T>) query.getResultList();
813 }
814
815
816
817
818 public List<?> getRawResultList() {
819 return query.getResultList();
820 }
821
822
823
824
825
826
827 @Override
828 public <T> T getSingleResult(Class<T> clazz) {
829 List<T> resultList = getResultList(clazz);
830 if (CollectionUtils.isEmpty(resultList)) {
831 return null;
832 }
833
834 if (resultList.size() > 1) {
835 throw new NonUniqueResultException("Result for query '" + query + "' must contain exactly one item");
836 }
837
838 return resultList.get(0);
839 }
840
841
842
843
844 @Override
845 public int getCount() {
846 Number count = (Number) query.getSingleResult();
847 return count.intValue();
848 }
849
850
851
852
853 @Override
854 public void append(String query) {
855 where = query;
856 }
857
858
859
860
861 public String toString() {
862 return getQuerySelectStr();
863 }
864
865
866
867
868 private String getQueryCountStr() {
869 return getQueryStr(true);
870 }
871
872
873
874
875 private String getQuerySelectStr() {
876 return getQueryStr(false);
877 }
878
879
880
881
882
883 private String getQueryStr(boolean isCount) {
884 StringBuilder result = new StringBuilder();
885
886 String selected;
887 String orderStr;
888 if (isCount) {
889 selected = "count(*)";
890 orderStr = "";
891 } else {
892 selected = selectQuery;
893 if (orderBy.isEmpty()) {
894 orderStr = "";
895 } else {
896 orderStr = " ORDER BY " + StringUtils.join(orderBy, COMMA);
897 }
898 }
899
900 String fromStr;
901 if (CollectionUtils.isEmpty(subUnionQueryBuilders)) {
902 fromStr = StringUtils.join(froms, COMMA);
903 } else {
904 fromStr = getUnionStr();
905 }
906
907 result.append(" SELECT ");
908 result.append(selected);
909 result.append(" FROM ");
910 result.append(fromStr);
911 result.append(" ");
912 result.append(StringUtils.join(joins, " "));
913 if (StringUtils.isNotBlank(where)) {
914 result.append(" WHERE ");
915 result.append(where);
916 }
917 result.append(orderStr);
918
919 return result.toString();
920 }
921
922
923
924
925 private String getUnionStr() {
926 StringBuilder result = new StringBuilder();
927 List<String> subQueries = new ArrayList<String>();
928 for (QueryBuilder builder : this.subUnionQueryBuilders) {
929 String queryStr = builder.toString();
930
931 int pos = queryStr.indexOf("ORDER BY");
932 if (pos != -1) {
933 queryStr = queryStr.substring(0, pos - 1);
934 }
935 subQueries.add(queryStr);
936 }
937 result.append(StringUtils.join(subQueries, " UNION "));
938 return "(" + result.toString() + ")";
939 }
940
941
942
943
944
945
946 public List<Object> getBodyParameters() {
947 return bodyParameters;
948 }
949
950
951
952
953
954 public QueryBuilder clearOrderBy() {
955 this.orderBy.clear();
956 return this;
957 }
958 }