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 examples.animal.data;
17  
18  import static examples.animal.data.AnimalDataDynamicSqlSupport.animalData;
19  import static examples.animal.data.AnimalDataDynamicSqlSupport.animalName;
20  import static examples.animal.data.AnimalDataDynamicSqlSupport.id;
21  import static org.assertj.core.api.Assertions.assertThat;
22  import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
23  import static org.mybatis.dynamic.sql.SqlBuilder.isIn;
24  import static org.mybatis.dynamic.sql.SqlBuilder.isInCaseInsensitive;
25  import static org.mybatis.dynamic.sql.SqlBuilder.isInCaseInsensitiveWhenPresent;
26  import static org.mybatis.dynamic.sql.SqlBuilder.isInWhenPresent;
27  import static org.mybatis.dynamic.sql.SqlBuilder.isNotIn;
28  import static org.mybatis.dynamic.sql.SqlBuilder.isNotInCaseInsensitive;
29  import static org.mybatis.dynamic.sql.SqlBuilder.isNotInCaseInsensitiveWhenPresent;
30  import static org.mybatis.dynamic.sql.SqlBuilder.isNotInWhenPresent;
31  import static org.mybatis.dynamic.sql.SqlBuilder.select;
32  
33  import java.io.InputStream;
34  import java.io.InputStreamReader;
35  import java.sql.Connection;
36  import java.sql.DriverManager;
37  import java.util.Collection;
38  import java.util.Collections;
39  import java.util.List;
40  import java.util.Map;
41  
42  import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
43  import org.apache.ibatis.jdbc.ScriptRunner;
44  import org.apache.ibatis.mapping.Environment;
45  import org.apache.ibatis.session.Configuration;
46  import org.apache.ibatis.session.SqlSession;
47  import org.apache.ibatis.session.SqlSessionFactory;
48  import org.apache.ibatis.session.SqlSessionFactoryBuilder;
49  import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
50  import org.junit.jupiter.api.BeforeEach;
51  import org.junit.jupiter.api.Test;
52  import org.mybatis.dynamic.sql.exception.InvalidSqlException;
53  import org.mybatis.dynamic.sql.render.RenderingStrategies;
54  import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
55  import org.mybatis.dynamic.sql.util.Messages;
56  import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper;
57  
58  class VariousListConditionsTest {
59      private static final String JDBC_URL = "jdbc:hsqldb:mem:aname";
60      private static final String JDBC_DRIVER = "org.hsqldb.jdbcDriver";
61  
62      private SqlSessionFactory sqlSessionFactory;
63  
64      @BeforeEach
65      void setup() throws Exception {
66          Class.forName(JDBC_DRIVER);
67          try (InputStream is = getClass().getResourceAsStream("/examples/animal/data/CreateAnimalData.sql")) {
68              assert is != null;
69              try (Connection connection = DriverManager.getConnection(JDBC_URL, "sa", "");
70                  InputStreamReader isr = new InputStreamReader(is)) {
71                  ScriptRunner sr = new ScriptRunner(connection);
72                  sr.setLogWriter(null);
73                  sr.runScript(isr);
74              }
75          }
76  
77          UnpooledDataSource ds = new UnpooledDataSource(JDBC_DRIVER, JDBC_URL, "sa", "");
78          Environment environment = new Environment("test", new JdbcTransactionFactory(), ds);
79          Configuration config = new Configuration(environment);
80          config.addMapper(CommonSelectMapper.class);
81          sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
82      }
83  
84      @Test
85      void testInWithNull() {
86          try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
87              CommonSelectMapper mapper = sqlSession.getMapper(CommonSelectMapper.class);
88  
89              SelectStatementProvider selectStatement = select(id, animalName)
90                      .from(animalData)
91                      .where(id, isInWhenPresent(2, 3, null))
92                      .orderBy(id)
93                      .build()
94                      .render(RenderingStrategies.MYBATIS3);
95  
96              assertThat(selectStatement.getSelectStatement()).isEqualTo(
97                      "select id, animal_name from AnimalData where id " +
98                              "in (#{parameters.p1,jdbcType=INTEGER},#{parameters.p2,jdbcType=INTEGER}) " +
99                              "order by id"
100             );
101             assertThat(selectStatement.getParameters()).containsEntry("p1", 2);
102             assertThat(selectStatement.getParameters()).containsEntry("p2", 3);
103 
104             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
105             assertThat(rows).hasSize(2);
106 
107             assertThat(rows.get(0)).containsEntry("ID", 2);
108         }
109     }
110 
111     @Test
112     void testInWhenPresentWithNull() {
113         try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
114             CommonSelectMapper mapper = sqlSession.getMapper(CommonSelectMapper.class);
115 
116             SelectStatementProvider selectStatement = select(id, animalName)
117                     .from(animalData)
118                     .where(id, isInWhenPresent(2, 3, null))
119                     .orderBy(id)
120                     .build()
121                     .render(RenderingStrategies.MYBATIS3);
122 
123             assertThat(selectStatement.getSelectStatement()).isEqualTo(
124                     "select id, animal_name from AnimalData " +
125                             "where id in (#{parameters.p1,jdbcType=INTEGER},#{parameters.p2,jdbcType=INTEGER}) " +
126                             "order by id"
127             );
128             assertThat(selectStatement.getParameters()).containsEntry("p1", 2);
129             assertThat(selectStatement.getParameters()).containsEntry("p2", 3);
130 
131             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
132             assertThat(rows).hasSize(2);
133 
134             assertThat(rows.get(0)).containsEntry("ID", 2);
135         }
136     }
137 
138     @Test
139     void testInWithEmptyList() {
140         var selectModel = select(id, animalName)
141                 .from(animalData)
142                 .where(id, isIn(Collections.emptyList()))
143                 .orderBy(id)
144                 .build();
145 
146         assertThatExceptionOfType(InvalidSqlException.class)
147                 .isThrownBy(() -> selectModel.render(RenderingStrategies.MYBATIS3))
148                 .withMessage(Messages.getString("ERROR.44", "IsIn"));
149     }
150 
151     @Test
152     void testInWhenPresentWithEmptyList() {
153         try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
154             CommonSelectMapper mapper = sqlSession.getMapper(CommonSelectMapper.class);
155 
156             SelectStatementProvider selectStatement = select(id, animalName)
157                     .from(animalData)
158                     .where(id, isInWhenPresent(Collections.emptyList()))
159                     .orderBy(id)
160                     .configureStatement(c -> c.setNonRenderingWhereClauseAllowed(true))
161                     .build()
162                     .render(RenderingStrategies.MYBATIS3);
163 
164             assertThat(selectStatement.getSelectStatement()).isEqualTo(
165                     "select id, animal_name from AnimalData " +
166                             "order by id");
167 
168             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
169             assertThat(rows).hasSize(65);
170 
171             assertThat(rows.get(0)).containsEntry("ID", 1);
172         }
173     }
174 
175     @Test
176     void testInWhenPresentWithNullList() {
177         try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
178             CommonSelectMapper mapper = sqlSession.getMapper(CommonSelectMapper.class);
179 
180             SelectStatementProvider selectStatement = select(id, animalName)
181                     .from(animalData)
182                     .where(id, isInWhenPresent((Collection<Integer>) null))
183                     .orderBy(id)
184                     .configureStatement(c -> c.setNonRenderingWhereClauseAllowed(true))
185                     .build()
186                     .render(RenderingStrategies.MYBATIS3);
187 
188             assertThat(selectStatement.getSelectStatement()).isEqualTo(
189                     "select id, animal_name from AnimalData " +
190                             "order by id");
191 
192             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
193             assertThat(rows).hasSize(65);
194 
195             assertThat(rows.get(0)).containsEntry("ID", 1);
196         }
197     }
198 
199     @Test
200     void testInWhenPresentMap() {
201         try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
202             CommonSelectMapper mapper = sqlSession.getMapper(CommonSelectMapper.class);
203 
204             SelectStatementProvider selectStatement = select(id, animalName)
205                     .from(animalData)
206                     .where(id, isInWhenPresent(2, 3).map(i -> i + 3))
207                     .orderBy(id)
208                     .build()
209                     .render(RenderingStrategies.MYBATIS3);
210 
211             assertThat(selectStatement.getSelectStatement()).isEqualTo(
212                     "select id, animal_name from AnimalData " +
213                             "where id in (#{parameters.p1,jdbcType=INTEGER},#{parameters.p2,jdbcType=INTEGER}) " +
214                             "order by id"
215             );
216             assertThat(selectStatement.getParameters()).containsEntry("p1", 5);
217             assertThat(selectStatement.getParameters()).containsEntry("p2", 6);
218 
219             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
220             assertThat(rows).hasSize(2);
221 
222             assertThat(rows.get(0)).containsEntry("ID", 5);
223         }
224     }
225 
226     @Test
227     void testNotInWhenPresentMap() {
228         try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
229             CommonSelectMapper mapper = sqlSession.getMapper(CommonSelectMapper.class);
230 
231             SelectStatementProvider selectStatement = select(id, animalName)
232                     .from(animalData)
233                     .where(id, isNotInWhenPresent(2, 3).map(i -> i + 3))
234                     .orderBy(id)
235                     .build()
236                     .render(RenderingStrategies.MYBATIS3);
237 
238             assertThat(selectStatement.getSelectStatement()).isEqualTo(
239                     "select id, animal_name from AnimalData " +
240                             "where id not in (#{parameters.p1,jdbcType=INTEGER},#{parameters.p2,jdbcType=INTEGER}) " +
241                             "order by id"
242             );
243             assertThat(selectStatement.getParameters()).containsEntry("p1", 5);
244             assertThat(selectStatement.getParameters()).containsEntry("p2", 6);
245 
246             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
247             assertThat(rows).hasSize(63);
248 
249             assertThat(rows.get(0)).containsEntry("ID", 1);
250         }
251     }
252 
253     @Test
254     void testInCaseInsensitiveWhenPresentMap() {
255         try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
256             CommonSelectMapper mapper = sqlSession.getMapper(CommonSelectMapper.class);
257 
258             SelectStatementProvider selectStatement = select(id, animalName)
259                     .from(animalData)
260                     .where(animalName, isInCaseInsensitiveWhenPresent("Fred", "Betty").filter(s -> false))
261                     .orderBy(id)
262                     .configureStatement(c -> c.setNonRenderingWhereClauseAllowed(true))
263                     .build()
264                     .render(RenderingStrategies.MYBATIS3);
265 
266             assertThat(selectStatement.getSelectStatement()).isEqualTo(
267                     "select id, animal_name from AnimalData " +
268                             "order by id"
269             );
270 
271             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
272             assertThat(rows).hasSize(65);
273 
274             assertThat(rows.get(0)).containsEntry("ID", 1);
275         }
276     }
277 
278     @Test
279     void testNotInCaseInsensitiveWhenPresentMap() {
280         try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
281             CommonSelectMapper mapper = sqlSession.getMapper(CommonSelectMapper.class);
282 
283             SelectStatementProvider selectStatement = select(id, animalName)
284                     .from(animalData)
285                     .where(animalName, isNotInCaseInsensitiveWhenPresent("Fred", "Betty").filter(s -> false))
286                     .orderBy(id)
287                     .configureStatement(c -> c.setNonRenderingWhereClauseAllowed(true))
288                     .build()
289                     .render(RenderingStrategies.MYBATIS3);
290 
291             assertThat(selectStatement.getSelectStatement()).isEqualTo(
292                     "select id, animal_name from AnimalData " +
293                             "order by id"
294             );
295 
296             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
297             assertThat(rows).hasSize(65);
298 
299             assertThat(rows.get(0)).containsEntry("ID", 1);
300         }
301     }
302 
303     @Test
304     void testInEventuallyEmpty() {
305         var selectModel = select(id, animalName)
306                 .from(animalData)
307                 .where(id, isIn(1, 2).filter(s -> false))
308                 .orderBy(id)
309                 .build();
310 
311         assertThatExceptionOfType(InvalidSqlException.class)
312                 .isThrownBy(() -> selectModel.render(RenderingStrategies.MYBATIS3))
313                 .withMessage(Messages.getString("ERROR.44", "IsIn"));
314     }
315 
316     @Test
317     void testInCaseInsensitiveEventuallyEmpty() {
318         var selectModel = select(id, animalName)
319                 .from(animalData)
320                 .where(animalName, isInCaseInsensitive("Fred", "Betty").filter(s -> false))
321                 .orderBy(id)
322                 .build();
323 
324         assertThatExceptionOfType(InvalidSqlException.class)
325                 .isThrownBy(() -> selectModel.render(RenderingStrategies.MYBATIS3))
326                 .withMessage(Messages.getString("ERROR.44", "IsInCaseInsensitive"));
327     }
328 
329     @Test
330     void testNotInEventuallyEmpty() {
331         var selectModel = select(id, animalName)
332                 .from(animalData)
333                 .where(id, isNotIn(1, 2).filter(s -> false))
334                 .orderBy(id)
335                 .build();
336 
337         assertThatExceptionOfType(InvalidSqlException.class)
338                 .isThrownBy(() -> selectModel.render(RenderingStrategies.MYBATIS3))
339                 .withMessage(Messages.getString("ERROR.44", "IsNotIn"));
340     }
341 
342     @Test
343     void testNotInCaseInsensitiveEventuallyEmpty() {
344         var selectModel = select(id, animalName)
345                 .from(animalData)
346                 .where(animalName, isNotInCaseInsensitive("Fred", "Betty").filter(s -> false))
347                 .orderBy(id)
348                 .build();
349 
350         assertThatExceptionOfType(InvalidSqlException.class)
351                 .isThrownBy(() -> selectModel.render(RenderingStrategies.MYBATIS3))
352                 .withMessage(Messages.getString("ERROR.44", "IsNotInCaseInsensitive"));
353     }
354 
355     @Test
356     void testInEventuallyEmptyDoubleFilter() {
357         var selectModel = select(id, animalName)
358                 .from(animalData)
359                 .where(id, isIn(1, 2).filter(s -> false).filter(s -> false))
360                 .orderBy(id)
361                 .build();
362 
363         assertThatExceptionOfType(InvalidSqlException.class)
364                 .isThrownBy(() -> selectModel.render(RenderingStrategies.MYBATIS3))
365                 .withMessage(Messages.getString("ERROR.44", "IsIn"));
366     }
367 }