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 static org.assertj.core.api.Assertions.assertThat;
19  import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
20  import static org.junit.jupiter.api.Assertions.assertAll;
21  import static org.mybatis.dynamic.sql.SqlBuilder.*;
22  
23  import java.sql.JDBCType;
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.Date;
27  import java.util.List;
28  import java.util.Map;
29  
30  import org.junit.jupiter.api.Test;
31  import org.mybatis.dynamic.sql.SortSpecification;
32  import org.mybatis.dynamic.sql.SqlColumn;
33  import org.mybatis.dynamic.sql.SqlTable;
34  import org.mybatis.dynamic.sql.exception.NonRenderingWhereClauseException;
35  import org.mybatis.dynamic.sql.render.RenderingStrategies;
36  import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
37  
38  class SelectStatementTest {
39  
40      static final SqlTable table = SqlTable.of("foo");
41      static final SqlColumn<Date> column1 = table.column("column1", JDBCType.DATE);
42      static final SqlColumn<Integer> column2 = table.column("column2", JDBCType.INTEGER);
43      static final SqlColumn<String> column3 = table.column("column3", JDBCType.VARCHAR);
44  
45      @Test
46      void testSimpleCriteria() {
47          Date d = new Date();
48  
49          SelectStatementProvider selectStatement = select(column1.as("A_COLUMN1"), column2)
50                  .from(table, "a")
51                  .where(column1, isEqualTo(d), and(column2, isEqualTo(33)))
52                  .or(column2, isEqualTo(4))
53                  .and(column2, isLessThan(3))
54                  .build()
55                  .render(RenderingStrategies.MYBATIS3);
56  
57          String expectedFullStatement = "select a.column1 as A_COLUMN1, a.column2 "
58                  + "from foo a "
59                  + "where (a.column1 = #{parameters.p1,jdbcType=DATE} and a.column2 = #{parameters.p2,jdbcType=INTEGER}) or a.column2 = #{parameters.p3,jdbcType=INTEGER} and a.column2 < #{parameters.p4,jdbcType=INTEGER}";
60  
61          Map<String, Object> parameters = selectStatement.getParameters();
62  
63          assertAll(
64                  () -> assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedFullStatement),
65                  () -> assertThat(parameters).containsEntry("p1", d),
66                  () -> assertThat(parameters).containsEntry("p2", 33),
67                  () -> assertThat(parameters).containsEntry("p3", 4),
68                  () -> assertThat(parameters).containsEntry("p4", 3)
69          );
70      }
71  
72      @Test
73      void testComplexCriteria() {
74          Date d = new Date();
75  
76          SelectStatementProvider selectStatement = select(column1.as("A_COLUMN1"), column2)
77                  .from(table, "a")
78                  .where(column1, isEqualTo(d))
79                  .or(column2, isEqualTo(4))
80                  .and(column2, isLessThan(3))
81                  .or(column2, isEqualTo(4), and(column2, isEqualTo(6)))
82                  .and(column2, isLessThan(3), or(column1, isEqualTo(d)))
83                  .build()
84                  .render(RenderingStrategies.MYBATIS3);
85  
86          String expectedFullStatement = "select a.column1 as A_COLUMN1, a.column2 "
87                  + "from foo a "
88                  + "where a.column1 = #{parameters.p1,jdbcType=DATE}"
89                  + " or a.column2 = #{parameters.p2,jdbcType=INTEGER}"
90                  + " and a.column2 < #{parameters.p3,jdbcType=INTEGER}"
91                  + " or (a.column2 = #{parameters.p4,jdbcType=INTEGER} and a.column2 = #{parameters.p5,jdbcType=INTEGER})"
92                  + " and (a.column2 < #{parameters.p6,jdbcType=INTEGER} or a.column1 = #{parameters.p7,jdbcType=DATE})";
93  
94          Map<String, Object> parameters = selectStatement.getParameters();
95  
96          assertAll(
97                  () -> assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedFullStatement),
98                  () -> assertThat(parameters).containsEntry("p1", d),
99                  () -> assertThat(parameters).containsEntry("p2", 4),
100                 () -> assertThat(parameters).containsEntry("p3", 3),
101                 () -> assertThat(parameters).containsEntry("p4", 4),
102                 () -> assertThat(parameters).containsEntry("p5", 6),
103                 () -> assertThat(parameters).containsEntry("p6", 3),
104                 () -> assertThat(parameters).containsEntry("p7", d)
105         );
106     }
107 
108     @Test
109     void testOrderBySingleColumnAscending() {
110         Date d = new Date();
111 
112         SelectStatementProvider selectStatement = select(column1.as("A_COLUMN1"), column2)
113                 .from(table, "a")
114                 .where(column1, isEqualTo(d))
115                 .orderBy(column1)
116                 .build()
117                 .render(RenderingStrategies.MYBATIS3);
118 
119         String expectedFullStatement = "select a.column1 as A_COLUMN1, a.column2 "
120                 + "from foo a "
121                 + "where a.column1 = #{parameters.p1,jdbcType=DATE} "
122                 + "order by column1";
123 
124         Map<String, Object> parameters = selectStatement.getParameters();
125 
126         assertAll(
127                 () -> assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedFullStatement),
128                 () -> assertThat(parameters).containsEntry("p1", d)
129         );
130     }
131 
132     @Test
133     void testOrderBySingleColumnDescending() {
134         Date d = new Date();
135 
136         SelectStatementProvider selectStatement = select(column1.as("A_COLUMN1"), column2)
137                 .from(table, "a")
138                 .where(column1, isEqualTo(d))
139                 .orderBy(column2.descending())
140                 .build()
141                 .render(RenderingStrategies.MYBATIS3);
142 
143         String expectedFullStatement = "select a.column1 as A_COLUMN1, a.column2 "
144                 + "from foo a "
145                 + "where a.column1 = #{parameters.p1,jdbcType=DATE} "
146                 + "order by column2 DESC";
147 
148         Map<String, Object> parameters = selectStatement.getParameters();
149 
150         assertAll(
151                 () -> assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedFullStatement),
152                 () -> assertThat(parameters).containsEntry("p1", d)
153         );
154     }
155 
156     @Test
157     void testOrderByMultipleColumns() {
158         Date d = new Date();
159 
160         SelectStatementProvider selectStatement = select(column1.as("A_COLUMN1"), column2)
161                 .from(table, "a")
162                 .where(column1, isEqualTo(d))
163                 .orderBy(column2.descending(), column1)
164                 .build()
165                 .render(RenderingStrategies.MYBATIS3);
166 
167         String expectedFullStatement = "select a.column1 as A_COLUMN1, a.column2 "
168                 + "from foo a "
169                 + "where a.column1 = #{parameters.p1,jdbcType=DATE} "
170                 + "order by column2 DESC, column1";
171 
172         Map<String, Object> parameters = selectStatement.getParameters();
173 
174         assertAll(
175                 () -> assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedFullStatement),
176                 () -> assertThat(parameters).containsEntry("p1", d)
177         );
178     }
179 
180     @Test
181     void testOrderByMultipleColumnsWithCollection() {
182         Collection<SortSpecification> orderByColumns = List.of(column2.descending(), column1);
183 
184         SelectStatementProvider selectStatement = select(column1.as("A_COLUMN1"), column2)
185                 .from(table, "a")
186                 .orderBy(orderByColumns)
187                 .build()
188                 .render(RenderingStrategies.MYBATIS3);
189 
190         String expectedFullStatement = "select a.column1 as A_COLUMN1, a.column2 "
191                 + "from foo a "
192                 + "order by column2 DESC, column1";
193 
194         assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedFullStatement);
195     }
196 
197     @Test
198     void testDistinct() {
199         Date d = new Date();
200 
201         SelectStatementProvider selectStatement = selectDistinct(column1.as("A_COLUMN1"), column2)
202                 .from(table, "a")
203                 .where(column1, isEqualTo(d))
204                 .orderBy(column2.descending(), column1)
205                 .build()
206                 .render(RenderingStrategies.MYBATIS3);
207 
208         String expectedFullStatement = "select distinct a.column1 as A_COLUMN1, a.column2 "
209                 + "from foo a "
210                 + "where a.column1 = #{parameters.p1,jdbcType=DATE} "
211                 + "order by column2 DESC, column1";
212 
213         Map<String, Object> parameters = selectStatement.getParameters();
214 
215         assertAll(
216                 () -> assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedFullStatement),
217                 () -> assertThat(parameters).containsEntry("p1", d)
218         );
219     }
220 
221     @Test
222     void testCount() {
223         Date d = new Date();
224 
225         SelectStatementProvider selectStatement = select(count())
226                 .from(table, "a")
227                 .where(column1, isEqualTo(d))
228                 .build()
229                 .render(RenderingStrategies.MYBATIS3);
230 
231         String expectedFullStatement = "select count(*) "
232                 + "from foo a "
233                 + "where a.column1 = #{parameters.p1,jdbcType=DATE}";
234 
235         Map<String, Object> parameters = selectStatement.getParameters();
236 
237         assertAll(
238                 () -> assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedFullStatement),
239                 () -> assertThat(parameters).containsEntry("p1", d)
240         );
241     }
242 
243     @Test
244     void testNoWhere() {
245         SelectStatementProvider selectStatement = select(count())
246                 .from(table, "a")
247                 .build()
248                 .render(RenderingStrategies.MYBATIS3);
249 
250         String expectedFullStatement = "select count(*) "
251                 + "from foo a";
252 
253         Map<String, Object> parameters = selectStatement.getParameters();
254 
255         assertAll(
256                 () -> assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedFullStatement),
257                 () -> assertThat(parameters).isEmpty()
258         );
259     }
260 
261     @Test
262     void testGroupBySingleColumn() {
263         Date d = new Date();
264 
265         SelectStatementProvider selectStatement = select(column1.as("A_COLUMN1"), column2)
266                 .from(table, "a")
267                 .where(column1, isEqualTo(d))
268                 .groupBy(column2)
269                 .build()
270                 .render(RenderingStrategies.MYBATIS3);
271 
272         String expectedFullStatement = "select a.column1 as A_COLUMN1, a.column2 "
273                 + "from foo a "
274                 + "where a.column1 = #{parameters.p1,jdbcType=DATE} "
275                 + "group by a.column2";
276 
277         Map<String, Object> parameters = selectStatement.getParameters();
278 
279         assertAll(
280                 () -> assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedFullStatement),
281                 () -> assertThat(parameters).containsEntry("p1", d)
282         );
283     }
284 
285     @Test
286     void testNotInEmptyList() {
287         List<String> emptyList = Collections.emptyList();
288         SelectModel selectModel = select(column1, column3)
289                 .from(table, "a")
290                 .where(column3, isNotInWhenPresent(emptyList))
291                 .build();
292 
293         assertThatExceptionOfType(RuntimeException.class).isThrownBy(() ->
294                 selectModel.render(RenderingStrategies.MYBATIS3)
295         );
296     }
297 
298     @Test
299     void testInWhenPresentEmptyList() {
300         List<String> emptyList = Collections.emptyList();
301         SelectModel selectModel = select(column1, column3)
302                 .from(table, "a")
303                 .where(column3, isInWhenPresent(emptyList))
304                 .build();
305 
306         assertThatExceptionOfType(RuntimeException.class).isThrownBy(() ->
307                 selectModel.render(RenderingStrategies.MYBATIS3)
308         );
309     }
310 
311     @Test
312     void testInCaseInsensitiveEmptyList() {
313         SelectModel selectModel = select(column1, column3)
314                 .from(table, "a")
315                 .where(column3, isInCaseInsensitiveWhenPresent(Collections.emptyList()))
316                 .build();
317 
318         assertThatExceptionOfType(RuntimeException.class).isThrownBy(() ->
319                 selectModel.render(RenderingStrategies.MYBATIS3)
320         );
321     }
322 
323     @Test
324     void testInCaseInsensitiveWhenPresentEmptyList() {
325         SelectModel selectModel = select(column1, column3)
326                 .from(table, "a")
327                 .where(column3, isInCaseInsensitiveWhenPresent(Collections.emptyList()))
328                 .build();
329 
330         assertThatExceptionOfType(NonRenderingWhereClauseException.class).isThrownBy(() ->
331                 selectModel.render(RenderingStrategies.MYBATIS3)
332         );
333     }
334 
335     @Test
336     void testNotInWhenPresentEmptyList() {
337         List<String> emptyList = Collections.emptyList();
338         SelectModel selectModel = select(column1, column3)
339                 .from(table, "a")
340                 .where(column3, isNotInWhenPresent(emptyList))
341                 .build();
342 
343         assertThatExceptionOfType(NonRenderingWhereClauseException.class).isThrownBy(() ->
344                 selectModel.render(RenderingStrategies.MYBATIS3)
345         );
346     }
347 
348     @Test
349     void testNotInCaseInsensitiveWhenPresentEmptyList() {
350         SelectModel selectModel = select(column1, column3)
351                 .from(table, "a")
352                 .where(column3, isNotInCaseInsensitiveWhenPresent(Collections.emptyList()))
353                 .build();
354 
355         assertThatExceptionOfType(NonRenderingWhereClauseException.class).isThrownBy(() ->
356                 selectModel.render(RenderingStrategies.MYBATIS3)
357         );
358     }
359 
360     @Test
361     void testInWhenPresentNullList() {
362         SelectStatementProvider selectStatement = select(column1, column3)
363                 .from(table)
364                 .where(column3, isInWhenPresent((Collection<String>) null))
365                 .configureStatement(c -> c.setNonRenderingWhereClauseAllowed(true))
366                 .build()
367                 .render(RenderingStrategies.MYBATIS3);
368 
369         assertThat(selectStatement.getSelectStatement()).isEqualTo("select column1, column3 from foo");
370     }
371 
372     @Test
373     void testInCaseInsensitiveWhenPresentNullList() {
374         SelectStatementProvider selectStatement = select(column1, column3)
375                 .from(table)
376                 .where(column3, isInCaseInsensitiveWhenPresent((Collection<String>) null))
377                 .configureStatement(c -> c.setNonRenderingWhereClauseAllowed(true))
378                 .build()
379                 .render(RenderingStrategies.MYBATIS3);
380 
381         assertThat(selectStatement.getSelectStatement()).isEqualTo("select column1, column3 from foo");
382     }
383 
384     @Test
385     void testNotInWhenPresentNullList() {
386         SelectStatementProvider selectStatement = select(column1, column3)
387                 .from(table)
388                 .where(column3, isNotInWhenPresent((Collection<String>) null))
389                 .configureStatement(c -> c.setNonRenderingWhereClauseAllowed(true))
390                 .build()
391                 .render(RenderingStrategies.MYBATIS3);
392 
393         assertThat(selectStatement.getSelectStatement()).isEqualTo("select column1, column3 from foo");
394     }
395 
396     @Test
397     void testNotInCaseInsensitiveWhenPresentNullList() {
398         SelectStatementProvider selectStatement = select(column1, column3)
399                 .from(table)
400                 .where(column3, isNotInCaseInsensitiveWhenPresent((Collection<String>) null))
401                 .configureStatement(c -> c.setNonRenderingWhereClauseAllowed(true))
402                 .build()
403                 .render(RenderingStrategies.MYBATIS3);
404 
405         assertThat(selectStatement.getSelectStatement()).isEqualTo("select column1, column3 from foo");
406     }
407 }