View Javadoc
1   /*
2    *    Copyright 2016-2025 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.Optional;
24  import java.util.function.Consumer;
25  import java.util.function.Function;
26  
27  import org.jspecify.annotations.Nullable;
28  import org.mybatis.dynamic.sql.BasicColumn;
29  import org.mybatis.dynamic.sql.SortSpecification;
30  import org.mybatis.dynamic.sql.common.OrderByModel;
31  import org.mybatis.dynamic.sql.configuration.StatementConfiguration;
32  import org.mybatis.dynamic.sql.select.QueryExpressionDSL.FromGatherer;
33  import org.mybatis.dynamic.sql.util.Buildable;
34  import org.mybatis.dynamic.sql.util.ConfigurableStatement;
35  import org.mybatis.dynamic.sql.util.Validator;
36  
37  /**
38   * Implements a SQL DSL for building select statements.
39   *
40   * @author Jeff Butler
41   *
42   * @param <R>
43   *            the type of model produced by this builder, typically SelectModel
44   */
45  public class SelectDSL<R> implements Buildable<R>, ConfigurableStatement<SelectDSL<R>> {
46  
47      private final Function<SelectModel, R> adapterFunction;
48      private final List<QueryExpressionDSL<R>> queryExpressions = new ArrayList<>();
49      private @Nullable OrderByModel orderByModel;
50      private @Nullable Long limit;
51      private @Nullable Long offset;
52      private @Nullable Long fetchFirstRows;
53      final StatementConfiguration statementConfiguration = new StatementConfiguration();
54      private @Nullable String forClause;
55      private @Nullable String waitClause;
56  
57      private SelectDSL(Function<SelectModel, R> adapterFunction) {
58          this.adapterFunction = Objects.requireNonNull(adapterFunction);
59      }
60  
61      public static QueryExpressionDSL.FromGatherer<SelectModel> select(BasicColumn... selectList) {
62          return select(Arrays.asList(selectList));
63      }
64  
65      public static QueryExpressionDSL.FromGatherer<SelectModel> select(Collection<? extends BasicColumn> selectList) {
66          return select(Function.identity(), selectList);
67      }
68  
69      public static <R> QueryExpressionDSL.FromGatherer<R> select(Function<SelectModel, R> adapterFunction,
70              BasicColumn... selectList) {
71          return select(adapterFunction, Arrays.asList(selectList));
72      }
73  
74      public static <R> QueryExpressionDSL.FromGatherer<R> select(Function<SelectModel, R> adapterFunction,
75              Collection<? extends BasicColumn> selectList) {
76          return new FromGatherer.Builder<R>()
77                  .withSelectList(selectList)
78                  .withSelectDSL(new SelectDSL<>(adapterFunction))
79                  .build();
80      }
81  
82      public static QueryExpressionDSL.FromGatherer<SelectModel> selectDistinct(BasicColumn... selectList) {
83          return selectDistinct(Function.identity(), selectList);
84      }
85  
86      public static QueryExpressionDSL.FromGatherer<SelectModel> selectDistinct(
87              Collection<? extends BasicColumn> selectList) {
88          return selectDistinct(Function.identity(), selectList);
89      }
90  
91      public static <R> QueryExpressionDSL.FromGatherer<R> selectDistinct(Function<SelectModel, R> adapterFunction,
92              BasicColumn... selectList) {
93          return selectDistinct(adapterFunction, Arrays.asList(selectList));
94      }
95  
96      public static <R> QueryExpressionDSL.FromGatherer<R> selectDistinct(Function<SelectModel, R> adapterFunction,
97              Collection<? extends BasicColumn> selectList) {
98          return new FromGatherer.Builder<R>()
99                  .withSelectList(selectList)
100                 .withSelectDSL(new SelectDSL<>(adapterFunction))
101                 .isDistinct()
102                 .build();
103     }
104 
105     void registerQueryExpression(QueryExpressionDSL<R> queryExpression) {
106         queryExpressions.add(queryExpression);
107     }
108 
109     void orderBy(Collection<? extends SortSpecification> columns) {
110         orderByModel = OrderByModel.of(columns);
111     }
112 
113     public SelectDSL<R>.LimitFinisher limit(long limit) {
114         return limitWhenPresent(limit);
115     }
116 
117     public SelectDSL<R>.LimitFinisher limitWhenPresent(@Nullable Long limit) {
118         this.limit = limit;
119         return new LimitFinisher();
120     }
121 
122     public SelectDSL<R>.OffsetFirstFinisher offset(long offset) {
123         return offsetWhenPresent(offset);
124     }
125 
126     public SelectDSL<R>.OffsetFirstFinisher offsetWhenPresent(@Nullable Long offset) {
127         this.offset = offset;
128         return new OffsetFirstFinisher();
129     }
130 
131     public SelectDSL<R>.FetchFirstFinisher fetchFirst(long fetchFirstRows) {
132         return fetchFirstWhenPresent(fetchFirstRows);
133     }
134 
135     public SelectDSL<R>.FetchFirstFinisher fetchFirstWhenPresent(@Nullable Long fetchFirstRows) {
136         this.fetchFirstRows = fetchFirstRows;
137         return new FetchFirstFinisher();
138     }
139 
140     public SelectDSL<R> forUpdate() {
141         Validator.assertNull(forClause, "ERROR.48"); //$NON-NLS-1$
142         forClause = "for update"; //$NON-NLS-1$
143         return this;
144     }
145 
146     public SelectDSL<R> forNoKeyUpdate() {
147         Validator.assertNull(forClause, "ERROR.48"); //$NON-NLS-1$
148         forClause = "for no key update"; //$NON-NLS-1$
149         return this;
150     }
151 
152     public SelectDSL<R> forShare() {
153         Validator.assertNull(forClause, "ERROR.48"); //$NON-NLS-1$
154         forClause = "for share"; //$NON-NLS-1$
155         return this;
156     }
157 
158     public SelectDSL<R> forKeyShare() {
159         Validator.assertNull(forClause, "ERROR.48"); //$NON-NLS-1$
160         forClause = "for key share"; //$NON-NLS-1$
161         return this;
162     }
163 
164     public SelectDSL<R> skipLocked() {
165         Validator.assertNull(waitClause, "ERROR.49"); //$NON-NLS-1$
166         waitClause = "skip locked"; //$NON-NLS-1$
167         return this;
168     }
169 
170     public SelectDSL<R> nowait() {
171         Validator.assertNull(waitClause, "ERROR.49"); //$NON-NLS-1$
172         waitClause = "nowait"; //$NON-NLS-1$
173         return this;
174     }
175 
176     @Override
177     public SelectDSL<R> configureStatement(Consumer<StatementConfiguration> consumer) {
178         consumer.accept(statementConfiguration);
179         return this;
180     }
181 
182     @Override
183     public R build() {
184         SelectModel selectModel = SelectModel.withQueryExpressions(buildModels())
185                 .withOrderByModel(orderByModel)
186                 .withPagingModel(buildPagingModel().orElse(null))
187                 .withStatementConfiguration(statementConfiguration)
188                 .withForClause(forClause)
189                 .withWaitClause(waitClause)
190                 .build();
191         return adapterFunction.apply(selectModel);
192     }
193 
194     private List<QueryExpressionModel> buildModels() {
195         return queryExpressions.stream()
196                 .map(QueryExpressionDSL::buildModel)
197                 .toList();
198     }
199 
200     private Optional<PagingModel> buildPagingModel() {
201         return new PagingModel.Builder()
202                 .withLimit(limit)
203                 .withOffset(offset)
204                 .withFetchFirstRows(fetchFirstRows)
205                 .build();
206     }
207 
208     public class OffsetFirstFinisher implements SelectDSLForAndWaitOperations<R>, Buildable<R> {
209         public FetchFirstFinisher fetchFirst(long fetchFirstRows) {
210             return fetchFirstWhenPresent(fetchFirstRows);
211         }
212 
213         public FetchFirstFinisher fetchFirstWhenPresent(@Nullable Long fetchFirstRows) {
214             SelectDSL.this.fetchFirstRows = fetchFirstRows;
215             return new FetchFirstFinisher();
216         }
217 
218         @Override
219         public SelectDSL<R> getSelectDSL() {
220             return SelectDSL.this;
221         }
222 
223         @Override
224         public R build() {
225             return SelectDSL.this.build();
226         }
227     }
228 
229     public class LimitFinisher implements SelectDSLForAndWaitOperations<R>, Buildable<R> {
230         public SelectDSL<R> offset(long offset) {
231             return offsetWhenPresent(offset);
232         }
233 
234         public SelectDSL<R> offsetWhenPresent(@Nullable Long offset) {
235             SelectDSL.this.offset = offset;
236             return SelectDSL.this;
237         }
238 
239         @Override
240         public SelectDSL<R> getSelectDSL() {
241             return SelectDSL.this;
242         }
243 
244         @Override
245         public R build() {
246             return SelectDSL.this.build();
247         }
248     }
249 
250     public class FetchFirstFinisher {
251         public SelectDSL<R> rowsOnly() {
252             return SelectDSL.this;
253         }
254     }
255 }