1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.jdbc;
17
18 import java.io.IOException;
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.List;
23 import java.util.function.BooleanSupplier;
24 import java.util.function.Consumer;
25
26
27
28
29
30
31
32 public abstract class AbstractSQL<T> {
33
34 private static final String AND = ") \nAND (";
35 private static final String OR = ") \nOR (";
36
37 private final SQLStatement sql = new SQLStatement();
38
39 public abstract T getSelf();
40
41 public T UPDATE(String table) {
42 sql().statementType = SQLStatement.StatementType.UPDATE;
43 sql().tables.add(table);
44 return getSelf();
45 }
46
47 public T SET(String sets) {
48 sql().sets.add(sets);
49 return getSelf();
50 }
51
52
53
54
55
56
57
58
59
60
61
62 public T SET(String... sets) {
63 sql().sets.addAll(Arrays.asList(sets));
64 return getSelf();
65 }
66
67 public T INSERT_INTO(String tableName) {
68 sql().statementType = SQLStatement.StatementType.INSERT;
69 sql().tables.add(tableName);
70 return getSelf();
71 }
72
73 public T VALUES(String columns, String values) {
74 INTO_COLUMNS(columns);
75 INTO_VALUES(values);
76 return getSelf();
77 }
78
79
80
81
82
83
84
85
86
87
88
89 public T INTO_COLUMNS(String... columns) {
90 sql().columns.addAll(Arrays.asList(columns));
91 return getSelf();
92 }
93
94
95
96
97
98
99
100
101
102
103
104 public T INTO_VALUES(String... values) {
105 List<String> list = sql().valuesList.get(sql().valuesList.size() - 1);
106 Collections.addAll(list, values);
107 return getSelf();
108 }
109
110 public T SELECT(String columns) {
111 sql().statementType = SQLStatement.StatementType.SELECT;
112 sql().select.add(columns);
113 return getSelf();
114 }
115
116
117
118
119
120
121
122
123
124
125
126 public T SELECT(String... columns) {
127 sql().statementType = SQLStatement.StatementType.SELECT;
128 sql().select.addAll(Arrays.asList(columns));
129 return getSelf();
130 }
131
132 public T SELECT_DISTINCT(String columns) {
133 sql().distinct = true;
134 SELECT(columns);
135 return getSelf();
136 }
137
138
139
140
141
142
143
144
145
146
147
148 public T SELECT_DISTINCT(String... columns) {
149 sql().distinct = true;
150 SELECT(columns);
151 return getSelf();
152 }
153
154 public T DELETE_FROM(String table) {
155 sql().statementType = SQLStatement.StatementType.DELETE;
156 sql().tables.add(table);
157 return getSelf();
158 }
159
160 public T FROM(String table) {
161 sql().tables.add(table);
162 return getSelf();
163 }
164
165
166
167
168
169
170
171
172
173
174
175 public T FROM(String... tables) {
176 sql().tables.addAll(Arrays.asList(tables));
177 return getSelf();
178 }
179
180 public T JOIN(String join) {
181 sql().join.add(join);
182 return getSelf();
183 }
184
185
186
187
188
189
190
191
192
193
194
195 public T JOIN(String... joins) {
196 sql().join.addAll(Arrays.asList(joins));
197 return getSelf();
198 }
199
200 public T INNER_JOIN(String join) {
201 sql().innerJoin.add(join);
202 return getSelf();
203 }
204
205
206
207
208
209
210
211
212
213
214
215 public T INNER_JOIN(String... joins) {
216 sql().innerJoin.addAll(Arrays.asList(joins));
217 return getSelf();
218 }
219
220 public T LEFT_OUTER_JOIN(String join) {
221 sql().leftOuterJoin.add(join);
222 return getSelf();
223 }
224
225
226
227
228
229
230
231
232
233
234
235 public T LEFT_OUTER_JOIN(String... joins) {
236 sql().leftOuterJoin.addAll(Arrays.asList(joins));
237 return getSelf();
238 }
239
240 public T RIGHT_OUTER_JOIN(String join) {
241 sql().rightOuterJoin.add(join);
242 return getSelf();
243 }
244
245
246
247
248
249
250
251
252
253
254
255 public T RIGHT_OUTER_JOIN(String... joins) {
256 sql().rightOuterJoin.addAll(Arrays.asList(joins));
257 return getSelf();
258 }
259
260 public T OUTER_JOIN(String join) {
261 sql().outerJoin.add(join);
262 return getSelf();
263 }
264
265
266
267
268
269
270
271
272
273
274
275 public T OUTER_JOIN(String... joins) {
276 sql().outerJoin.addAll(Arrays.asList(joins));
277 return getSelf();
278 }
279
280 public T WHERE(String conditions) {
281 sql().where.add(conditions);
282 sql().lastList = sql().where;
283 return getSelf();
284 }
285
286
287
288
289
290
291
292
293
294
295
296 public T WHERE(String... conditions) {
297 sql().where.addAll(Arrays.asList(conditions));
298 sql().lastList = sql().where;
299 return getSelf();
300 }
301
302 public T OR() {
303 sql().lastList.add(OR);
304 return getSelf();
305 }
306
307 public T AND() {
308 sql().lastList.add(AND);
309 return getSelf();
310 }
311
312 public T GROUP_BY(String columns) {
313 sql().groupBy.add(columns);
314 return getSelf();
315 }
316
317
318
319
320
321
322
323
324
325
326
327 public T GROUP_BY(String... columns) {
328 sql().groupBy.addAll(Arrays.asList(columns));
329 return getSelf();
330 }
331
332 public T HAVING(String conditions) {
333 sql().having.add(conditions);
334 sql().lastList = sql().having;
335 return getSelf();
336 }
337
338
339
340
341
342
343
344
345
346
347
348 public T HAVING(String... conditions) {
349 sql().having.addAll(Arrays.asList(conditions));
350 sql().lastList = sql().having;
351 return getSelf();
352 }
353
354 public T ORDER_BY(String columns) {
355 sql().orderBy.add(columns);
356 return getSelf();
357 }
358
359
360
361
362
363
364
365
366
367
368
369 public T ORDER_BY(String... columns) {
370 sql().orderBy.addAll(Arrays.asList(columns));
371 return getSelf();
372 }
373
374
375
376
377
378
379
380
381
382
383
384
385
386 public T LIMIT(String variable) {
387 sql().limit = variable;
388 sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.OFFSET_LIMIT;
389 return getSelf();
390 }
391
392
393
394
395
396
397
398
399
400
401
402
403
404 public T LIMIT(int value) {
405 return LIMIT(String.valueOf(value));
406 }
407
408
409
410
411
412
413
414
415
416
417
418
419
420 public T OFFSET(String variable) {
421 sql().offset = variable;
422 sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.OFFSET_LIMIT;
423 return getSelf();
424 }
425
426
427
428
429
430
431
432
433
434
435
436
437
438 public T OFFSET(long value) {
439 return OFFSET(String.valueOf(value));
440 }
441
442
443
444
445
446
447
448
449
450
451
452
453
454 public T FETCH_FIRST_ROWS_ONLY(String variable) {
455 sql().limit = variable;
456 sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.ISO;
457 return getSelf();
458 }
459
460
461
462
463
464
465
466
467
468
469
470
471
472 public T FETCH_FIRST_ROWS_ONLY(int value) {
473 return FETCH_FIRST_ROWS_ONLY(String.valueOf(value));
474 }
475
476
477
478
479
480
481
482
483
484
485
486
487
488 public T OFFSET_ROWS(String variable) {
489 sql().offset = variable;
490 sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.ISO;
491 return getSelf();
492 }
493
494
495
496
497
498
499
500
501
502
503
504
505
506 public T OFFSET_ROWS(long value) {
507 return OFFSET_ROWS(String.valueOf(value));
508 }
509
510
511
512
513
514
515
516
517 public T ADD_ROW() {
518 sql().valuesList.add(new ArrayList<>());
519 return getSelf();
520 }
521
522 private SQLStatement sql() {
523 return sql;
524 }
525
526 public <A extends Appendable> A usingAppender(A a) {
527 sql().sql(a);
528 return a;
529 }
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545 public T applyIf(boolean applyCondition, Consumer<T> sqlConsumer) {
546 T self = getSelf();
547 if (applyCondition) {
548 sqlConsumer.accept(self);
549 }
550 return self;
551 }
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567 public T applyIf(BooleanSupplier applyConditionSupplier, Consumer<T> sqlConsumer) {
568 return applyIf(applyConditionSupplier.getAsBoolean(), sqlConsumer);
569 }
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586 public <E> T applyForEach(Iterable<E> iterable, ForEachConsumer<T, E> forEachSqlConsumer) {
587 T self = getSelf();
588 int elementIndex = 0;
589 for (E element : iterable) {
590 forEachSqlConsumer.accept(self, element, elementIndex);
591 elementIndex++;
592 }
593 return self;
594 }
595
596 @Override
597 public String toString() {
598 StringBuilder sb = new StringBuilder();
599 sql().sql(sb);
600 return sb.toString();
601 }
602
603 private static class SafeAppendable {
604 private final Appendable appendable;
605 private boolean empty = true;
606
607 public SafeAppendable(Appendable a) {
608 this.appendable = a;
609 }
610
611 public SafeAppendable append(CharSequence s) {
612 try {
613 if (empty && s.length() > 0) {
614 empty = false;
615 }
616 appendable.append(s);
617 } catch (IOException e) {
618 throw new RuntimeException(e);
619 }
620 return this;
621 }
622
623 public boolean isEmpty() {
624 return empty;
625 }
626
627 }
628
629 private static class SQLStatement {
630
631 public enum StatementType {
632
633 DELETE,
634
635 INSERT,
636
637 SELECT,
638
639 UPDATE
640
641 }
642
643 private enum LimitingRowsStrategy {
644 NOP {
645 @Override
646 protected void appendClause(SafeAppendable builder, String offset, String limit) {
647
648 }
649 },
650 ISO {
651 @Override
652 protected void appendClause(SafeAppendable builder, String offset, String limit) {
653 if (offset != null) {
654 builder.append(" OFFSET ").append(offset).append(" ROWS");
655 }
656 if (limit != null) {
657 builder.append(" FETCH FIRST ").append(limit).append(" ROWS ONLY");
658 }
659 }
660 },
661 OFFSET_LIMIT {
662 @Override
663 protected void appendClause(SafeAppendable builder, String offset, String limit) {
664 if (limit != null) {
665 builder.append(" LIMIT ").append(limit);
666 }
667 if (offset != null) {
668 builder.append(" OFFSET ").append(offset);
669 }
670 }
671 };
672
673 protected abstract void appendClause(SafeAppendable builder, String offset, String limit);
674
675 }
676
677 StatementType statementType;
678 List<String> sets = new ArrayList<>();
679 List<String> select = new ArrayList<>();
680 List<String> tables = new ArrayList<>();
681 List<String> join = new ArrayList<>();
682 List<String> innerJoin = new ArrayList<>();
683 List<String> outerJoin = new ArrayList<>();
684 List<String> leftOuterJoin = new ArrayList<>();
685 List<String> rightOuterJoin = new ArrayList<>();
686 List<String> where = new ArrayList<>();
687 List<String> having = new ArrayList<>();
688 List<String> groupBy = new ArrayList<>();
689 List<String> orderBy = new ArrayList<>();
690 List<String> lastList = new ArrayList<>();
691 List<String> columns = new ArrayList<>();
692 List<List<String>> valuesList = new ArrayList<>();
693 boolean distinct;
694 String offset;
695 String limit;
696 LimitingRowsStrategy limitingRowsStrategy = LimitingRowsStrategy.NOP;
697
698 public SQLStatement() {
699
700 valuesList.add(new ArrayList<>());
701 }
702
703 private void sqlClause(SafeAppendable builder, String keyword, List<String> parts, String open, String close,
704 String conjunction) {
705 if (!parts.isEmpty()) {
706 if (!builder.isEmpty()) {
707 builder.append("\n");
708 }
709 builder.append(keyword);
710 builder.append(" ");
711 builder.append(open);
712 String last = "________";
713 for (int i = 0, n = parts.size(); i < n; i++) {
714 String part = parts.get(i);
715 if (i > 0 && !part.equals(AND) && !part.equals(OR) && !last.equals(AND) && !last.equals(OR)) {
716 builder.append(conjunction);
717 }
718 builder.append(part);
719 last = part;
720 }
721 builder.append(close);
722 }
723 }
724
725 private String selectSQL(SafeAppendable builder) {
726 if (distinct) {
727 sqlClause(builder, "SELECT DISTINCT", select, "", "", ", ");
728 } else {
729 sqlClause(builder, "SELECT", select, "", "", ", ");
730 }
731
732 sqlClause(builder, "FROM", tables, "", "", ", ");
733 joins(builder);
734 sqlClause(builder, "WHERE", where, "(", ")", " AND ");
735 sqlClause(builder, "GROUP BY", groupBy, "", "", ", ");
736 sqlClause(builder, "HAVING", having, "(", ")", " AND ");
737 sqlClause(builder, "ORDER BY", orderBy, "", "", ", ");
738 limitingRowsStrategy.appendClause(builder, offset, limit);
739 return builder.toString();
740 }
741
742 private void joins(SafeAppendable builder) {
743 sqlClause(builder, "JOIN", join, "", "", "\nJOIN ");
744 sqlClause(builder, "INNER JOIN", innerJoin, "", "", "\nINNER JOIN ");
745 sqlClause(builder, "OUTER JOIN", outerJoin, "", "", "\nOUTER JOIN ");
746 sqlClause(builder, "LEFT OUTER JOIN", leftOuterJoin, "", "", "\nLEFT OUTER JOIN ");
747 sqlClause(builder, "RIGHT OUTER JOIN", rightOuterJoin, "", "", "\nRIGHT OUTER JOIN ");
748 }
749
750 private String insertSQL(SafeAppendable builder) {
751 sqlClause(builder, "INSERT INTO", tables, "", "", "");
752 sqlClause(builder, "", columns, "(", ")", ", ");
753 for (int i = 0; i < valuesList.size(); i++) {
754 sqlClause(builder, i > 0 ? "," : "VALUES", valuesList.get(i), "(", ")", ", ");
755 }
756 return builder.toString();
757 }
758
759 private String deleteSQL(SafeAppendable builder) {
760 sqlClause(builder, "DELETE FROM", tables, "", "", "");
761 sqlClause(builder, "WHERE", where, "(", ")", " AND ");
762 limitingRowsStrategy.appendClause(builder, null, limit);
763 return builder.toString();
764 }
765
766 private String updateSQL(SafeAppendable builder) {
767 sqlClause(builder, "UPDATE", tables, "", "", "");
768 joins(builder);
769 sqlClause(builder, "SET", sets, "", "", ", ");
770 sqlClause(builder, "WHERE", where, "(", ")", " AND ");
771 limitingRowsStrategy.appendClause(builder, null, limit);
772 return builder.toString();
773 }
774
775 public String sql(Appendable a) {
776 SafeAppendable builder = new SafeAppendable(a);
777 if (statementType == null) {
778 return null;
779 }
780
781 String answer;
782
783 switch (statementType) {
784 case DELETE:
785 answer = deleteSQL(builder);
786 break;
787
788 case INSERT:
789 answer = insertSQL(builder);
790 break;
791
792 case SELECT:
793 answer = selectSQL(builder);
794 break;
795
796 case UPDATE:
797 answer = updateSQL(builder);
798 break;
799
800 default:
801 answer = null;
802 }
803
804 return answer;
805 }
806 }
807
808
809
810
811
812
813
814
815
816
817
818 public interface ForEachConsumer<T, E> {
819
820
821
822
823
824
825
826
827
828
829
830 void accept(T sql, E element, int elementIndex);
831
832 }
833
834 }