View Javadoc
1   /*
2    *    Copyright 2009-2021 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
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  
24  /**
25   * @author Clinton Begin
26   * @author Jeff Butler
27   * @author Adam Gent
28   * @author Kazuki Shimizu
29   */
30  public abstract class AbstractSQL<T> {
31  
32    private static final String AND = ") \nAND (";
33    private static final String OR = ") \nOR (";
34  
35    private final SQLStatement sql = new SQLStatement();
36  
37    public abstract T getSelf();
38  
39    public T UPDATE(String table) {
40      sql().statementType = SQLStatement.StatementType.UPDATE;
41      sql().tables.add(table);
42      return getSelf();
43    }
44  
45    public T SET(String sets) {
46      sql().sets.add(sets);
47      return getSelf();
48    }
49  
50    /**
51     * Sets the.
52     *
53     * @param sets
54     *          the sets
55     * @return the t
56     * @since 3.4.2
57     */
58    public T SET(String... sets) {
59      sql().sets.addAll(Arrays.asList(sets));
60      return getSelf();
61    }
62  
63    public T INSERT_INTO(String tableName) {
64      sql().statementType = SQLStatement.StatementType.INSERT;
65      sql().tables.add(tableName);
66      return getSelf();
67    }
68  
69    public T VALUES(String columns, String values) {
70      INTO_COLUMNS(columns);
71      INTO_VALUES(values);
72      return getSelf();
73    }
74  
75    /**
76     * Into columns.
77     *
78     * @param columns
79     *          the columns
80     * @return the t
81     * @since 3.4.2
82     */
83    public T INTO_COLUMNS(String... columns) {
84      sql().columns.addAll(Arrays.asList(columns));
85      return getSelf();
86    }
87  
88    /**
89     * Into values.
90     *
91     * @param values
92     *          the values
93     * @return the t
94     * @since 3.4.2
95     */
96    public T INTO_VALUES(String... values) {
97      List<String> list = sql().valuesList.get(sql().valuesList.size() - 1);
98      Collections.addAll(list, values);
99      return getSelf();
100   }
101 
102   public T SELECT(String columns) {
103     sql().statementType = SQLStatement.StatementType.SELECT;
104     sql().select.add(columns);
105     return getSelf();
106   }
107 
108   /**
109    * Select.
110    *
111    * @param columns
112    *          the columns
113    * @return the t
114    * @since 3.4.2
115    */
116   public T SELECT(String... columns) {
117     sql().statementType = SQLStatement.StatementType.SELECT;
118     sql().select.addAll(Arrays.asList(columns));
119     return getSelf();
120   }
121 
122   public T SELECT_DISTINCT(String columns) {
123     sql().distinct = true;
124     SELECT(columns);
125     return getSelf();
126   }
127 
128   /**
129    * Select distinct.
130    *
131    * @param columns
132    *          the columns
133    * @return the t
134    * @since 3.4.2
135    */
136   public T SELECT_DISTINCT(String... columns) {
137     sql().distinct = true;
138     SELECT(columns);
139     return getSelf();
140   }
141 
142   public T DELETE_FROM(String table) {
143     sql().statementType = SQLStatement.StatementType.DELETE;
144     sql().tables.add(table);
145     return getSelf();
146   }
147 
148   public T FROM(String table) {
149     sql().tables.add(table);
150     return getSelf();
151   }
152 
153   /**
154    * From.
155    *
156    * @param tables
157    *          the tables
158    * @return the t
159    * @since 3.4.2
160    */
161   public T FROM(String... tables) {
162     sql().tables.addAll(Arrays.asList(tables));
163     return getSelf();
164   }
165 
166   public T JOIN(String join) {
167     sql().join.add(join);
168     return getSelf();
169   }
170 
171   /**
172    * Join.
173    *
174    * @param joins
175    *          the joins
176    * @return the t
177    * @since 3.4.2
178    */
179   public T JOIN(String... joins) {
180     sql().join.addAll(Arrays.asList(joins));
181     return getSelf();
182   }
183 
184   public T INNER_JOIN(String join) {
185     sql().innerJoin.add(join);
186     return getSelf();
187   }
188 
189   /**
190    * Inner join.
191    *
192    * @param joins
193    *          the joins
194    * @return the t
195    * @since 3.4.2
196    */
197   public T INNER_JOIN(String... joins) {
198     sql().innerJoin.addAll(Arrays.asList(joins));
199     return getSelf();
200   }
201 
202   public T LEFT_OUTER_JOIN(String join) {
203     sql().leftOuterJoin.add(join);
204     return getSelf();
205   }
206 
207   /**
208    * Left outer join.
209    *
210    * @param joins
211    *          the joins
212    * @return the t
213    * @since 3.4.2
214    */
215   public T LEFT_OUTER_JOIN(String... joins) {
216     sql().leftOuterJoin.addAll(Arrays.asList(joins));
217     return getSelf();
218   }
219 
220   public T RIGHT_OUTER_JOIN(String join) {
221     sql().rightOuterJoin.add(join);
222     return getSelf();
223   }
224 
225   /**
226    * Right outer join.
227    *
228    * @param joins
229    *          the joins
230    * @return the t
231    * @since 3.4.2
232    */
233   public T RIGHT_OUTER_JOIN(String... joins) {
234     sql().rightOuterJoin.addAll(Arrays.asList(joins));
235     return getSelf();
236   }
237 
238   public T OUTER_JOIN(String join) {
239     sql().outerJoin.add(join);
240     return getSelf();
241   }
242 
243   /**
244    * Outer join.
245    *
246    * @param joins
247    *          the joins
248    * @return the t
249    * @since 3.4.2
250    */
251   public T OUTER_JOIN(String... joins) {
252     sql().outerJoin.addAll(Arrays.asList(joins));
253     return getSelf();
254   }
255 
256   public T WHERE(String conditions) {
257     sql().where.add(conditions);
258     sql().lastList = sql().where;
259     return getSelf();
260   }
261 
262   /**
263    * Where.
264    *
265    * @param conditions
266    *          the conditions
267    * @return the t
268    * @since 3.4.2
269    */
270   public T WHERE(String... conditions) {
271     sql().where.addAll(Arrays.asList(conditions));
272     sql().lastList = sql().where;
273     return getSelf();
274   }
275 
276   public T OR() {
277     sql().lastList.add(OR);
278     return getSelf();
279   }
280 
281   public T AND() {
282     sql().lastList.add(AND);
283     return getSelf();
284   }
285 
286   public T GROUP_BY(String columns) {
287     sql().groupBy.add(columns);
288     return getSelf();
289   }
290 
291   /**
292    * Group by.
293    *
294    * @param columns
295    *          the columns
296    * @return the t
297    * @since 3.4.2
298    */
299   public T GROUP_BY(String... columns) {
300     sql().groupBy.addAll(Arrays.asList(columns));
301     return getSelf();
302   }
303 
304   public T HAVING(String conditions) {
305     sql().having.add(conditions);
306     sql().lastList = sql().having;
307     return getSelf();
308   }
309 
310   /**
311    * Having.
312    *
313    * @param conditions
314    *          the conditions
315    * @return the t
316    * @since 3.4.2
317    */
318   public T HAVING(String... conditions) {
319     sql().having.addAll(Arrays.asList(conditions));
320     sql().lastList = sql().having;
321     return getSelf();
322   }
323 
324   public T ORDER_BY(String columns) {
325     sql().orderBy.add(columns);
326     return getSelf();
327   }
328 
329   /**
330    * Order by.
331    *
332    * @param columns
333    *          the columns
334    * @return the t
335    * @since 3.4.2
336    */
337   public T ORDER_BY(String... columns) {
338     sql().orderBy.addAll(Arrays.asList(columns));
339     return getSelf();
340   }
341 
342   /**
343    * Set the limit variable string(e.g. {@code "#{limit}"}).
344    *
345    * @param variable a limit variable string
346    * @return a self instance
347    * @see #OFFSET(String)
348    * @since 3.5.2
349    */
350   public T LIMIT(String variable) {
351     sql().limit = variable;
352     sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.OFFSET_LIMIT;
353     return getSelf();
354   }
355 
356   /**
357    * Set the limit value.
358    *
359    * @param value an offset value
360    * @return a self instance
361    * @see #OFFSET(long)
362    * @since 3.5.2
363    */
364   public T LIMIT(int value) {
365     return LIMIT(String.valueOf(value));
366   }
367 
368   /**
369    * Set the offset variable string(e.g. {@code "#{offset}"}).
370    *
371    * @param variable a offset variable string
372    * @return a self instance
373    * @see #LIMIT(String)
374    * @since 3.5.2
375    */
376   public T OFFSET(String variable) {
377     sql().offset = variable;
378     sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.OFFSET_LIMIT;
379     return getSelf();
380   }
381 
382   /**
383    * Set the offset value.
384    *
385    * @param value an offset value
386    * @return a self instance
387    * @see #LIMIT(int)
388    * @since 3.5.2
389    */
390   public T OFFSET(long value) {
391     return OFFSET(String.valueOf(value));
392   }
393 
394   /**
395    * Set the fetch first rows variable string(e.g. {@code "#{fetchFirstRows}"}).
396    *
397    * @param variable a fetch first rows variable string
398    * @return a self instance
399    * @see #OFFSET_ROWS(String)
400    * @since 3.5.2
401    */
402   public T FETCH_FIRST_ROWS_ONLY(String variable) {
403     sql().limit = variable;
404     sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.ISO;
405     return getSelf();
406   }
407 
408   /**
409    * Set the fetch first rows value.
410    *
411    * @param value a fetch first rows value
412    * @return a self instance
413    * @see #OFFSET_ROWS(long)
414    * @since 3.5.2
415    */
416   public T FETCH_FIRST_ROWS_ONLY(int value) {
417     return FETCH_FIRST_ROWS_ONLY(String.valueOf(value));
418   }
419 
420   /**
421    * Set the offset rows variable string(e.g. {@code "#{offset}"}).
422    *
423    * @param variable a offset rows variable string
424    * @return a self instance
425    * @see #FETCH_FIRST_ROWS_ONLY(String)
426    * @since 3.5.2
427    */
428   public T OFFSET_ROWS(String variable) {
429     sql().offset = variable;
430     sql().limitingRowsStrategy = SQLStatement.LimitingRowsStrategy.ISO;
431     return getSelf();
432   }
433 
434   /**
435    * Set the offset rows value.
436    *
437    * @param value an offset rows value
438    * @return a self instance
439    * @see #FETCH_FIRST_ROWS_ONLY(int)
440    * @since 3.5.2
441    */
442   public T OFFSET_ROWS(long value) {
443     return OFFSET_ROWS(String.valueOf(value));
444   }
445 
446   /**
447    * used to add a new inserted row while do multi-row insert.
448    *
449    * @return the t
450    * @since 3.5.2
451    */
452   public T ADD_ROW() {
453     sql().valuesList.add(new ArrayList<>());
454     return getSelf();
455   }
456 
457   private SQLStatement sql() {
458     return sql;
459   }
460 
461   public <A extends Appendable> A usingAppender(A a) {
462     sql().sql(a);
463     return a;
464   }
465 
466   @Override
467   public String toString() {
468     StringBuilder sb = new StringBuilder();
469     sql().sql(sb);
470     return sb.toString();
471   }
472 
473   private static class SafeAppendable {
474     private final Appendable appendable;
475     private boolean empty = true;
476 
477     public SafeAppendable(Appendable a) {
478       super();
479       this.appendable = a;
480     }
481 
482     public SafeAppendable append(CharSequence s) {
483       try {
484         if (empty && s.length() > 0) {
485           empty = false;
486         }
487         appendable.append(s);
488       } catch (IOException e) {
489         throw new RuntimeException(e);
490       }
491       return this;
492     }
493 
494     public boolean isEmpty() {
495       return empty;
496     }
497 
498   }
499 
500   private static class SQLStatement {
501 
502     public enum StatementType {
503       DELETE, INSERT, SELECT, UPDATE
504     }
505 
506     private enum LimitingRowsStrategy {
507       NOP {
508         @Override
509         protected void appendClause(SafeAppendable builder, String offset, String limit) {
510           // NOP
511         }
512       },
513       ISO {
514         @Override
515         protected void appendClause(SafeAppendable builder, String offset, String limit) {
516           if (offset != null) {
517             builder.append(" OFFSET ").append(offset).append(" ROWS");
518           }
519           if (limit != null) {
520             builder.append(" FETCH FIRST ").append(limit).append(" ROWS ONLY");
521           }
522         }
523       },
524       OFFSET_LIMIT {
525         @Override
526         protected void appendClause(SafeAppendable builder, String offset, String limit) {
527           if (limit != null) {
528             builder.append(" LIMIT ").append(limit);
529           }
530           if (offset != null) {
531             builder.append(" OFFSET ").append(offset);
532           }
533         }
534       };
535 
536       protected abstract void appendClause(SafeAppendable builder, String offset, String limit);
537 
538     }
539 
540     StatementType statementType;
541     List<String> sets = new ArrayList<>();
542     List<String> select = new ArrayList<>();
543     List<String> tables = new ArrayList<>();
544     List<String> join = new ArrayList<>();
545     List<String> innerJoin = new ArrayList<>();
546     List<String> outerJoin = new ArrayList<>();
547     List<String> leftOuterJoin = new ArrayList<>();
548     List<String> rightOuterJoin = new ArrayList<>();
549     List<String> where = new ArrayList<>();
550     List<String> having = new ArrayList<>();
551     List<String> groupBy = new ArrayList<>();
552     List<String> orderBy = new ArrayList<>();
553     List<String> lastList = new ArrayList<>();
554     List<String> columns = new ArrayList<>();
555     List<List<String>> valuesList = new ArrayList<>();
556     boolean distinct;
557     String offset;
558     String limit;
559     LimitingRowsStrategy limitingRowsStrategy = LimitingRowsStrategy.NOP;
560 
561     public SQLStatement() {
562       // Prevent Synthetic Access
563       valuesList.add(new ArrayList<>());
564     }
565 
566     private void sqlClause(SafeAppendable builder, String keyword, List<String> parts, String open, String close,
567                            String conjunction) {
568       if (!parts.isEmpty()) {
569         if (!builder.isEmpty()) {
570           builder.append("\n");
571         }
572         builder.append(keyword);
573         builder.append(" ");
574         builder.append(open);
575         String last = "________";
576         for (int i = 0, n = parts.size(); i < n; i++) {
577           String part = parts.get(i);
578           if (i > 0 && !part.equals(AND) && !part.equals(OR) && !last.equals(AND) && !last.equals(OR)) {
579             builder.append(conjunction);
580           }
581           builder.append(part);
582           last = part;
583         }
584         builder.append(close);
585       }
586     }
587 
588     private String selectSQL(SafeAppendable builder) {
589       if (distinct) {
590         sqlClause(builder, "SELECT DISTINCT", select, "", "", ", ");
591       } else {
592         sqlClause(builder, "SELECT", select, "", "", ", ");
593       }
594 
595       sqlClause(builder, "FROM", tables, "", "", ", ");
596       joins(builder);
597       sqlClause(builder, "WHERE", where, "(", ")", " AND ");
598       sqlClause(builder, "GROUP BY", groupBy, "", "", ", ");
599       sqlClause(builder, "HAVING", having, "(", ")", " AND ");
600       sqlClause(builder, "ORDER BY", orderBy, "", "", ", ");
601       limitingRowsStrategy.appendClause(builder, offset, limit);
602       return builder.toString();
603     }
604 
605     private void joins(SafeAppendable builder) {
606       sqlClause(builder, "JOIN", join, "", "", "\nJOIN ");
607       sqlClause(builder, "INNER JOIN", innerJoin, "", "", "\nINNER JOIN ");
608       sqlClause(builder, "OUTER JOIN", outerJoin, "", "", "\nOUTER JOIN ");
609       sqlClause(builder, "LEFT OUTER JOIN", leftOuterJoin, "", "", "\nLEFT OUTER JOIN ");
610       sqlClause(builder, "RIGHT OUTER JOIN", rightOuterJoin, "", "", "\nRIGHT OUTER JOIN ");
611     }
612 
613     private String insertSQL(SafeAppendable builder) {
614       sqlClause(builder, "INSERT INTO", tables, "", "", "");
615       sqlClause(builder, "", columns, "(", ")", ", ");
616       for (int i = 0; i < valuesList.size(); i++) {
617         sqlClause(builder, i > 0 ? "," : "VALUES", valuesList.get(i), "(", ")", ", ");
618       }
619       return builder.toString();
620     }
621 
622     private String deleteSQL(SafeAppendable builder) {
623       sqlClause(builder, "DELETE FROM", tables, "", "", "");
624       sqlClause(builder, "WHERE", where, "(", ")", " AND ");
625       limitingRowsStrategy.appendClause(builder, null, limit);
626       return builder.toString();
627     }
628 
629     private String updateSQL(SafeAppendable builder) {
630       sqlClause(builder, "UPDATE", tables, "", "", "");
631       joins(builder);
632       sqlClause(builder, "SET", sets, "", "", ", ");
633       sqlClause(builder, "WHERE", where, "(", ")", " AND ");
634       limitingRowsStrategy.appendClause(builder, null, limit);
635       return builder.toString();
636     }
637 
638     public String sql(Appendable a) {
639       SafeAppendable builder = new SafeAppendable(a);
640       if (statementType == null) {
641         return null;
642       }
643 
644       String answer;
645 
646       switch (statementType) {
647         case DELETE:
648           answer = deleteSQL(builder);
649           break;
650 
651         case INSERT:
652           answer = insertSQL(builder);
653           break;
654 
655         case SELECT:
656           answer = selectSQL(builder);
657           break;
658 
659         case UPDATE:
660           answer = updateSQL(builder);
661           break;
662 
663         default:
664           answer = null;
665       }
666 
667       return answer;
668     }
669   }
670 }