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