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.simple;
17  
18  import static examples.simple.AddressDynamicSqlSupport.address;
19  import static examples.simple.PersonDynamicSqlSupport.addressId;
20  import static examples.simple.PersonDynamicSqlSupport.birthDate;
21  import static examples.simple.PersonDynamicSqlSupport.employed;
22  import static examples.simple.PersonDynamicSqlSupport.firstName;
23  import static examples.simple.PersonDynamicSqlSupport.id;
24  import static examples.simple.PersonDynamicSqlSupport.lastName;
25  import static examples.simple.PersonDynamicSqlSupport.occupation;
26  import static examples.simple.PersonDynamicSqlSupport.person;
27  import static org.assertj.core.api.Assertions.assertThat;
28  import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
29  import static org.assertj.core.api.Assertions.entry;
30  import static org.mybatis.dynamic.sql.SqlBuilder.*;
31  
32  import java.io.InputStream;
33  import java.io.InputStreamReader;
34  import java.sql.Connection;
35  import java.sql.DriverManager;
36  import java.util.Collection;
37  import java.util.Date;
38  import java.util.List;
39  import java.util.Optional;
40  
41  import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
42  import org.apache.ibatis.jdbc.ScriptRunner;
43  import org.apache.ibatis.mapping.Environment;
44  import org.apache.ibatis.session.Configuration;
45  import org.apache.ibatis.session.SqlSession;
46  import org.apache.ibatis.session.SqlSessionFactory;
47  import org.apache.ibatis.session.SqlSessionFactoryBuilder;
48  import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
49  import org.junit.jupiter.api.BeforeEach;
50  import org.junit.jupiter.api.Test;
51  import org.mybatis.dynamic.sql.SortSpecification;
52  import org.mybatis.dynamic.sql.delete.DeleteDSLCompleter;
53  import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider;
54  import org.mybatis.dynamic.sql.exception.NonRenderingWhereClauseException;
55  import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider;
56  import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider;
57  import org.mybatis.dynamic.sql.render.RenderingStrategies;
58  import org.mybatis.dynamic.sql.select.CountDSLCompleter;
59  import org.mybatis.dynamic.sql.select.SelectDSLCompleter;
60  import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
61  import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider;
62  
63  class PersonMapperTest {
64  
65      private static final String JDBC_URL = "jdbc:hsqldb:mem:aname";
66      private static final String JDBC_DRIVER = "org.hsqldb.jdbcDriver";
67  
68      private SqlSessionFactory sqlSessionFactory;
69  
70      @BeforeEach
71      void setup() throws Exception {
72          Class.forName(JDBC_DRIVER);
73          try (InputStream is = getClass().getResourceAsStream("/examples/simple/CreateSimpleDB.sql")) {
74              assert is != null;
75              try (Connection connection = DriverManager.getConnection(JDBC_URL, "sa", "");
76                   InputStreamReader isr = new InputStreamReader(is)) {
77                  ScriptRunner sr = new ScriptRunner(connection);
78                  sr.setLogWriter(null);
79                  sr.runScript(isr);
80              }
81          }
82  
83          UnpooledDataSource ds = new UnpooledDataSource(JDBC_DRIVER, JDBC_URL, "sa", "");
84          Environment environment = new Environment("test", new JdbcTransactionFactory(), ds);
85          Configuration config = new Configuration(environment);
86          config.addMapper(PersonMapper.class);
87          config.addMapper(PersonWithAddressMapper.class);
88          config.addMapper(AddressMapper.class);
89          sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
90      }
91  
92      @Test
93      void testSelect() {
94          try (SqlSession session = sqlSessionFactory.openSession()) {
95              PersonMapper mapper = session.getMapper(PersonMapper.class);
96  
97              List<PersonRecord> rows = mapper.select(c ->
98                      c.where(id, isEqualTo(1))
99                      .or(occupation, isNull()));
100 
101             assertThat(rows).hasSize(3);
102         }
103     }
104 
105     @Test
106     void testSelectEmployed() {
107         try (SqlSession session = sqlSessionFactory.openSession()) {
108             PersonMapper mapper = session.getMapper(PersonMapper.class);
109 
110             List<PersonRecord> rows = mapper.select(c ->
111                     c.where(employed, isTrue())
112                     .orderBy(id));
113 
114             assertThat(rows).hasSize(4);
115             assertThat(rows.get(0).id()).isEqualTo(1);
116         }
117     }
118 
119     @Test
120     void testSelectUnemployed() {
121         try (SqlSession session = sqlSessionFactory.openSession()) {
122             PersonMapper mapper = session.getMapper(PersonMapper.class);
123 
124             List<PersonRecord> rows = mapper.select(c ->
125                     c.where(employed, isFalse())
126                             .orderBy(id));
127 
128             assertThat(rows).hasSize(2);
129             assertThat(rows.get(0).id()).isEqualTo(3);
130         }
131     }
132 
133     @Test
134     void testSelectWithTypeConversion() throws NumberFormatException {
135         try (SqlSession session = sqlSessionFactory.openSession()) {
136             PersonMapper mapper = session.getMapper(PersonMapper.class);
137 
138             List<PersonRecord> rows = mapper.select(c ->
139                     c.where(id, isEqualTo("1").map(Integer::parseInt))
140                             .or(occupation, isNull()));
141 
142             assertThat(rows).hasSize(3);
143         }
144     }
145 
146     @Test
147     void testSelectWithTypeConversionAndFilterAndNull() throws NumberFormatException {
148         try (SqlSession session = sqlSessionFactory.openSession()) {
149             PersonMapper mapper = session.getMapper(PersonMapper.class);
150 
151             List<PersonRecord> rows = mapper.select(c ->
152                     c.where(id, isEqualToWhenPresent((String) null).map(Integer::parseInt))
153                             .or(occupation, isNull()));
154 
155             assertThat(rows).hasSize(2);
156         }
157     }
158 
159     // this example is in the quick start documentation...
160     @Test
161     void testGeneralSelect() {
162         try (SqlSession session = sqlSessionFactory.openSession()) {
163             PersonMapper mapper = session.getMapper(PersonMapper.class);
164 
165             SelectStatementProvider selectStatement = select(id.as("A_ID"), firstName, lastName, birthDate, employed,
166                         occupation, addressId)
167                     .from(person)
168                     .where(id, isEqualTo(1))
169                     .or(occupation, isNull())
170                     .build()
171                     .render(RenderingStrategies.MYBATIS3);
172 
173             List<PersonRecord> rows = mapper.selectMany(selectStatement);
174             assertThat(rows).hasSize(3);
175         }
176     }
177 
178     @Test
179     void testSelectAll() {
180         try (SqlSession session = sqlSessionFactory.openSession()) {
181             PersonMapper mapper = session.getMapper(PersonMapper.class);
182 
183             List<PersonRecord> rows = mapper.select(SelectDSLCompleter.allRows());
184 
185             assertThat(rows).hasSize(6);
186             assertThat(rows.get(0).id()).isEqualTo(1);
187             assertThat(rows.get(5).id()).isEqualTo(6);
188         }
189     }
190 
191     @Test
192     void testSelectAllOrdered() {
193         try (SqlSession session = sqlSessionFactory.openSession()) {
194             PersonMapper mapper = session.getMapper(PersonMapper.class);
195 
196             List<PersonRecord> rows = mapper
197                     .select(SelectDSLCompleter.allRowsOrderedBy(lastName.descending(), firstName.descending()));
198 
199             assertThat(rows).hasSize(6);
200             assertThat(rows.get(0).id()).isEqualTo(5);
201             assertThat(rows.get(5).id()).isEqualTo(1);
202         }
203     }
204 
205     @Test
206     void testSelectDistinct() {
207         try (SqlSession session = sqlSessionFactory.openSession()) {
208             PersonMapper mapper = session.getMapper(PersonMapper.class);
209 
210             List<PersonRecord> rows = mapper.selectDistinct(c ->
211                     c.where(id, isGreaterThan(1))
212                     .or(occupation, isNull()));
213 
214             assertThat(rows).hasSize(5);
215         }
216     }
217 
218     @Test
219     void testSelectWithTypeHandler() {
220         try (SqlSession session = sqlSessionFactory.openSession()) {
221             PersonMapper mapper = session.getMapper(PersonMapper.class);
222 
223             List<PersonRecord> rows = mapper.select(c ->
224                     c.where(employed, isEqualTo(false))
225                     .orderBy(id));
226 
227             assertThat(rows).hasSize(2);
228             assertThat(rows.get(0).id()).isEqualTo(3);
229             assertThat(rows.get(1).id()).isEqualTo(6);
230         }
231     }
232 
233     @Test
234     void testSelectByPrimaryKeyWithMissingRecord() {
235         try (SqlSession session = sqlSessionFactory.openSession()) {
236             PersonMapper mapper = session.getMapper(PersonMapper.class);
237 
238             Optional<PersonRecord> row = mapper.selectByPrimaryKey(300);
239             assertThat(row).isNotPresent();
240         }
241     }
242 
243     @Test
244     void testFirstNameIn() {
245         try (SqlSession session = sqlSessionFactory.openSession()) {
246             PersonMapper mapper = session.getMapper(PersonMapper.class);
247 
248             List<PersonRecord> rows = mapper.select(c ->
249                     c.where(firstName, isIn("Fred", "Barney")));
250 
251             assertThat(rows).hasSize(2);
252             assertThat(rows.get(0))
253                     .isNotNull()
254                     .extracting("lastName").isNotNull()
255                     .extracting("name").isEqualTo("Flintstone");
256             assertThat(rows.get(1))
257                     .isNotNull()
258                     .extracting("lastName").isNotNull()
259                     .extracting("name").isEqualTo("Rubble");
260         }
261     }
262 
263     @Test
264     void testOrderByCollection() {
265         Collection<SortSpecification> orderByColumns = List.of(firstName);
266 
267         try (SqlSession session = sqlSessionFactory.openSession()) {
268             PersonMapper mapper = session.getMapper(PersonMapper.class);
269 
270             List<PersonRecord> rows = mapper.select(c -> c
271                     .where(firstName, isIn("Fred", "Barney"))
272                     .orderBy(orderByColumns)
273             );
274 
275             assertThat(rows).hasSize(2);
276             assertThat(rows.get(0))
277                     .isNotNull()
278                     .extracting("lastName").isNotNull()
279                     .extracting("name").isEqualTo("Rubble");
280             assertThat(rows.get(1))
281                     .isNotNull()
282                     .extracting("lastName").isNotNull()
283                     .extracting("name").isEqualTo("Flintstone");
284         }
285     }
286 
287     @Test
288     void testDelete() {
289         try (SqlSession session = sqlSessionFactory.openSession()) {
290             PersonMapper mapper = session.getMapper(PersonMapper.class);
291             int rows = mapper.delete(c ->
292                     c.where(occupation, isNull()));
293             assertThat(rows).isEqualTo(2);
294         }
295     }
296 
297     // this test is in the quick start documentation
298     @Test
299     void testGeneralDelete() {
300         try (SqlSession session = sqlSessionFactory.openSession()) {
301             PersonMapper mapper = session.getMapper(PersonMapper.class);
302 
303             DeleteStatementProvider deleteStatement = deleteFrom(person)
304                     .where(occupation, isNull())
305                     .build()
306                     .render(RenderingStrategies.MYBATIS3);
307 
308             int rows = mapper.delete(deleteStatement);
309             assertThat(rows).isEqualTo(2);
310         }
311     }
312 
313     @Test
314     void testDeleteAll() {
315         try (SqlSession session = sqlSessionFactory.openSession()) {
316             PersonMapper mapper = session.getMapper(PersonMapper.class);
317             int rows = mapper.delete(DeleteDSLCompleter.allRows());
318 
319             assertThat(rows).isEqualTo(6);
320         }
321     }
322 
323     @Test
324     void testDeleteByPrimaryKey() {
325         try (SqlSession session = sqlSessionFactory.openSession()) {
326             PersonMapper mapper = session.getMapper(PersonMapper.class);
327             int rows = mapper.deleteByPrimaryKey(2);
328 
329             assertThat(rows).isEqualTo(1);
330         }
331     }
332 
333     @Test
334     void testInsert() {
335         try (SqlSession session = sqlSessionFactory.openSession()) {
336             PersonMapper mapper = session.getMapper(PersonMapper.class);
337             PersonRecord row = new PersonRecord(100, "Joe", new LastName("Jones"), new Date(), true, "Developer", 1);
338 
339             int rows = mapper.insert(row);
340             assertThat(rows).isEqualTo(1);
341         }
342     }
343 
344     @Test
345     void testRawInsert() {
346         try (SqlSession session = sqlSessionFactory.openSession()) {
347             PersonMapper mapper = session.getMapper(PersonMapper.class);
348             PersonRecord row = new PersonRecord(100, "Joe", new LastName("Jones"), new Date(), true, "Developer", 1);
349 
350             InsertStatementProvider<PersonRecord> insertStatement = insert(row).into(person)
351                     .withMappedColumn(id)
352                     .withMappedColumn(firstName)
353                     .withMappedColumn(lastName)
354                     .withMappedColumn(birthDate)
355                     .withMappedColumn(employed)
356                     .withMappedColumn(occupation)
357                     .withMappedColumn(addressId)
358                     .build().render(RenderingStrategies.MYBATIS3);
359 
360             int rows = mapper.insert(insertStatement);
361             assertThat(rows).isEqualTo(1);
362         }
363     }
364 
365     @Test
366     void testGeneralInsert() {
367         try (SqlSession session = sqlSessionFactory.openSession()) {
368             PersonMapper mapper = session.getMapper(PersonMapper.class);
369             int rows = mapper.generalInsert(c ->
370                 c.set(id).toValue(100)
371                 .set(firstName).toValue("Joe")
372                 .set(lastName).toValue(new LastName("Jones"))
373                 .set(birthDate).toValue(new Date())
374                 .set(employed).toValue(true)
375                 .set(occupation).toValue("Developer")
376                 .set(addressId).toValue(1)
377             );
378 
379             assertThat(rows).isEqualTo(1);
380         }
381     }
382 
383     @Test
384     void testInsertMultiple() {
385         try (SqlSession session = sqlSessionFactory.openSession()) {
386             PersonMapper mapper = session.getMapper(PersonMapper.class);
387 
388             List<PersonRecord> records = List.of(
389                     new PersonRecord(100, "Joe", new LastName("Jones"), new Date(), true, "Developer", 1),
390                     new PersonRecord(101, "Sarah", new LastName("Smith"), new Date(), true, "Architect", 2)
391             );
392 
393             int rows = mapper.insertMultiple(records);
394             assertThat(rows).isEqualTo(2);
395         }
396     }
397 
398     @Test
399     void testInsertSelective() {
400         try (SqlSession session = sqlSessionFactory.openSession()) {
401             PersonMapper mapper = session.getMapper(PersonMapper.class);
402             PersonRecord row = new PersonRecord(100, "Joe", new LastName("Jones"), new Date(), false, null, 1);
403 
404             int rows = mapper.insertSelective(row);
405             assertThat(rows).isEqualTo(1);
406         }
407     }
408 
409     @Test
410     void testUpdateByPrimaryKeyNullKeyShouldThrowException() {
411         try (SqlSession session = sqlSessionFactory.openSession()) {
412             PersonMapper mapper = session.getMapper(PersonMapper.class);
413             PersonRecord row = new PersonRecord(null, "Joe", new LastName("Jones"), new Date(), true, "Developer", 1);
414 
415             assertThatExceptionOfType(NonRenderingWhereClauseException.class).isThrownBy(() -> mapper.updateByPrimaryKey(row));
416         }
417     }
418 
419     @Test
420     void testUpdateByPrimaryKey() {
421         try (SqlSession session = sqlSessionFactory.openSession()) {
422             PersonMapper mapper = session.getMapper(PersonMapper.class);
423             PersonRecord row = new PersonRecord(100, "Joe", new LastName("Jones"), new Date(), true, "Developer", 1);
424 
425             int rows = mapper.insert(row);
426             assertThat(rows).isEqualTo(1);
427 
428             row = row.withOccupation("Programmer");
429             rows = mapper.updateByPrimaryKey(row);
430             assertThat(rows).isEqualTo(1);
431 
432             Optional<PersonRecord> newRecord = mapper.selectByPrimaryKey(100);
433             assertThat(newRecord).hasValueSatisfying(r ->
434                     assertThat(r.occupation()).isEqualTo("Programmer"));
435         }
436     }
437 
438     @Test
439     void testUpdateByPrimaryKeySelective() {
440         try (SqlSession session = sqlSessionFactory.openSession()) {
441             PersonMapper mapper = session.getMapper(PersonMapper.class);
442             PersonRecord row = new PersonRecord(100, "Joe", new LastName("Jones"), new Date(), true, "Developer", 1);
443 
444             int rows = mapper.insert(row);
445             assertThat(rows).isEqualTo(1);
446 
447             PersonRecord updateRecord = new PersonRecord(100, null, null, null, null, "Programmer", null);
448             rows = mapper.updateByPrimaryKeySelective(updateRecord);
449             assertThat(rows).isEqualTo(1);
450 
451             Optional<PersonRecord> newRecord = mapper.selectByPrimaryKey(100);
452             assertThat(newRecord).hasValueSatisfying(r -> {
453                 assertThat(r.occupation()).isEqualTo("Programmer");
454                 assertThat(r.firstName()).isEqualTo("Joe");
455             });
456         }
457     }
458 
459     @Test
460     void testUpdate() {
461         try (SqlSession session = sqlSessionFactory.openSession()) {
462             PersonMapper mapper = session.getMapper(PersonMapper.class);
463             PersonRecord row = new PersonRecord(100, "Joe", new LastName("Jones"), new Date(), true, "Developer", 1);
464 
465             int rows = mapper.insert(row);
466             assertThat(rows).isEqualTo(1);
467 
468             PersonRecord updateRow = row.withOccupation("Programmer");
469 
470             rows = mapper.update(c ->
471                 PersonMapper.updateAllColumns(updateRow, c)
472                 .where(id, isEqualTo(100))
473                 .and(firstName, isEqualTo("Joe")));
474 
475             assertThat(rows).isEqualTo(1);
476 
477             Optional<PersonRecord> newRecord = mapper.selectByPrimaryKey(100);
478             assertThat(newRecord).hasValueSatisfying(r ->
479                     assertThat(r.occupation()).isEqualTo("Programmer"));
480         }
481     }
482 
483     @Test
484     void testUpdateOneField() {
485         try (SqlSession session = sqlSessionFactory.openSession()) {
486             PersonMapper mapper = session.getMapper(PersonMapper.class);
487             PersonRecord row = new PersonRecord(100, "Joe", new LastName("Jones"), new Date(), true, "Developer", 1);
488 
489             int rows = mapper.insert(row);
490             assertThat(rows).isEqualTo(1);
491 
492             rows = mapper.update(c ->
493                 c.set(occupation).equalTo("Programmer")
494                 .where(id, isEqualTo(100)));
495 
496             assertThat(rows).isEqualTo(1);
497 
498             Optional<PersonRecord> newRecord = mapper.selectByPrimaryKey(100);
499             assertThat(newRecord).hasValueSatisfying(r ->
500                     assertThat(r.occupation()).isEqualTo("Programmer"));
501         }
502     }
503 
504     @Test
505     void testUpdateAll() {
506         try (SqlSession session = sqlSessionFactory.openSession()) {
507             PersonMapper mapper = session.getMapper(PersonMapper.class);
508             PersonRecord row = new PersonRecord(100, "Joe", new LastName("Jones"), new Date(), true, "Developer", 1);
509 
510             int rows = mapper.insert(row);
511             assertThat(rows).isEqualTo(1);
512 
513             PersonRecord updateRecord = new PersonRecord(null, null, null, null, null, "Programmer", null);
514             rows = mapper.update(c ->
515                 PersonMapper.updateSelectiveColumns(updateRecord, c));
516 
517             assertThat(rows).isEqualTo(7);
518 
519             Optional<PersonRecord> newRecord = mapper.selectByPrimaryKey(100);
520             assertThat(newRecord).hasValueSatisfying(r ->
521                     assertThat(r.occupation()).isEqualTo("Programmer"));
522         }
523     }
524 
525     @Test
526     void testUpdateSelective() {
527         try (SqlSession session = sqlSessionFactory.openSession()) {
528             PersonMapper mapper = session.getMapper(PersonMapper.class);
529             PersonRecord row = new PersonRecord(100, "Joe", new LastName("Jones"), new Date(), true, "Developer", 1);
530 
531             int rows = mapper.insert(row);
532             assertThat(rows).isEqualTo(1);
533 
534             PersonRecord updateRecord = new PersonRecord(null, null, null, null, null, "Programmer", null);
535             rows = mapper.update(c ->
536                 PersonMapper.updateSelectiveColumns(updateRecord, c)
537                 .where(id, isEqualTo(100)));
538 
539             assertThat(rows).isEqualTo(1);
540 
541             Optional<PersonRecord> newRecord = mapper.selectByPrimaryKey(100);
542             assertThat(newRecord).hasValueSatisfying(r ->
543                     assertThat(r.occupation()).isEqualTo("Programmer"));
544         }
545     }
546 
547     @Test
548     void testCount() {
549         try (SqlSession session = sqlSessionFactory.openSession()) {
550             PersonMapper mapper = session.getMapper(PersonMapper.class);
551             long rows = mapper.count(c ->
552                     c.where(occupation, isNull()));
553 
554             assertThat(rows).isEqualTo(2L);
555         }
556     }
557 
558     @Test
559     void testCountAll() {
560         try (SqlSession session = sqlSessionFactory.openSession()) {
561             PersonMapper mapper = session.getMapper(PersonMapper.class);
562             long rows = mapper.count(CountDSLCompleter.allRows());
563 
564             assertThat(rows).isEqualTo(6L);
565         }
566     }
567 
568     @Test
569     void testCountLastName() {
570         try (SqlSession session = sqlSessionFactory.openSession()) {
571             PersonMapper mapper = session.getMapper(PersonMapper.class);
572             long rows = mapper.count(lastName, CountDSLCompleter.allRows());
573 
574             assertThat(rows).isEqualTo(6L);
575         }
576     }
577 
578     @Test
579     void testCountDistinctLastName() {
580         try (SqlSession session = sqlSessionFactory.openSession()) {
581             PersonMapper mapper = session.getMapper(PersonMapper.class);
582             long rows = mapper.countDistinct(lastName, CountDSLCompleter.allRows());
583 
584             assertThat(rows).isEqualTo(2L);
585         }
586     }
587 
588     @Test
589     void testTypeHandledLike() {
590         try (SqlSession session = sqlSessionFactory.openSession()) {
591             PersonMapper mapper = session.getMapper(PersonMapper.class);
592 
593             List<PersonRecord> rows = mapper.select(c ->
594                     c.where(lastName, isLike(new LastName("Fl%")))
595                     .orderBy(id));
596 
597             assertThat(rows).hasSize(3);
598             assertThat(rows.get(0).firstName()).isEqualTo("Fred");
599         }
600     }
601 
602     @Test
603     void testTypeHandledNotLike() {
604         try (SqlSession session = sqlSessionFactory.openSession()) {
605             PersonMapper mapper = session.getMapper(PersonMapper.class);
606 
607             List<PersonRecord> rows = mapper.select(c ->
608                     c.where(lastName, isNotLike(new LastName("Fl%")))
609                     .orderBy(id));
610 
611             assertThat(rows).hasSize(3);
612             assertThat(rows.get(0).firstName()).isEqualTo("Barney");
613         }
614     }
615 
616     @Test
617     void testJoinAllRows() {
618         try (SqlSession session = sqlSessionFactory.openSession()) {
619             PersonWithAddressMapper mapper = session.getMapper(PersonWithAddressMapper.class);
620             List<PersonWithAddress> records = mapper.select(
621                     SelectDSLCompleter.allRowsOrderedBy(id)
622             );
623 
624             assertThat(records).hasSize(6);
625             assertThat(records.get(0).getId()).isEqualTo(1);
626             assertThat(records.get(0).getEmployed()).isTrue();
627             assertThat(records.get(0).getFirstName()).isEqualTo("Fred");
628             assertThat(records.get(0).getLastName()).isEqualTo(new LastName("Flintstone"));
629             assertThat(records.get(0).getOccupation()).isEqualTo("Brontosaurus Operator");
630             assertThat(records.get(0).getBirthDate()).isNotNull();
631             assertThat(records.get(0).getAddress()).isNotNull()
632                     .extracting("id", "streetAddress", "city", "state", "addressType")
633                     .containsExactly(1, "123 Main Street", "Bedrock", "IN", AddressRecord.AddressType.HOME);
634 
635             assertThat(records.get(4).getAddress()).isNotNull()
636                     .extracting("addressType")
637                     .isEqualTo(AddressRecord.AddressType.BUSINESS);
638         }
639     }
640 
641     @Test
642     void testJoinOneRow() {
643         try (SqlSession session = sqlSessionFactory.openSession()) {
644             PersonWithAddressMapper mapper = session.getMapper(PersonWithAddressMapper.class);
645             List<PersonWithAddress> records = mapper.select(c -> c.where(id, isEqualTo(1)));
646 
647             assertThat(records).hasSize(1);
648             assertThat(records.get(0).getId()).isEqualTo(1);
649             assertThat(records.get(0).getEmployed()).isTrue();
650             assertThat(records.get(0).getFirstName()).isEqualTo("Fred");
651             assertThat(records.get(0).getLastName()).isEqualTo(new LastName("Flintstone"));
652             assertThat(records.get(0).getOccupation()).isEqualTo("Brontosaurus Operator");
653             assertThat(records.get(0).getBirthDate()).isNotNull();
654             assertThat(records.get(0).getAddress()).isNotNull()
655                     .extracting("id", "streetAddress", "city", "state", "addressType")
656                     .containsExactly(1, "123 Main Street", "Bedrock", "IN", AddressRecord.AddressType.HOME);
657         }
658     }
659 
660     @Test
661     void testJoinPrimaryKey() {
662         try (SqlSession session = sqlSessionFactory.openSession()) {
663             PersonWithAddressMapper mapper = session.getMapper(PersonWithAddressMapper.class);
664             Optional<PersonWithAddress> row = mapper.selectByPrimaryKey(1);
665 
666             assertThat(row).hasValueSatisfying(r -> {
667                 assertThat(r.getId()).isEqualTo(1);
668                 assertThat(r.getEmployed()).isTrue();
669                 assertThat(r.getFirstName()).isEqualTo("Fred");
670                 assertThat(r.getLastName()).isEqualTo(new LastName("Flintstone"));
671                 assertThat(r.getOccupation()).isEqualTo("Brontosaurus Operator");
672                 assertThat(r.getBirthDate()).isNotNull();
673                 assertThat(r.getAddress()).isNotNull()
674                         .extracting("id", "streetAddress", "city", "state", "addressType")
675                         .containsExactly(1, "123 Main Street", "Bedrock", "IN", AddressRecord.AddressType.HOME);
676             });
677         }
678     }
679 
680     @Test
681     void testJoinPrimaryKeyInvalidRecord() {
682         try (SqlSession session = sqlSessionFactory.openSession()) {
683             PersonWithAddressMapper mapper = session.getMapper(PersonWithAddressMapper.class);
684             Optional<PersonWithAddress> row = mapper.selectByPrimaryKey(55);
685 
686             assertThat(row).isEmpty();
687         }
688     }
689 
690     @Test
691     void testJoinCount() {
692         try (SqlSession session = sqlSessionFactory.openSession()) {
693             PersonWithAddressMapper mapper = session.getMapper(PersonWithAddressMapper.class);
694             long count = mapper.count(c -> c.where(person.id, isEqualTo(55)));
695 
696             assertThat(count).isZero();
697         }
698     }
699 
700     @Test
701     void testJoinCountWithSubcriteria() {
702         try (SqlSession session = sqlSessionFactory.openSession()) {
703             PersonWithAddressMapper mapper = session.getMapper(PersonWithAddressMapper.class);
704             long count = mapper.count(c -> c.where(person.id, isEqualTo(55), or(person.id, isEqualTo(1))));
705 
706             assertThat(count).isEqualTo(1);
707         }
708     }
709 
710     @Test
711     void testJoinCountWithSubcriteriaInSingleStatement() {
712         try (SqlSession session = sqlSessionFactory.openSession()) {
713             PersonWithAddressMapper mapper = session.getMapper(PersonWithAddressMapper.class);
714 
715             SelectStatementProvider selectStatement = countFrom(person)
716                     .join(address).on(person.id, isEqualTo(address.id))
717                     .where(person.id, isEqualTo(55), or(person.id, isEqualTo(1)))
718                     .build()
719                     .render(RenderingStrategies.MYBATIS3);
720 
721             String expected = """
722                 select count(*)
723                 from Person
724                 join Address on Person.id = Address.address_id
725                 where Person.id = #{parameters.p1,jdbcType=INTEGER} or Person.id = #{parameters.p2,jdbcType=INTEGER}
726                 """;
727             assertThat(selectStatement.getSelectStatement()).isEqualToNormalizingWhitespace(expected);
728 
729             long count = mapper.count(selectStatement);
730             assertThat(count).isEqualTo(1);
731         }
732     }
733 
734     @Test
735     void testJoinCountInSingleStatement() {
736         try (SqlSession session = sqlSessionFactory.openSession()) {
737             PersonWithAddressMapper mapper = session.getMapper(PersonWithAddressMapper.class);
738 
739             SelectStatementProvider selectStatement = countFrom(person, "p")
740                     .join(address, "a").on(person.id, isEqualTo(address.id))
741                     .configureStatement(c -> c.setNonRenderingWhereClauseAllowed(true))
742                     .build()
743                     .render(RenderingStrategies.MYBATIS3);
744 
745             String expected = """
746                 select count(*)
747                 from Person p
748                 join Address a on p.id = a.address_id
749                 """;
750             assertThat(selectStatement.getSelectStatement()).isEqualToNormalizingWhitespace(expected);
751 
752             long count = mapper.count(selectStatement);
753             assertThat(count).isEqualTo(2);
754         }
755     }
756 
757     @Test
758     void testSelectJoinForUpdate() {
759         SelectStatementProvider selectStatement = select(person.id, address.city)
760                 .from(person, "p")
761                 .join(address, "a").on(person.id, isEqualTo(address.id))
762                 .forUpdate()
763                 .build()
764                 .render(RenderingStrategies.MYBATIS3);
765 
766         String expected = """
767                 select p.id, a.city
768                 from Person p
769                 join Address a on p.id = a.address_id
770                 for update
771                 """;
772         assertThat(selectStatement.getSelectStatement()).isEqualToNormalizingWhitespace(expected);
773     }
774 
775     @Test
776     void testWithEnumOrdinalTypeHandler() {
777         try (SqlSession session = sqlSessionFactory.openSession()) {
778             AddressMapper mapper = session.getMapper(AddressMapper.class);
779 
780             GeneralInsertStatementProvider insertStatement = insertInto(address)
781                     .set(address.id).toValue(4)
782                     .set(address.streetAddress).toValue("987 Elm Street")
783                     .set(address.city).toValue("Mayberry")
784                     .set(address.state).toValue("NC")
785                     .set(address.addressType).toValue(AddressRecord.AddressType.HOME)
786                     .build()
787                     .render(RenderingStrategies.MYBATIS3);
788 
789             int rows = mapper.generalInsert(insertStatement);
790             assertThat(rows).isEqualTo(1);
791 
792             SelectStatementProvider selectStatement = select(address.addressType)
793                     .from(address)
794                     .where(address.id, isEqualTo(4))
795                     .build()
796                     .render(RenderingStrategies.MYBATIS3);
797 
798             Optional<Integer> type = mapper.selectOptionalInteger(selectStatement);
799             assertThat(type).hasValueSatisfying(i -> assertThat(i).isZero());
800         }
801     }
802 
803     @Test
804     void testMultiSelectWithUnion() {
805         try (SqlSession session = sqlSessionFactory.openSession()) {
806             PersonMapper mapper = session.getMapper(PersonMapper.class);
807 
808             SelectStatementProvider selectStatement = multiSelect(
809                     select(id.as("A_ID"), firstName, lastName, birthDate, employed, occupation, addressId)
810                             .from(person)
811                             .where(id, isLessThanOrEqualTo(2))
812                             .orderBy(id)
813                             .limit(1)
814             ).union(
815                     select(id.as("A_ID"), firstName, lastName, birthDate, employed, occupation, addressId)
816                             .from(person)
817                             .where(id, isGreaterThanOrEqualTo(4))
818                             .orderBy(id.descending())
819                             .limit(1)
820             )
821             .orderBy(sortColumn("A_ID"))
822             .limit(3)
823             .build()
824             .render(RenderingStrategies.MYBATIS3);
825 
826             String expected =
827                     "(select id as A_ID, first_name, last_name, birth_date, employed, occupation, address_id " +
828                     "from Person " +
829                     "where id <= #{parameters.p1,jdbcType=INTEGER} " +
830                     "order by id limit #{parameters.p2}) " +
831                     "union " +
832                     "(select id as A_ID, first_name, last_name, birth_date, employed, occupation, address_id " +
833                     "from Person " +
834                     "where id >= #{parameters.p3,jdbcType=INTEGER} " +
835                     "order by id DESC limit #{parameters.p4}) " +
836                     "order by A_ID " +
837                     "limit #{parameters.p5}";
838 
839             assertThat(selectStatement.getSelectStatement()).isEqualTo(expected);
840 
841             List<PersonRecord> records = mapper.selectMany(selectStatement);
842 
843             assertThat(records).hasSize(2);
844             assertThat(records.get(0).id()).isEqualTo(1);
845             assertThat(records.get(1).id()).isEqualTo(6);
846         }
847     }
848 
849     @Test
850     void testMultiSelectWithUnionAll() {
851         try (SqlSession session = sqlSessionFactory.openSession()) {
852             PersonMapper mapper = session.getMapper(PersonMapper.class);
853 
854             SelectStatementProvider selectStatement = multiSelect(
855                     select(id.as("A_ID"), firstName, lastName, birthDate, employed, occupation, addressId)
856                             .from(person)
857                             .where(id, isLessThanOrEqualTo(2))
858                             .orderBy(id)
859                             .limit(1)
860             ).unionAll(
861                     select(id.as("A_ID"), firstName, lastName, birthDate, employed, occupation, addressId)
862                             .from(person)
863                             .where(id, isGreaterThanOrEqualTo(4))
864                             .orderBy(id.descending())
865                             .limit(1)
866                     ).orderBy(sortColumn("A_ID"))
867                     .fetchFirst(2).rowsOnly()
868                     .build()
869                     .render(RenderingStrategies.MYBATIS3);
870 
871             String expected =
872                     "(select id as A_ID, first_name, last_name, birth_date, employed, occupation, address_id " +
873                             "from Person " +
874                             "where id <= #{parameters.p1,jdbcType=INTEGER} " +
875                             "order by id limit #{parameters.p2}) " +
876                             "union all " +
877                             "(select id as A_ID, first_name, last_name, birth_date, employed, occupation, address_id " +
878                             "from Person " +
879                             "where id >= #{parameters.p3,jdbcType=INTEGER} " +
880                             "order by id DESC limit #{parameters.p4}) " +
881                             "order by A_ID " +
882                             "fetch first #{parameters.p5} rows only";
883 
884             assertThat(selectStatement.getSelectStatement()).isEqualTo(expected);
885 
886             List<PersonRecord> records = mapper.selectMany(selectStatement);
887 
888             assertThat(records).hasSize(2);
889             assertThat(records.get(0).id()).isEqualTo(1);
890             assertThat(records.get(1).id()).isEqualTo(6);
891         }
892     }
893 
894     @Test
895     void testMultiSelectPagingVariation1() {
896         SelectStatementProvider selectStatement = multiSelect(
897                 select(id, firstName, lastName, birthDate, employed, occupation, addressId)
898                         .from(person)
899                         .where(id, isLessThanOrEqualTo(2))
900         ).unionAll(
901                 select(id, firstName, lastName, birthDate, employed, occupation, addressId)
902                         .from(person)
903                         .where(id, isGreaterThanOrEqualTo(4))
904                 )
905                 .orderBy(id)
906                 .limit(3).offset(2)
907                 .build()
908                 .render(RenderingStrategies.MYBATIS3);
909 
910         String expected =
911                 "(select id, first_name, last_name, birth_date, employed, occupation, address_id " +
912                         "from Person " +
913                         "where id <= #{parameters.p1,jdbcType=INTEGER}) " +
914                         "union all " +
915                         "(select id, first_name, last_name, birth_date, employed, occupation, address_id " +
916                         "from Person " +
917                         "where id >= #{parameters.p2,jdbcType=INTEGER}) " +
918                         "order by id " +
919                         "limit #{parameters.p3} offset #{parameters.p4}";
920 
921         assertThat(selectStatement.getSelectStatement()).isEqualTo(expected);
922     }
923 
924     @Test
925     void testMultiSelectPagingVariation2() {
926         SelectStatementProvider selectStatement = multiSelect(
927                 select(id, firstName, lastName, birthDate, employed, occupation, addressId)
928                         .from(person)
929                         .where(id, isLessThanOrEqualTo(2))
930         ).unionAll(
931                 select(id, firstName, lastName, birthDate, employed, occupation, addressId)
932                         .from(person)
933                         .where(id, isGreaterThanOrEqualTo(4))
934                 )
935                 .orderBy(id)
936                 .offset(2)
937                 .build()
938                 .render(RenderingStrategies.MYBATIS3);
939 
940         String expected =
941                 "(select id, first_name, last_name, birth_date, employed, occupation, address_id " +
942                         "from Person " +
943                         "where id <= #{parameters.p1,jdbcType=INTEGER}) " +
944                         "union all " +
945                         "(select id, first_name, last_name, birth_date, employed, occupation, address_id " +
946                         "from Person " +
947                         "where id >= #{parameters.p2,jdbcType=INTEGER}) " +
948                         "order by id " +
949                         "offset #{parameters.p3} rows";
950 
951         assertThat(selectStatement.getSelectStatement()).isEqualTo(expected);
952     }
953 
954     @Test
955     void testMultiSelectPagingVariation3() {
956         SelectStatementProvider selectStatement = multiSelect(
957                 select(id, firstName, lastName, birthDate, employed, occupation, addressId)
958                         .from(person)
959                         .where(id, isLessThanOrEqualTo(2))
960         ).unionAll(
961                 select(id, firstName, lastName, birthDate, employed, occupation, addressId)
962                         .from(person)
963                         .where(id, isGreaterThanOrEqualTo(4))
964                 )
965                 .orderBy(id)
966                 .offset(2).fetchFirst(3).rowsOnly()
967                 .build()
968                 .render(RenderingStrategies.MYBATIS3);
969 
970         String expected =
971                 "(select id, first_name, last_name, birth_date, employed, occupation, address_id " +
972                         "from Person " +
973                         "where id <= #{parameters.p1,jdbcType=INTEGER}) " +
974                         "union all " +
975                         "(select id, first_name, last_name, birth_date, employed, occupation, address_id " +
976                         "from Person " +
977                         "where id >= #{parameters.p2,jdbcType=INTEGER}) " +
978                         "order by id " +
979                         "offset #{parameters.p3} rows fetch first #{parameters.p4} rows only";
980 
981         assertThat(selectStatement.getSelectStatement()).isEqualTo(expected);
982     }
983 
984     @Test
985     void testMultiSelectPagingVariation() {
986         SelectStatementProvider selectStatement = multiSelect(
987                 select(id, firstName, lastName, birthDate, employed, occupation, addressId)
988                         .from(person)
989                         .where(id, isLessThanOrEqualTo(2))
990         ).unionAll(
991                 select(id, firstName, lastName, birthDate, employed, occupation, addressId)
992                         .from(person)
993                         .where(id, isGreaterThanOrEqualTo(4))
994                 )
995                 .orderBy(id)
996                 .build()
997                 .render(RenderingStrategies.MYBATIS3);
998 
999         String expected =
1000                 "(select id, first_name, last_name, birth_date, employed, occupation, address_id " +
1001                         "from Person " +
1002                         "where id <= #{parameters.p1,jdbcType=INTEGER}) " +
1003                         "union all " +
1004                         "(select id, first_name, last_name, birth_date, employed, occupation, address_id " +
1005                         "from Person " +
1006                         "where id >= #{parameters.p2,jdbcType=INTEGER}) " +
1007                         "order by id";
1008 
1009         assertThat(selectStatement.getSelectStatement()).isEqualTo(expected);
1010     }
1011 
1012     @Test
1013     void gh737() {
1014         UpdateStatementProvider updateStatement = update(person)
1015                 .set(addressId).equalTo(add(addressId, value(4)))
1016                 .where(id, isEqualTo(5))
1017                 .build()
1018                 .render(RenderingStrategies.MYBATIS3);
1019 
1020         String expected = "update Person " +
1021                 "set address_id = (address_id + #{parameters.p1}) " +
1022                 "where id = #{parameters.p2,jdbcType=INTEGER}";
1023 
1024         assertThat(updateStatement.getUpdateStatement()).isEqualTo(expected);
1025         assertThat(updateStatement.getParameters()).containsOnly(entry("p1", 4), entry("p2", 5));
1026     }
1027 }