View Javadoc
1   /*
2    *    Copyright 2016-2026 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    *       https://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.mybatis.dynamic.sql.select;
17  
18  import java.util.ArrayList;
19  import java.util.Arrays;
20  import java.util.Collection;
21  import java.util.List;
22  import java.util.Objects;
23  import java.util.function.Consumer;
24  
25  import org.jspecify.annotations.Nullable;
26  import org.mybatis.dynamic.sql.AndOrCriteriaGroup;
27  import org.mybatis.dynamic.sql.BasicColumn;
28  import org.mybatis.dynamic.sql.NullCriterion;
29  import org.mybatis.dynamic.sql.SortSpecification;
30  import org.mybatis.dynamic.sql.SqlCriterion;
31  import org.mybatis.dynamic.sql.SqlTable;
32  import org.mybatis.dynamic.sql.TableExpression;
33  import org.mybatis.dynamic.sql.configuration.StatementConfiguration;
34  import org.mybatis.dynamic.sql.dsl.AbstractJoinSupport;
35  import org.mybatis.dynamic.sql.dsl.AbstractQueryingDSL;
36  import org.mybatis.dynamic.sql.dsl.BooleanOperations;
37  import org.mybatis.dynamic.sql.dsl.ForAndWaitOperations;
38  import org.mybatis.dynamic.sql.dsl.GroupByOperations;
39  import org.mybatis.dynamic.sql.dsl.HavingOperations;
40  import org.mybatis.dynamic.sql.dsl.JoinOperations;
41  import org.mybatis.dynamic.sql.dsl.LimitAndOffsetOperations;
42  import org.mybatis.dynamic.sql.dsl.OrderByOperations;
43  import org.mybatis.dynamic.sql.dsl.WhereOperations;
44  import org.mybatis.dynamic.sql.select.join.JoinType;
45  import org.mybatis.dynamic.sql.util.Buildable;
46  import org.mybatis.dynamic.sql.util.ConfigurableStatement;
47  import org.mybatis.dynamic.sql.util.Validator;
48  import org.mybatis.dynamic.sql.where.WhereApplier;
49  import org.mybatis.dynamic.sql.where.WhereModel;
50  
51  public class QueryExpressionDSL<R> extends AbstractQueryingDSL implements
52          JoinOperations<QueryExpressionDSL<R>.JoinSpecificationFinisher>,
53          WhereOperations<QueryExpressionDSL<R>.QueryExpressionWhereBuilder>,
54          GroupByOperations<QueryExpressionDSL<R>>,
55          HavingOperations<QueryExpressionDSL<R>.QueryExpressionHavingBuilder>,
56          ConfigurableStatement<QueryExpressionDSL<R>>,
57          LimitAndOffsetOperations<SelectDSL<R>, R>,
58          ForAndWaitOperations<SelectDSL<R>>,
59          OrderByOperations<SelectDSL<R>>,
60          Buildable<R> {
61  
62      private final @Nullable String connector;
63      private final SelectDSL<R> selectDSL;
64      private final boolean isDistinct;
65      private final List<BasicColumn> selectList;
66      private @Nullable QueryExpressionWhereBuilder whereBuilder;
67      private @Nullable GroupByModel groupByModel;
68      private @Nullable QueryExpressionHavingBuilder havingBuilder;
69  
70      protected QueryExpressionDSL(Builder<R> builder) {
71          connector = builder.connector;
72          selectList = builder.selectList;
73          isDistinct = builder.isDistinct;
74          selectDSL = Objects.requireNonNull(builder.selectDSL);
75          selectDSL.registerQueryExpression(this);
76      }
77  
78      public QueryExpressionDSL<R> from(Buildable<SelectModel> select) {
79          setTable(select);
80          return this;
81      }
82  
83      public QueryExpressionDSL<R> from(Buildable<SelectModel> select, String tableAlias) {
84          setTable(select, tableAlias);
85          return this;
86      }
87  
88      public QueryExpressionDSL<R> from(SqlTable table) {
89          setTable(table);
90          return this;
91      }
92  
93      public QueryExpressionDSL<R> from(SqlTable table, String tableAlias) {
94          setTable(table, tableAlias);
95          return this;
96      }
97  
98      @Override
99      public JoinSpecificationFinisher join(JoinType joinType, TableExpression joinTable,
100                                           SqlCriterion initialCriterion) {
101         var finisher = new JoinSpecificationFinisher(joinType, joinTable, initialCriterion);
102         addJoinSpecification(finisher);
103         return finisher;
104     }
105 
106     @Override
107     public JoinSpecificationFinisher join(JoinType joinType, SqlTable joinTable, String tableAlias,
108                                           SqlCriterion initialCriterion) {
109         addTableAlias(joinTable, tableAlias);
110         return join(joinType, joinTable, initialCriterion);
111     }
112 
113 
114     @Override
115     public QueryExpressionWhereBuilder where() {
116         whereBuilder = Objects.requireNonNullElseGet(whereBuilder,
117                 () -> new QueryExpressionWhereBuilder(new NullCriterion()));
118         return whereBuilder;
119     }
120 
121     @Override
122     public QueryExpressionWhereBuilder where(SqlCriterion initialCriterion) {
123         Validator.assertNull(whereBuilder, Validator.ERROR_32);
124         whereBuilder = new QueryExpressionWhereBuilder(initialCriterion);
125         return whereBuilder;
126     }
127 
128     @Override
129     public QueryExpressionWhereBuilder applyWhere(WhereApplier whereApplier) {
130         Validator.assertNull(whereBuilder, Validator.ERROR_32);
131         whereBuilder = new QueryExpressionWhereBuilder(whereApplier.initialCriterion(), whereApplier.subCriteria());
132         return whereBuilder;
133     }
134 
135     @Override
136     public QueryExpressionDSL<R> configureStatement(Consumer<StatementConfiguration> consumer) {
137         selectDSL.configureStatement(consumer);
138         return this;
139     }
140 
141     @Override
142     public QueryExpressionHavingBuilder having(SqlCriterion initialCriterion) {
143         Validator.assertNull(havingBuilder, "ERROR.31"); //$NON-NLS-1$
144         havingBuilder = new QueryExpressionHavingBuilder(initialCriterion);
145         return havingBuilder;
146     }
147 
148     @Override
149     public QueryExpressionHavingBuilder applyHaving(HavingApplier havingApplier) {
150         Validator.assertNull(havingBuilder, "ERROR.31"); //$NON-NLS-1$
151         havingBuilder = new QueryExpressionHavingBuilder(havingApplier.initialCriterion(), havingApplier.subCriteria());
152         return havingBuilder;
153     }
154 
155     @Override
156     public R build() {
157         return selectDSL.build();
158     }
159 
160     @Override
161     public QueryExpressionDSL<R> groupBy(Collection<? extends BasicColumn> columns) {
162         groupByModel = GroupByModel.of(columns);
163         return this;
164     }
165 
166     @Override
167     public SelectDSL<R> orderBy(Collection<? extends SortSpecification> columns) {
168         return selectDSL.orderBy(columns);
169     }
170 
171     public UnionBuilder union() {
172         return new UnionBuilder("union"); //$NON-NLS-1$
173     }
174 
175     public UnionBuilder unionAll() {
176         return new UnionBuilder("union all"); //$NON-NLS-1$
177     }
178 
179     protected QueryExpressionModel buildModel() {
180         return QueryExpressionModel.withSelectList(selectList)
181                 .withConnector(connector)
182                 .withTable(table())
183                 .isDistinct(isDistinct)
184                 .withTableAliases(tableAliases())
185                 .withJoinModel(buildJoinModel())
186                 .withGroupByModel(groupByModel)
187                 .withWhereModel(whereBuilder == null ? null : whereBuilder.buildWhereModel())
188                 .withHavingModel(havingBuilder == null ? null : havingBuilder.buildHavingModel())
189                 .build();
190     }
191 
192     @Override
193     public SelectDSL<R> setWaitClause(String waitClause) {
194         return selectDSL.setWaitClause(waitClause);
195     }
196 
197     @Override
198     public SelectDSL<R> setForClause(String forClause) {
199         return selectDSL.setForClause(forClause);
200     }
201 
202     @Override
203     public LimitFinisher<SelectDSL<R>, R> limitWhenPresent(@Nullable Long limit) {
204         return selectDSL.limitWhenPresent(limit);
205     }
206 
207     @Override
208     public OffsetFirstFinisher<SelectDSL<R>, R> offsetWhenPresent(@Nullable Long offset) {
209         return selectDSL.offsetWhenPresent(offset);
210     }
211 
212     @Override
213     public FetchFirstFinisher<SelectDSL<R>> fetchFirstWhenPresent(@Nullable Long fetchFirstRows) {
214         return selectDSL.fetchFirstWhenPresent(fetchFirstRows);
215     }
216 
217     public class QueryExpressionWhereBuilder
218             implements BooleanOperations<QueryExpressionWhereBuilder>,
219             ConfigurableStatement<QueryExpressionWhereBuilder>,
220             OrderByOperations<SelectDSL<R>>,
221             GroupByOperations<QueryExpressionDSL<R>>,
222             ForAndWaitOperations<SelectDSL<R>>,
223             LimitAndOffsetOperations<SelectDSL<R>, R>,
224             Buildable<R> {
225         protected final SqlCriterion initialCriterion;
226         protected final List<AndOrCriteriaGroup> subCriteria = new ArrayList<>();
227 
228         public QueryExpressionWhereBuilder(SqlCriterion initialCriterion) {
229             this.initialCriterion = initialCriterion;
230         }
231 
232         public QueryExpressionWhereBuilder(SqlCriterion initialCriterion, List<AndOrCriteriaGroup> subCriteria) {
233             this(initialCriterion);
234             this.subCriteria.addAll(subCriteria);
235         }
236 
237         @Override
238         public QueryExpressionWhereBuilder addSubCriterion(AndOrCriteriaGroup subCriterion) {
239             subCriteria.add(subCriterion);
240             return this;
241         }
242 
243         public UnionBuilder union() {
244             return QueryExpressionDSL.this.union();
245         }
246 
247         public UnionBuilder unionAll() {
248             return QueryExpressionDSL.this.unionAll();
249         }
250 
251         @Override
252         public SelectDSL<R> orderBy(Collection<? extends SortSpecification> columns) {
253             return QueryExpressionDSL.this.orderBy(columns);
254         }
255 
256         @Override
257         public QueryExpressionDSL<R> groupBy(Collection<? extends BasicColumn> columns) {
258             return QueryExpressionDSL.this.groupBy(columns);
259         }
260 
261         @Override
262         public QueryExpressionWhereBuilder configureStatement(Consumer<StatementConfiguration> consumer) {
263             QueryExpressionDSL.this.configureStatement(consumer);
264             return this;
265         }
266 
267         @Override
268         public R build() {
269             return QueryExpressionDSL.this.build();
270         }
271 
272         @Override
273         public SelectDSL<R> setWaitClause(String waitClause) {
274             return QueryExpressionDSL.this.setWaitClause(waitClause);
275         }
276 
277         @Override
278         public SelectDSL<R> setForClause(String forClause) {
279             return QueryExpressionDSL.this.setForClause(forClause);
280         }
281 
282         @Override
283         public LimitFinisher<SelectDSL<R>, R> limitWhenPresent(@Nullable Long limit) {
284             return QueryExpressionDSL.this.limitWhenPresent(limit);
285         }
286 
287         @Override
288         public OffsetFirstFinisher<SelectDSL<R>, R> offsetWhenPresent(@Nullable Long offset) {
289             return QueryExpressionDSL.this.offsetWhenPresent(offset);
290         }
291 
292         @Override
293         public FetchFirstFinisher<SelectDSL<R>> fetchFirstWhenPresent(@Nullable Long fetchFirstRows) {
294             return QueryExpressionDSL.this.fetchFirstWhenPresent(fetchFirstRows);
295         }
296 
297         protected WhereModel buildWhereModel() {
298             return new WhereModel.Builder()
299                     .withInitialCriterion(initialCriterion)
300                     .withSubCriteria(subCriteria)
301                     .build();
302         }
303     }
304 
305     public class JoinSpecificationFinisher
306             extends AbstractJoinSupport<QueryExpressionDSL<R>, JoinSpecificationFinisher>
307             implements WhereOperations<QueryExpressionWhereBuilder>,
308             ConfigurableStatement<JoinSpecificationFinisher>,
309             GroupByOperations<QueryExpressionDSL<R>>,
310             ForAndWaitOperations<SelectDSL<R>>,
311             LimitAndOffsetOperations<SelectDSL<R>, R>,
312             OrderByOperations<SelectDSL<R>>,
313             Buildable<R> {
314         protected JoinSpecificationFinisher(JoinType joinType, TableExpression joinTable,
315                                             SqlCriterion initialCriterion) {
316             super(joinType, joinTable, initialCriterion);
317         }
318 
319         @Override
320         public R build() {
321             return QueryExpressionDSL.this.build();
322         }
323 
324         @Override
325         public JoinSpecificationFinisher configureStatement(Consumer<StatementConfiguration> consumer) {
326             selectDSL.configureStatement(consumer);
327             return this;
328         }
329 
330         @Override
331         public QueryExpressionWhereBuilder where() {
332             return QueryExpressionDSL.this.where();
333         }
334 
335         @Override
336         public QueryExpressionWhereBuilder where(SqlCriterion initialCriterion) {
337             return QueryExpressionDSL.this.where(initialCriterion);
338         }
339 
340         @Override
341         public QueryExpressionWhereBuilder applyWhere(WhereApplier whereApplier) {
342             return QueryExpressionDSL.this.applyWhere(whereApplier);
343         }
344 
345         @Override
346         public QueryExpressionDSL<R> groupBy(Collection<? extends BasicColumn> columns) {
347             return QueryExpressionDSL.this.groupBy(columns);
348         }
349 
350         public UnionBuilder union() {
351             return QueryExpressionDSL.this.union();
352         }
353 
354         public UnionBuilder unionAll() {
355             return QueryExpressionDSL.this.unionAll();
356         }
357 
358         @Override
359         public SelectDSL<R> orderBy(Collection<? extends SortSpecification> columns) {
360             return QueryExpressionDSL.this.orderBy(columns);
361         }
362 
363         @Override
364         protected JoinSpecificationFinisher getThis() {
365             return this;
366         }
367 
368         @Override
369         public JoinSpecificationFinisher join(JoinType joinType, TableExpression joinTable,
370                                               SqlCriterion initialCriterion) {
371             return QueryExpressionDSL.this.join(joinType, joinTable, initialCriterion);
372         }
373 
374         @Override
375         public JoinSpecificationFinisher join(JoinType joinType, SqlTable joinTable, String tableAlias,
376                                               SqlCriterion initialCriterion) {
377             return QueryExpressionDSL.this.join(joinType, joinTable, tableAlias, initialCriterion);
378         }
379 
380         @Override
381         public QueryExpressionDSL<R> endJoin() {
382             return QueryExpressionDSL.this;
383         }
384 
385         @Override
386         public LimitFinisher<SelectDSL<R>, R> limitWhenPresent(@Nullable Long limit) {
387             return QueryExpressionDSL.this.limitWhenPresent(limit);
388         }
389 
390         @Override
391         public OffsetFirstFinisher<SelectDSL<R>, R> offsetWhenPresent(@Nullable Long offset) {
392             return QueryExpressionDSL.this.offsetWhenPresent(offset);
393         }
394 
395         @Override
396         public FetchFirstFinisher<SelectDSL<R>> fetchFirstWhenPresent(@Nullable Long fetchFirstRows) {
397             return QueryExpressionDSL.this.fetchFirstWhenPresent(fetchFirstRows);
398         }
399 
400         @Override
401         public SelectDSL<R> setWaitClause(String waitClause) {
402             return QueryExpressionDSL.this.setWaitClause(waitClause);
403         }
404 
405         @Override
406         public SelectDSL<R> setForClause(String forClause) {
407             return QueryExpressionDSL.this.setForClause(forClause);
408         }
409     }
410 
411     public class UnionBuilder {
412         protected final String connector;
413 
414         public UnionBuilder(String connector) {
415             this.connector = connector;
416         }
417 
418         public QueryExpressionDSL<R> select(BasicColumn... selectList) {
419             return select(Arrays.asList(selectList));
420         }
421 
422         public QueryExpressionDSL<R> select(List<BasicColumn> selectList) {
423             return new Builder<R>()
424                     .withConnector(connector)
425                     .withSelectList(selectList)
426                     .withSelectDSL(selectDSL)
427                     .build();
428         }
429 
430         public QueryExpressionDSL<R> selectDistinct(BasicColumn... selectList) {
431             return selectDistinct(Arrays.asList(selectList));
432         }
433 
434         public QueryExpressionDSL<R> selectDistinct(List<BasicColumn> selectList) {
435             return new Builder<R>()
436                     .withConnector(connector)
437                     .withSelectList(selectList)
438                     .withSelectDSL(selectDSL)
439                     .isDistinct()
440                     .build();
441         }
442     }
443 
444     public class QueryExpressionHavingBuilder implements BooleanOperations<QueryExpressionHavingBuilder>,
445             ForAndWaitOperations<SelectDSL<R>>,
446             LimitAndOffsetOperations<SelectDSL<R>, R>,
447             OrderByOperations<SelectDSL<R>>,
448             Buildable<R> {
449         private final SqlCriterion initialCriterion;
450         private final List<AndOrCriteriaGroup> subCriteria = new ArrayList<>();
451 
452         public QueryExpressionHavingBuilder(SqlCriterion initialCriterion) {
453             this.initialCriterion = initialCriterion;
454         }
455 
456         public QueryExpressionHavingBuilder(SqlCriterion initialCriterion, List<AndOrCriteriaGroup> subCriteria) {
457             this(initialCriterion);
458             this.subCriteria.addAll(subCriteria);
459         }
460 
461         @Override
462         public QueryExpressionHavingBuilder addSubCriterion(AndOrCriteriaGroup subCriterion) {
463             subCriteria.add(subCriterion);
464             return this;
465         }
466 
467         @Override
468         public SelectDSL<R> orderBy(Collection<? extends SortSpecification> columns) {
469             return QueryExpressionDSL.this.orderBy(columns);
470         }
471 
472         public UnionBuilder union() {
473             return QueryExpressionDSL.this.union();
474         }
475 
476         public UnionBuilder unionAll() {
477             return QueryExpressionDSL.this.unionAll();
478         }
479 
480         @Override
481         public R build() {
482             return QueryExpressionDSL.this.build();
483         }
484 
485         protected HavingModel buildHavingModel() {
486             return new HavingModel.Builder()
487                     .withInitialCriterion(initialCriterion)
488                     .withSubCriteria(subCriteria)
489                     .build();
490         }
491 
492         @Override
493         public SelectDSL<R> setWaitClause(String waitClause) {
494             return QueryExpressionDSL.this.setWaitClause(waitClause);
495         }
496 
497         @Override
498         public SelectDSL<R> setForClause(String forClause) {
499             return QueryExpressionDSL.this.setForClause(forClause);
500         }
501 
502         @Override
503         public LimitFinisher<SelectDSL<R>, R> limitWhenPresent(@Nullable Long limit) {
504             return QueryExpressionDSL.this.limitWhenPresent(limit);
505         }
506 
507         @Override
508         public OffsetFirstFinisher<SelectDSL<R>, R> offsetWhenPresent(@Nullable Long offset) {
509             return QueryExpressionDSL.this.offsetWhenPresent(offset);
510         }
511 
512         @Override
513         public FetchFirstFinisher<SelectDSL<R>> fetchFirstWhenPresent(@Nullable Long fetchFirstRows) {
514             return QueryExpressionDSL.this.fetchFirstWhenPresent(fetchFirstRows);
515         }
516     }
517 
518     public static class Builder<R> {
519         private @Nullable String connector;
520         private final List<BasicColumn> selectList = new ArrayList<>();
521         private @Nullable SelectDSL<R> selectDSL;
522         private boolean isDistinct;
523 
524         public Builder<R> withConnector(String connector) {
525             this.connector = connector;
526             return this;
527         }
528 
529         public Builder<R> withSelectList(Collection<? extends BasicColumn> selectList) {
530             this.selectList.addAll(selectList);
531             return this;
532         }
533 
534         public Builder<R> withSelectDSL(SelectDSL<R> selectDSL) {
535             this.selectDSL = selectDSL;
536             return this;
537         }
538 
539         public Builder<R> isDistinct() {
540             this.isDistinct = true;
541             return this;
542         }
543 
544         public QueryExpressionDSL<R> build() {
545             return new QueryExpressionDSL<>(this);
546         }
547     }
548 }