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.joins;
17  
18  import static examples.joins.ItemMasterDynamicSQLSupport.itemMaster;
19  import static examples.joins.OrderDetailDynamicSQLSupport.orderDetail;
20  import static examples.joins.OrderLineDynamicSQLSupport.orderLine;
21  import static examples.joins.OrderMasterDynamicSQLSupport.orderDate;
22  import static examples.joins.OrderMasterDynamicSQLSupport.orderMaster;
23  import static examples.joins.UserDynamicSQLSupport.user;
24  import static org.assertj.core.api.Assertions.assertThat;
25  import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
26  import static org.assertj.core.api.Assertions.entry;
27  import static org.mybatis.dynamic.sql.SqlBuilder.*;
28  
29  import java.io.InputStream;
30  import java.io.InputStreamReader;
31  import java.sql.Connection;
32  import java.sql.DriverManager;
33  import java.util.List;
34  import java.util.Map;
35  
36  import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
37  import org.apache.ibatis.jdbc.ScriptRunner;
38  import org.apache.ibatis.mapping.Environment;
39  import org.apache.ibatis.session.Configuration;
40  import org.apache.ibatis.session.SqlSession;
41  import org.apache.ibatis.session.SqlSessionFactory;
42  import org.apache.ibatis.session.SqlSessionFactoryBuilder;
43  import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
44  import org.junit.jupiter.api.BeforeEach;
45  import org.junit.jupiter.api.Test;
46  import org.mybatis.dynamic.sql.exception.DuplicateTableAliasException;
47  import org.mybatis.dynamic.sql.render.RenderingStrategies;
48  import org.mybatis.dynamic.sql.select.QueryExpressionDSL;
49  import org.mybatis.dynamic.sql.select.SelectModel;
50  import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
51  import org.mybatis.dynamic.sql.util.Messages;
52  import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper;
53  
54  class JoinMapperTest {
55  
56      private static final String JDBC_URL = "jdbc:hsqldb:mem:aname";
57      private static final String JDBC_DRIVER = "org.hsqldb.jdbcDriver";
58  
59      private SqlSessionFactory sqlSessionFactory;
60  
61      @BeforeEach
62      void setup() throws Exception {
63          Class.forName(JDBC_DRIVER);
64          InputStream is = getClass().getResourceAsStream("/examples/joins/CreateJoinDB.sql");
65          assert is != null;
66          try (Connection connection = DriverManager.getConnection(JDBC_URL, "sa", "")) {
67              ScriptRunner sr = new ScriptRunner(connection);
68              sr.setLogWriter(null);
69              sr.runScript(new InputStreamReader(is));
70          }
71  
72          UnpooledDataSource ds = new UnpooledDataSource(JDBC_DRIVER, JDBC_URL, "sa", "");
73          Environment environment = new Environment("test", new JdbcTransactionFactory(), ds);
74          Configuration config = new Configuration(environment);
75          config.addMapper(JoinMapper.class);
76          config.addMapper(CommonSelectMapper.class);
77          sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
78      }
79  
80      @Test
81      void testSingleTableJoin1() {
82          try (SqlSession session = sqlSessionFactory.openSession()) {
83              JoinMapper mapper = session.getMapper(JoinMapper.class);
84  
85              SelectStatementProvider selectStatement = select(orderMaster.orderId, orderDate, orderDetail.lineNumber, orderDetail.description, orderDetail.quantity)
86                      .from(orderMaster, "om")
87                      .join(orderDetail, "od").on(orderMaster.orderId, isEqualTo(orderDetail.orderId))
88                      .build()
89                      .render(RenderingStrategies.MYBATIS3);
90  
91              String expectedStatement = "select om.order_id, om.order_date, od.line_number, od.description, od.quantity"
92                      + " from OrderMaster om join OrderDetail od on om.order_id = od.order_id";
93              assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
94  
95              List<OrderMaster> rows = mapper.selectMany(selectStatement);
96  
97              assertThat(rows).hasSize(2);
98              OrderMaster orderMaster = rows.get(0);
99              assertThat(orderMaster.getId()).isEqualTo(1);
100             assertThat(orderMaster.getDetails()).hasSize(2);
101             OrderDetail orderDetail = orderMaster.getDetails().get(0);
102             assertThat(orderDetail.lineNumber()).isEqualTo(1);
103             orderDetail = orderMaster.getDetails().get(1);
104             assertThat(orderDetail.lineNumber()).isEqualTo(2);
105 
106             orderMaster = rows.get(1);
107             assertThat(orderMaster.getId()).isEqualTo(2);
108             assertThat(orderMaster.getDetails()).hasSize(1);
109             orderDetail = orderMaster.getDetails().get(0);
110             assertThat(orderDetail.lineNumber()).isEqualTo(1);
111         }
112     }
113 
114     @Test
115     void testSingleTableJoin2() {
116         try (SqlSession session = sqlSessionFactory.openSession()) {
117             JoinMapper mapper = session.getMapper(JoinMapper.class);
118 
119             SelectStatementProvider selectStatement = select(orderMaster.orderId, orderDate, orderDetail.lineNumber, orderDetail.description, orderDetail.quantity)
120                     .from(orderMaster, "om")
121                     .join(orderDetail, "od", on(orderMaster.orderId, isEqualTo(orderDetail.orderId)))
122                     .build()
123                     .render(RenderingStrategies.MYBATIS3);
124 
125             String expectedStatement = "select om.order_id, om.order_date, od.line_number, od.description, od.quantity"
126                     + " from OrderMaster om join OrderDetail od on om.order_id = od.order_id";
127             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
128 
129             List<OrderMaster> rows = mapper.selectMany(selectStatement);
130 
131             assertThat(rows).hasSize(2);
132             OrderMaster orderMaster = rows.get(0);
133             assertThat(orderMaster.getId()).isEqualTo(1);
134             assertThat(orderMaster.getDetails()).hasSize(2);
135             OrderDetail orderDetail = orderMaster.getDetails().get(0);
136             assertThat(orderDetail.lineNumber()).isEqualTo(1);
137             orderDetail = orderMaster.getDetails().get(1);
138             assertThat(orderDetail.lineNumber()).isEqualTo(2);
139 
140             orderMaster = rows.get(1);
141             assertThat(orderMaster.getId()).isEqualTo(2);
142             assertThat(orderMaster.getDetails()).hasSize(1);
143             orderDetail = orderMaster.getDetails().get(0);
144             assertThat(orderDetail.lineNumber()).isEqualTo(1);
145         }
146     }
147 
148     @Test
149     void testCompoundJoin1() {
150         // this is a nonsensical join, but it does test the "and" capability
151         SelectStatementProvider selectStatement = select(orderMaster.orderId, orderDate, orderDetail.lineNumber, orderDetail.description, orderDetail.quantity)
152                 .from(orderMaster, "om")
153                 .join(orderDetail, "od").on(orderMaster.orderId, isEqualTo(orderDetail.orderId), and(orderMaster.orderId, isEqualTo(orderDetail.orderId)))
154                 .build()
155                 .render(RenderingStrategies.MYBATIS3);
156 
157         String expectedStatement = "select om.order_id, om.order_date, od.line_number, od.description, od.quantity"
158                 + " from OrderMaster om join OrderDetail od on om.order_id = od.order_id and om.order_id = od.order_id";
159         assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
160     }
161 
162     @Test
163     void testCompoundJoin2() {
164         // this is a nonsensical join, but it does test the "and" capability
165         SelectStatementProvider selectStatement = select(orderMaster.orderId, orderDate, orderDetail.lineNumber, orderDetail.description, orderDetail.quantity)
166                 .from(orderMaster, "om")
167                 .join(orderDetail, "od").on(orderMaster.orderId, isEqualTo(orderDetail.orderId))
168                 .configureStatement(c -> c.setNonRenderingWhereClauseAllowed(true))
169                 .and(orderMaster.orderId, isEqualTo(orderDetail.orderId))
170                 .build()
171                 .render(RenderingStrategies.MYBATIS3);
172 
173         String expectedStatement = "select om.order_id, om.order_date, od.line_number, od.description, od.quantity"
174                 + " from OrderMaster om join OrderDetail od on om.order_id = od.order_id and om.order_id = od.order_id";
175         assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
176     }
177 
178     @Test
179     void testCompoundJoin3() {
180         // this is a nonsensical join, but it does test the "and" capability
181         SelectStatementProvider selectStatement = select(orderMaster.orderId, orderDate, orderDetail.lineNumber, orderDetail.description, orderDetail.quantity)
182                 .from(orderMaster, "om")
183                 .join(orderDetail, "od", on(orderMaster.orderId, isEqualTo(orderDetail.orderId)), and(orderMaster.orderId, isEqualTo(orderDetail.orderId)))
184                 .build()
185                 .render(RenderingStrategies.MYBATIS3);
186 
187         String expectedStatement = "select om.order_id, om.order_date, od.line_number, od.description, od.quantity"
188                 + " from OrderMaster om join OrderDetail od on om.order_id = od.order_id and om.order_id = od.order_id";
189         assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
190     }
191 
192     @Test
193     void testCompoundJoin4() {
194         // this is a nonsensical join, but it does test the "and" capability
195         SelectStatementProvider selectStatement = select(orderMaster.orderId, orderDate, orderDetail.lineNumber, orderDetail.description, orderDetail.quantity)
196                 .from(orderMaster, "om")
197                 .leftJoin(orderDetail, "od", on(orderMaster.orderId, isEqualTo(orderDetail.orderId)), and(orderMaster.orderId, isEqualTo(orderDetail.orderId)))
198                 .build()
199                 .render(RenderingStrategies.MYBATIS3);
200 
201         String expectedStatement = "select om.order_id, om.order_date, od.line_number, od.description, od.quantity"
202                 + " from OrderMaster om left join OrderDetail od on om.order_id = od.order_id and om.order_id = od.order_id";
203         assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
204     }
205 
206     @Test
207     void testCompoundJoin5() {
208         // this is a nonsensical join, but it does test the "and" capability
209         SelectStatementProvider selectStatement = select(orderMaster.orderId, orderDate, orderDetail.lineNumber, orderDetail.description, orderDetail.quantity)
210                 .from(orderMaster, "om")
211                 .rightJoin(orderDetail, "od", on(orderMaster.orderId, isEqualTo(orderDetail.orderId)), and(orderMaster.orderId, isEqualTo(orderDetail.orderId)))
212                 .build()
213                 .render(RenderingStrategies.MYBATIS3);
214 
215         String expectedStatement = "select om.order_id, om.order_date, od.line_number, od.description, od.quantity"
216                 + " from OrderMaster om right join OrderDetail od on om.order_id = od.order_id and om.order_id = od.order_id";
217         assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
218     }
219 
220     @Test
221     void testCompoundJoin6() {
222         // this is a nonsensical join, but it does test the "and" capability
223         SelectStatementProvider selectStatement = select(orderMaster.orderId, orderDate, orderDetail.lineNumber, orderDetail.description, orderDetail.quantity)
224                 .from(orderMaster, "om")
225                 .fullJoin(orderDetail, "od", on(orderMaster.orderId, isEqualTo(orderDetail.orderId)), and(orderMaster.orderId, isEqualTo(orderDetail.orderId)))
226                 .build()
227                 .render(RenderingStrategies.MYBATIS3);
228 
229         String expectedStatement = "select om.order_id, om.order_date, od.line_number, od.description, od.quantity"
230                 + " from OrderMaster om full join OrderDetail od on om.order_id = od.order_id and om.order_id = od.order_id";
231         assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
232     }
233 
234     @Test
235     void testMultipleTableJoinWithWhereClause() {
236         try (SqlSession session = sqlSessionFactory.openSession()) {
237             JoinMapper mapper = session.getMapper(JoinMapper.class);
238 
239             SelectStatementProvider selectStatement = select(orderMaster.orderId, orderDate, orderLine.lineNumber, itemMaster.description, orderLine.quantity)
240                     .from(orderMaster, "om")
241                     .join(orderLine, "ol").on(orderMaster.orderId, isEqualTo(orderLine.orderId))
242                     .join(itemMaster, "im").on(orderLine.itemId, isEqualTo(itemMaster.itemId))
243                     .where(orderMaster.orderId, isEqualTo(2))
244                     .build()
245                     .render(RenderingStrategies.MYBATIS3);
246 
247             String expectedStatement = "select om.order_id, om.order_date, ol.line_number, im.description, ol.quantity"
248                     + " from OrderMaster om join OrderLine ol on om.order_id = ol.order_id join ItemMaster im on ol.item_id = im.item_id"
249                     + " where om.order_id = #{parameters.p1,jdbcType=INTEGER}";
250             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
251 
252             List<OrderMaster> rows = mapper.selectMany(selectStatement);
253 
254             assertThat(rows).hasSize(1);
255             OrderMaster orderMaster = rows.get(0);
256             assertThat(orderMaster.getId()).isEqualTo(2);
257             assertThat(orderMaster.getDetails()).hasSize(2);
258             OrderDetail orderDetail = orderMaster.getDetails().get(0);
259             assertThat(orderDetail.lineNumber()).isEqualTo(1);
260             orderDetail = orderMaster.getDetails().get(1);
261             assertThat(orderDetail.lineNumber()).isEqualTo(2);
262         }
263     }
264 
265     @Test
266     void testMultipleTableJoinWithApplyWhere() {
267         try (SqlSession session = sqlSessionFactory.openSession()) {
268             JoinMapper mapper = session.getMapper(JoinMapper.class);
269 
270             SelectStatementProvider selectStatement = select(orderMaster.orderId, orderDate, orderLine.lineNumber, itemMaster.description, orderLine.quantity)
271                     .from(orderMaster, "om")
272                     .join(orderLine, "ol").on(orderMaster.orderId, isEqualTo(orderLine.orderId))
273                     .join(itemMaster, "im").on(orderLine.itemId, isEqualTo(itemMaster.itemId))
274                     .applyWhere(where(orderMaster.orderId, isEqualTo(2)).toWhereApplier())
275                     .build()
276                     .render(RenderingStrategies.MYBATIS3);
277 
278             String expectedStatement = "select om.order_id, om.order_date, ol.line_number, im.description, ol.quantity"
279                     + " from OrderMaster om join OrderLine ol on om.order_id = ol.order_id join ItemMaster im on ol.item_id = im.item_id"
280                     + " where om.order_id = #{parameters.p1,jdbcType=INTEGER}";
281             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
282 
283             List<OrderMaster> rows = mapper.selectMany(selectStatement);
284 
285             assertThat(rows).hasSize(1);
286             OrderMaster orderMaster = rows.get(0);
287             assertThat(orderMaster.getId()).isEqualTo(2);
288             assertThat(orderMaster.getDetails()).hasSize(2);
289             OrderDetail orderDetail = orderMaster.getDetails().get(0);
290             assertThat(orderDetail.lineNumber()).isEqualTo(1);
291             orderDetail = orderMaster.getDetails().get(1);
292             assertThat(orderDetail.lineNumber()).isEqualTo(2);
293         }
294     }
295 
296     @Test
297     void testMultipleTableJoinWithComplexWhereClause() {
298         try (SqlSession session = sqlSessionFactory.openSession()) {
299             JoinMapper mapper = session.getMapper(JoinMapper.class);
300 
301             SelectStatementProvider selectStatement = select(orderMaster.orderId, orderDate, orderLine.lineNumber, itemMaster.description, orderLine.quantity)
302                     .from(orderMaster, "om")
303                     .join(orderLine, "ol").on(orderMaster.orderId, isEqualTo(orderLine.orderId))
304                     .join(itemMaster, "im").on(orderLine.itemId, isEqualTo(itemMaster.itemId))
305                     .where(orderMaster.orderId, isEqualTo(2), and(orderLine.lineNumber, isEqualTo(2)))
306                     .build()
307                     .render(RenderingStrategies.MYBATIS3);
308 
309             String expectedStatement = "select om.order_id, om.order_date, ol.line_number, im.description, ol.quantity"
310                     + " from OrderMaster om join OrderLine ol on om.order_id = ol.order_id join ItemMaster im on ol.item_id = im.item_id"
311                     + " where om.order_id = #{parameters.p1,jdbcType=INTEGER} and ol.line_number = #{parameters.p2,jdbcType=INTEGER}";
312             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
313 
314             List<OrderMaster> rows = mapper.selectMany(selectStatement);
315 
316             assertThat(rows).hasSize(1);
317             OrderMaster orderMaster = rows.get(0);
318             assertThat(orderMaster.getId()).isEqualTo(2);
319             assertThat(orderMaster.getDetails()).hasSize(1);
320             OrderDetail orderDetail = orderMaster.getDetails().get(0);
321             assertThat(orderDetail.lineNumber()).isEqualTo(2);
322         }
323     }
324 
325     @Test
326     void testMultipleTableJoinWithOrderBy() {
327         try (SqlSession session = sqlSessionFactory.openSession()) {
328             JoinMapper mapper = session.getMapper(JoinMapper.class);
329 
330             SelectStatementProvider selectStatement = select(orderMaster.orderId, orderDate, orderLine.lineNumber, itemMaster.description, orderLine.quantity)
331                     .from(orderMaster, "om")
332                     .join(orderLine, "ol").on(orderMaster.orderId, isEqualTo(orderLine.orderId))
333                     .join(itemMaster, "im").on(orderLine.itemId, isEqualTo(itemMaster.itemId))
334                     .orderBy(orderMaster.orderId)
335                     .build()
336                     .render(RenderingStrategies.MYBATIS3);
337 
338             String expectedStatement = "select om.order_id, om.order_date, ol.line_number, im.description, ol.quantity"
339                     + " from OrderMaster om join OrderLine ol on om.order_id = ol.order_id join ItemMaster im on ol.item_id = im.item_id"
340                     + " order by order_id";
341             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
342 
343             List<OrderMaster> rows = mapper.selectMany(selectStatement);
344 
345             assertThat(rows).hasSize(2);
346             OrderMaster orderMaster = rows.get(0);
347             assertThat(orderMaster.getId()).isEqualTo(1);
348             assertThat(orderMaster.getDetails()).hasSize(1);
349             OrderDetail orderDetail = orderMaster.getDetails().get(0);
350             assertThat(orderDetail.lineNumber()).isEqualTo(1);
351 
352             orderMaster = rows.get(1);
353             assertThat(orderMaster.getId()).isEqualTo(2);
354             assertThat(orderMaster.getDetails()).hasSize(2);
355             orderDetail = orderMaster.getDetails().get(0);
356             assertThat(orderDetail.lineNumber()).isEqualTo(1);
357             orderDetail = orderMaster.getDetails().get(1);
358             assertThat(orderDetail.lineNumber()).isEqualTo(2);
359         }
360     }
361 
362     @Test
363     void testMultipleTableJoinNoAliasWithOrderBy() {
364         try (SqlSession session = sqlSessionFactory.openSession()) {
365             JoinMapper mapper = session.getMapper(JoinMapper.class);
366 
367             SelectStatementProvider selectStatement = select(orderMaster.orderId, orderDate, orderLine.lineNumber, itemMaster.description, orderLine.quantity)
368                     .from(orderMaster)
369                     .join(orderLine).on(orderMaster.orderId, isEqualTo(orderLine.orderId))
370                     .join(itemMaster).on(orderLine.itemId, isEqualTo(itemMaster.itemId))
371                     .where(orderMaster.orderId, isEqualTo(2))
372                     .orderBy(orderMaster.orderId)
373                     .build()
374                     .render(RenderingStrategies.MYBATIS3);
375 
376             String expectedStatement = "select OrderMaster.order_id, OrderMaster.order_date, OrderLine.line_number, ItemMaster.description, OrderLine.quantity"
377                     + " from OrderMaster join OrderLine on OrderMaster.order_id = OrderLine.order_id join ItemMaster on OrderLine.item_id = ItemMaster.item_id"
378                     + " where OrderMaster.order_id = #{parameters.p1,jdbcType=INTEGER}"
379                     + " order by order_id";
380             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
381 
382             List<OrderMaster> rows = mapper.selectMany(selectStatement);
383 
384             assertThat(rows).hasSize(1);
385             OrderMaster orderMaster = rows.get(0);
386             assertThat(orderMaster.getId()).isEqualTo(2);
387             assertThat(orderMaster.getDetails()).hasSize(2);
388             OrderDetail orderDetail = orderMaster.getDetails().get(0);
389             assertThat(orderDetail.lineNumber()).isEqualTo(1);
390             orderDetail = orderMaster.getDetails().get(1);
391             assertThat(orderDetail.lineNumber()).isEqualTo(2);
392         }
393     }
394 
395     @Test
396     void testRightJoin() {
397         try (SqlSession session = sqlSessionFactory.openSession()) {
398             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
399 
400             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description)
401                     .from(orderLine, "ol")
402                     .rightJoin(itemMaster, "im").on(orderLine.itemId, isEqualTo(itemMaster.itemId))
403                     .orderBy(itemMaster.itemId)
404                     .build()
405                     .render(RenderingStrategies.MYBATIS3);
406 
407             String expectedStatement = "select ol.order_id, ol.quantity, im.item_id, im.description"
408                     + " from OrderLine ol right join ItemMaster im on ol.item_id = im.item_id"
409                     + " order by item_id";
410             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
411 
412             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
413 
414             assertThat(rows).hasSize(5);
415             Map<String, Object> row = rows.get(2);
416             assertThat(row).containsEntry("ORDER_ID", 1);
417             assertThat(row).containsEntry("QUANTITY", 1);
418             assertThat(row).containsEntry("DESCRIPTION", "First Base Glove");
419             assertThat(row).containsEntry("ITEM_ID", 33);
420 
421             row = rows.get(4);
422             assertThat(row).doesNotContainKey("ORDER_ID");
423             assertThat(row).doesNotContainKey("QUANTITY");
424             assertThat(row).containsEntry("DESCRIPTION", "Catcher Glove");
425             assertThat(row).containsEntry("ITEM_ID", 55);
426         }
427     }
428 
429     @Test
430     void testRightJoin2() {
431         try (SqlSession session = sqlSessionFactory.openSession()) {
432             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
433 
434             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description)
435                     .from(orderMaster, "om")
436                     .join(orderLine, "ol").on(orderMaster.orderId, isEqualTo(orderLine.orderId))
437                     .rightJoin(itemMaster, "im").on(orderLine.itemId, isEqualTo(itemMaster.itemId))
438                     .orderBy(orderLine.orderId, itemMaster.itemId)
439                     .build()
440                     .render(RenderingStrategies.MYBATIS3);
441 
442             String expectedStatement = "select ol.order_id, ol.quantity, im.item_id, im.description"
443                     + " from OrderMaster om join OrderLine ol on om.order_id = ol.order_id"
444                     + " right join ItemMaster im on ol.item_id = im.item_id"
445                     + " order by order_id, item_id";
446             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
447 
448             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
449 
450             assertThat(rows).hasSize(5);
451             Map<String, Object> row = rows.get(0);
452             assertThat(row).doesNotContainKey("ORDER_ID");
453             assertThat(row).doesNotContainKey("QUANTITY");
454             assertThat(row).containsEntry("DESCRIPTION", "Catcher Glove");
455             assertThat(row).containsEntry("ITEM_ID", 55);
456 
457             row = rows.get(4);
458             assertThat(row).containsEntry("ORDER_ID", 2);
459             assertThat(row).containsEntry("QUANTITY", 1);
460             assertThat(row).containsEntry("DESCRIPTION", "Outfield Glove");
461             assertThat(row).containsEntry("ITEM_ID", 44);
462         }
463     }
464 
465     @Test
466     void testRightJoin3() {
467         try (SqlSession session = sqlSessionFactory.openSession()) {
468             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
469 
470             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description)
471                     .from(orderMaster, "om")
472                     .join(orderLine, "ol", on(orderMaster.orderId, isEqualTo(orderLine.orderId)))
473                     .rightJoin(itemMaster, "im", on(orderLine.itemId, isEqualTo(itemMaster.itemId)))
474                     .orderBy(orderLine.orderId, itemMaster.itemId)
475                     .build()
476                     .render(RenderingStrategies.MYBATIS3);
477 
478             String expectedStatement = "select ol.order_id, ol.quantity, im.item_id, im.description"
479                     + " from OrderMaster om join OrderLine ol on om.order_id = ol.order_id"
480                     + " right join ItemMaster im on ol.item_id = im.item_id"
481                     + " order by order_id, item_id";
482             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
483 
484             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
485 
486             assertThat(rows).hasSize(5);
487             Map<String, Object> row = rows.get(0);
488             assertThat(row).doesNotContainKey("ORDER_ID");
489             assertThat(row).doesNotContainKey("QUANTITY");
490             assertThat(row).containsEntry("DESCRIPTION", "Catcher Glove");
491             assertThat(row).containsEntry("ITEM_ID", 55);
492 
493             row = rows.get(4);
494             assertThat(row).containsEntry("ORDER_ID", 2);
495             assertThat(row).containsEntry("QUANTITY", 1);
496             assertThat(row).containsEntry("DESCRIPTION", "Outfield Glove");
497             assertThat(row).containsEntry("ITEM_ID", 44);
498         }
499     }
500 
501     @Test
502     void testRightJoinNoAliases() {
503         try (SqlSession session = sqlSessionFactory.openSession()) {
504             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
505 
506             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description)
507                     .from(orderMaster)
508                     .join(orderLine).on(orderMaster.orderId, isEqualTo(orderLine.orderId))
509                     .rightJoin(itemMaster).on(orderLine.itemId, isEqualTo(itemMaster.itemId))
510                     .orderBy(orderLine.orderId, itemMaster.itemId)
511                     .build()
512                     .render(RenderingStrategies.MYBATIS3);
513 
514             String expectedStatement = "select OrderLine.order_id, OrderLine.quantity, ItemMaster.item_id, ItemMaster.description"
515                     + " from OrderMaster join OrderLine on OrderMaster.order_id = OrderLine.order_id"
516                     + " right join ItemMaster on OrderLine.item_id = ItemMaster.item_id"
517                     + " order by order_id, item_id";
518             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
519 
520             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
521 
522             assertThat(rows).hasSize(5);
523             Map<String, Object> row = rows.get(0);
524             assertThat(row).doesNotContainKey("ORDER_ID");
525             assertThat(row).doesNotContainKey("QUANTITY");
526             assertThat(row).containsEntry("DESCRIPTION", "Catcher Glove");
527             assertThat(row).containsEntry("ITEM_ID", 55);
528 
529             row = rows.get(4);
530             assertThat(row).containsEntry("ORDER_ID", 2);
531             assertThat(row).containsEntry("QUANTITY", 1);
532             assertThat(row).containsEntry("DESCRIPTION", "Outfield Glove");
533             assertThat(row).containsEntry("ITEM_ID", 44);
534         }
535     }
536 
537     @Test
538     void testLeftJoin() {
539         try (SqlSession session = sqlSessionFactory.openSession()) {
540             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
541 
542             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description)
543                     .from(itemMaster, "im")
544                     .leftJoin(orderLine, "ol").on(orderLine.itemId, isEqualTo(itemMaster.itemId))
545                     .orderBy(itemMaster.itemId)
546                     .build()
547                     .render(RenderingStrategies.MYBATIS3);
548 
549             String expectedStatement = "select ol.order_id, ol.quantity, im.item_id, im.description"
550                     + " from ItemMaster im left join OrderLine ol on ol.item_id = im.item_id"
551                     + " order by item_id";
552             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
553 
554             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
555 
556             assertThat(rows).hasSize(5);
557             Map<String, Object> row = rows.get(2);
558             assertThat(row).containsEntry("ORDER_ID", 1);
559             assertThat(row).containsEntry("QUANTITY", 1);
560             assertThat(row).containsEntry("DESCRIPTION", "First Base Glove");
561             assertThat(row).containsEntry("ITEM_ID", 33);
562 
563             row = rows.get(4);
564             assertThat(row).doesNotContainKey("ORDER_ID");
565             assertThat(row).doesNotContainKey("QUANTITY");
566             assertThat(row).containsEntry("DESCRIPTION", "Catcher Glove");
567             assertThat(row).containsEntry("ITEM_ID", 55);
568         }
569     }
570 
571     @Test
572     void testLeftJoin2() {
573         try (SqlSession session = sqlSessionFactory.openSession()) {
574             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
575 
576             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description)
577                     .from(orderMaster, "om")
578                     .join(orderLine, "ol").on(orderMaster.orderId, isEqualTo(orderLine.orderId))
579                     .leftJoin(itemMaster, "im").on(orderLine.itemId, isEqualTo(itemMaster.itemId))
580                     .orderBy(orderLine.orderId, itemMaster.itemId)
581                     .build()
582                     .render(RenderingStrategies.MYBATIS3);
583 
584             String expectedStatement = "select ol.order_id, ol.quantity, im.item_id, im.description"
585                     + " from OrderMaster om join OrderLine ol on om.order_id = ol.order_id"
586                     + " left join ItemMaster im on ol.item_id = im.item_id"
587                     + " order by order_id, item_id";
588             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
589 
590             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
591 
592             assertThat(rows).hasSize(5);
593             Map<String, Object> row = rows.get(2);
594             assertThat(row).containsEntry("ORDER_ID", 2);
595             assertThat(row).containsEntry("QUANTITY", 6);
596             assertThat(row).doesNotContainKey("DESCRIPTION");
597             assertThat(row).doesNotContainKey("ITEM_ID");
598 
599             row = rows.get(4);
600             assertThat(row).containsEntry("ORDER_ID", 2);
601             assertThat(row).containsEntry("QUANTITY", 1);
602             assertThat(row).containsEntry("DESCRIPTION", "Outfield Glove");
603             assertThat(row).containsEntry("ITEM_ID", 44);
604         }
605     }
606 
607     @Test
608     void testLeftJoin3() {
609         try (SqlSession session = sqlSessionFactory.openSession()) {
610             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
611 
612             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description)
613                     .from(orderMaster, "om")
614                     .join(orderLine, "ol", on(orderMaster.orderId, isEqualTo(orderLine.orderId)))
615                     .leftJoin(itemMaster, "im", on(orderLine.itemId, isEqualTo(itemMaster.itemId)))
616                     .orderBy(orderLine.orderId, itemMaster.itemId)
617                     .build()
618                     .render(RenderingStrategies.MYBATIS3);
619 
620             String expectedStatement = "select ol.order_id, ol.quantity, im.item_id, im.description"
621                     + " from OrderMaster om join OrderLine ol on om.order_id = ol.order_id"
622                     + " left join ItemMaster im on ol.item_id = im.item_id"
623                     + " order by order_id, item_id";
624             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
625 
626             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
627 
628             assertThat(rows).hasSize(5);
629             Map<String, Object> row = rows.get(2);
630             assertThat(row).containsEntry("ORDER_ID", 2);
631             assertThat(row).containsEntry("QUANTITY", 6);
632             assertThat(row).doesNotContainKey("DESCRIPTION");
633             assertThat(row).doesNotContainKey("ITEM_ID");
634 
635             row = rows.get(4);
636             assertThat(row).containsEntry("ORDER_ID", 2);
637             assertThat(row).containsEntry("QUANTITY", 1);
638             assertThat(row).containsEntry("DESCRIPTION", "Outfield Glove");
639             assertThat(row).containsEntry("ITEM_ID", 44);
640         }
641     }
642 
643     @Test
644     void testLeftJoinNoAliases() {
645         try (SqlSession session = sqlSessionFactory.openSession()) {
646             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
647 
648             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description)
649                     .from(orderMaster)
650                     .join(orderLine).on(orderMaster.orderId, isEqualTo(orderLine.orderId))
651                     .leftJoin(itemMaster).on(orderLine.itemId, isEqualTo(itemMaster.itemId))
652                     .orderBy(orderLine.orderId, itemMaster.itemId)
653                     .build()
654                     .render(RenderingStrategies.MYBATIS3);
655 
656             String expectedStatement = "select OrderLine.order_id, OrderLine.quantity, ItemMaster.item_id, ItemMaster.description"
657                     + " from OrderMaster join OrderLine on OrderMaster.order_id = OrderLine.order_id"
658                     + " left join ItemMaster on OrderLine.item_id = ItemMaster.item_id"
659                     + " order by order_id, item_id";
660             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
661 
662             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
663 
664             assertThat(rows).hasSize(5);
665             Map<String, Object> row = rows.get(2);
666             assertThat(row).containsEntry("ORDER_ID", 2);
667             assertThat(row).containsEntry("QUANTITY", 6);
668             assertThat(row).doesNotContainKey("DESCRIPTION");
669             assertThat(row).doesNotContainKey("ITEM_ID");
670 
671             row = rows.get(4);
672             assertThat(row).containsEntry("ORDER_ID", 2);
673             assertThat(row).containsEntry("QUANTITY", 1);
674             assertThat(row).containsEntry("DESCRIPTION", "Outfield Glove");
675             assertThat(row).containsEntry("ITEM_ID", 44);
676         }
677     }
678 
679     @Test
680     void testFullJoin() {
681         try (SqlSession session = sqlSessionFactory.openSession()) {
682             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
683 
684             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, orderLine.itemId.as("ol_itemid"), itemMaster.itemId.as("im_itemid"), itemMaster.description)
685                     .from(itemMaster, "im")
686                     .fullJoin(orderLine, "ol").on(itemMaster.itemId, isEqualTo(orderLine.itemId))
687                     .orderBy(orderLine.orderId, sortColumn("im_itemid"))
688                     .build()
689                     .render(RenderingStrategies.MYBATIS3);
690 
691             String expectedStatement = "select ol.order_id, ol.quantity, ol.item_id as ol_itemid, im.item_id as im_itemid, im.description"
692                     + " from ItemMaster im full join OrderLine ol on im.item_id = ol.item_id"
693                     + " order by order_id, im_itemid";
694             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
695 
696             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
697 
698             assertThat(rows).hasSize(6);
699             Map<String, Object> row = rows.get(0);
700             assertThat(row).doesNotContainKey("ORDER_ID");
701             assertThat(row).doesNotContainKey("QUANTITY");
702             assertThat(row).containsEntry("DESCRIPTION", "Catcher Glove");
703             assertThat(row).containsEntry("IM_ITEMID", 55);
704 
705             row = rows.get(2);
706             assertThat(row).containsEntry("ORDER_ID", 1);
707             assertThat(row).containsEntry("QUANTITY", 1);
708             assertThat(row).containsEntry("DESCRIPTION", "First Base Glove");
709             assertThat(row).containsEntry("IM_ITEMID", 33);
710 
711             row = rows.get(3);
712             assertThat(row).containsEntry("ORDER_ID", 2);
713             assertThat(row).containsEntry("QUANTITY", 6);
714             assertThat(row).containsEntry("OL_ITEMID", 66);
715             assertThat(row).doesNotContainKey("DESCRIPTION");
716             assertThat(row).doesNotContainKey("IM_ITEMID");
717         }
718     }
719 
720     @Test
721     void testFullJoin2() {
722         try (SqlSession session = sqlSessionFactory.openSession()) {
723             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
724 
725             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description)
726                     .from(orderMaster, "om")
727                     .join(orderLine, "ol").on(orderMaster.orderId, isEqualTo(orderLine.orderId))
728                     .fullJoin(itemMaster, "im").on(orderLine.itemId, isEqualTo(itemMaster.itemId))
729                     .orderBy(orderLine.orderId, itemMaster.itemId)
730                     .build()
731                     .render(RenderingStrategies.MYBATIS3);
732 
733             String expectedStatement = "select ol.order_id, ol.quantity, im.item_id, im.description"
734                     + " from OrderMaster om join OrderLine ol on om.order_id = ol.order_id"
735                     + " full join ItemMaster im on ol.item_id = im.item_id"
736                     + " order by order_id, item_id";
737             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
738 
739             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
740 
741             assertThat(rows).hasSize(6);
742             Map<String, Object> row = rows.get(0);
743             assertThat(row).doesNotContainKey("ORDER_ID");
744             assertThat(row).doesNotContainKey("QUANTITY");
745             assertThat(row).containsEntry("DESCRIPTION", "Catcher Glove");
746             assertThat(row).containsEntry("ITEM_ID", 55);
747 
748             row = rows.get(3);
749             assertThat(row).containsEntry("ORDER_ID", 2);
750             assertThat(row).containsEntry("QUANTITY", 6);
751             assertThat(row).doesNotContainKey("DESCRIPTION");
752             assertThat(row).doesNotContainKey("ITEM_ID");
753 
754             row = rows.get(5);
755             assertThat(row).containsEntry("ORDER_ID", 2);
756             assertThat(row).containsEntry("QUANTITY", 1);
757             assertThat(row).containsEntry("DESCRIPTION", "Outfield Glove");
758             assertThat(row).containsEntry("ITEM_ID", 44);
759         }
760     }
761 
762     @Test
763     void testFullJoin3() {
764         try (SqlSession session = sqlSessionFactory.openSession()) {
765             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
766 
767             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description)
768                     .from(orderMaster, "om")
769                     .join(orderLine, "ol", on(orderMaster.orderId, isEqualTo(orderLine.orderId)))
770                     .fullJoin(itemMaster, "im", on(orderLine.itemId, isEqualTo(itemMaster.itemId)))
771                     .orderBy(orderLine.orderId, itemMaster.itemId)
772                     .build()
773                     .render(RenderingStrategies.MYBATIS3);
774 
775             String expectedStatement = "select ol.order_id, ol.quantity, im.item_id, im.description"
776                     + " from OrderMaster om join OrderLine ol on om.order_id = ol.order_id"
777                     + " full join ItemMaster im on ol.item_id = im.item_id"
778                     + " order by order_id, item_id";
779             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
780 
781             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
782 
783             assertThat(rows).hasSize(6);
784             Map<String, Object> row = rows.get(0);
785             assertThat(row).doesNotContainKey("ORDER_ID");
786             assertThat(row).doesNotContainKey("QUANTITY");
787             assertThat(row).containsEntry("DESCRIPTION", "Catcher Glove");
788             assertThat(row).containsEntry("ITEM_ID", 55);
789 
790             row = rows.get(3);
791             assertThat(row).containsEntry("ORDER_ID", 2);
792             assertThat(row).containsEntry("QUANTITY", 6);
793             assertThat(row).doesNotContainKey("DESCRIPTION");
794             assertThat(row).doesNotContainKey("ITEM_ID");
795 
796             row = rows.get(5);
797             assertThat(row).containsEntry("ORDER_ID", 2);
798             assertThat(row).containsEntry("QUANTITY", 1);
799             assertThat(row).containsEntry("DESCRIPTION", "Outfield Glove");
800             assertThat(row).containsEntry("ITEM_ID", 44);
801         }
802     }
803 
804     @Test
805     void testFullJoin4() {
806         try (SqlSession session = sqlSessionFactory.openSession()) {
807             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
808 
809             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.description)
810                     .from(orderMaster, "om")
811                     .join(orderLine, "ol", on(orderMaster.orderId, isEqualTo(orderLine.orderId)))
812                     .fullJoin(itemMaster, "im", on(orderLine.itemId, isEqualTo(itemMaster.itemId)))
813                     .orderBy(orderLine.orderId, sortColumn("im", itemMaster.itemId))
814                     .build()
815                     .render(RenderingStrategies.MYBATIS3);
816 
817             String expectedStatement = "select ol.order_id, ol.quantity, im.description"
818                     + " from OrderMaster om join OrderLine ol on om.order_id = ol.order_id"
819                     + " full join ItemMaster im on ol.item_id = im.item_id"
820                     + " order by order_id, im.item_id";
821             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
822 
823             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
824 
825             assertThat(rows).hasSize(6);
826             Map<String, Object> row = rows.get(0);
827             assertThat(row).doesNotContainKey("ORDER_ID");
828             assertThat(row).doesNotContainKey("QUANTITY");
829             assertThat(row).containsEntry("DESCRIPTION", "Catcher Glove");
830 
831             row = rows.get(3);
832             assertThat(row).containsEntry("ORDER_ID", 2);
833             assertThat(row).containsEntry("QUANTITY", 6);
834             assertThat(row).doesNotContainKey("DESCRIPTION");
835 
836             row = rows.get(5);
837             assertThat(row).containsEntry("ORDER_ID", 2);
838             assertThat(row).containsEntry("QUANTITY", 1);
839             assertThat(row).containsEntry("DESCRIPTION", "Outfield Glove");
840         }
841     }
842 
843     @Test
844     void testFullJoin5() {
845         try (SqlSession session = sqlSessionFactory.openSession()) {
846             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
847 
848             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.description)
849                     .from(orderMaster, "om")
850                     .join(orderLine, "ol", on(orderMaster.orderId, isEqualTo(orderLine.orderId)))
851                     .fullJoin(itemMaster, "im", on(orderLine.itemId, isEqualTo(itemMaster.itemId)))
852                     .orderBy(orderLine.orderId, sortColumn("im", itemMaster.itemId).descending())
853                     .build()
854                     .render(RenderingStrategies.MYBATIS3);
855 
856             String expectedStatement = "select ol.order_id, ol.quantity, im.description"
857                     + " from OrderMaster om join OrderLine ol on om.order_id = ol.order_id"
858                     + " full join ItemMaster im on ol.item_id = im.item_id"
859                     + " order by order_id, im.item_id DESC";
860             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
861 
862             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
863 
864             assertThat(rows).hasSize(6);
865             Map<String, Object> row = rows.get(0);
866             assertThat(row).doesNotContainKey("ORDER_ID");
867             assertThat(row).doesNotContainKey("QUANTITY");
868             assertThat(row).containsEntry("DESCRIPTION", "Catcher Glove");
869 
870             row = rows.get(3);
871             assertThat(row).containsEntry("ORDER_ID", 2);
872             assertThat(row).containsEntry("QUANTITY", 6);
873             assertThat(row).doesNotContainKey("DESCRIPTION");
874 
875             row = rows.get(5);
876             assertThat(row).containsEntry("ORDER_ID", 2);
877             assertThat(row).containsEntry("QUANTITY", 1);
878             assertThat(row).containsEntry("DESCRIPTION", "Helmet");
879         }
880     }
881 
882     @Test
883     void testFullJoinNoAliases() {
884         try (SqlSession session = sqlSessionFactory.openSession()) {
885             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
886 
887             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description)
888                     .from(orderMaster)
889                     .join(orderLine).on(orderMaster.orderId, isEqualTo(orderLine.orderId))
890                     .fullJoin(itemMaster).on(orderLine.itemId, isEqualTo(itemMaster.itemId))
891                     .orderBy(orderLine.orderId, itemMaster.itemId)
892                     .build()
893                     .render(RenderingStrategies.MYBATIS3);
894 
895             String expectedStatement = "select OrderLine.order_id, OrderLine.quantity, ItemMaster.item_id, ItemMaster.description"
896                     + " from OrderMaster join OrderLine on OrderMaster.order_id = OrderLine.order_id"
897                     + " full join ItemMaster on OrderLine.item_id = ItemMaster.item_id"
898                     + " order by order_id, item_id";
899             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
900 
901             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
902 
903             assertThat(rows).hasSize(6);
904             Map<String, Object> row = rows.get(0);
905             assertThat(row).doesNotContainKey("ORDER_ID");
906             assertThat(row).doesNotContainKey("QUANTITY");
907             assertThat(row).containsEntry("DESCRIPTION", "Catcher Glove");
908             assertThat(row).containsEntry("ITEM_ID", 55);
909 
910             row = rows.get(3);
911             assertThat(row).containsEntry("ORDER_ID", 2);
912             assertThat(row).containsEntry("QUANTITY", 6);
913             assertThat(row).doesNotContainKey("DESCRIPTION");
914             assertThat(row).doesNotContainKey("ITEM_ID");
915 
916             row = rows.get(5);
917             assertThat(row).containsEntry("ORDER_ID", 2);
918             assertThat(row).containsEntry("QUANTITY", 1);
919             assertThat(row).containsEntry("DESCRIPTION", "Outfield Glove");
920             assertThat(row).containsEntry("ITEM_ID", 44);
921         }
922     }
923 
924     @Test
925     void testSelf() {
926         try (SqlSession session = sqlSessionFactory.openSession()) {
927             JoinMapper mapper = session.getMapper(JoinMapper.class);
928 
929             // create second table instance for self-join
930             UserDynamicSQLSupport.User user2 = new UserDynamicSQLSupport.User();
931 
932             // get Bamm Bamm's parent - should be Barney
933             SelectStatementProvider selectStatement = select(user.userId, user.userName, user.parentId)
934                     .from(user, "u1")
935                     .join(user2, "u2").on(user.userId, isEqualTo(user2.parentId))
936                     .where(user2.userId, isEqualTo(4))
937                     .build()
938                     .render(RenderingStrategies.MYBATIS3);
939 
940             String expectedStatement = "select u1.user_id, u1.user_name, u1.parent_id"
941                     + " from User u1 join User u2 on u1.user_id = u2.parent_id"
942                     + " where u2.user_id = #{parameters.p1,jdbcType=INTEGER}";
943             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
944 
945             List<User> rows = mapper.selectUsers(selectStatement);
946 
947             assertThat(rows).hasSize(1);
948             User row = rows.get(0);
949             assertThat(row.userId()).isEqualTo(2);
950             assertThat(row.userName()).isEqualTo("Barney");
951             assertThat(row.parentId()).isNull();
952         }
953     }
954 
955     @Test
956     void testSelfWithDuplicateAlias() {
957         QueryExpressionDSL<SelectModel> dsl = select(user.userId, user.userName, user.parentId)
958                 .from(user, "u1");
959 
960         assertThatExceptionOfType(DuplicateTableAliasException.class).isThrownBy(() -> dsl.join(user, "u2"))
961                 .withMessage(Messages.getString("ERROR.1", user.tableName(), "u2", "u1"));
962     }
963 
964     @Test
965     void testSelfWithNewAlias() {
966         try (SqlSession session = sqlSessionFactory.openSession()) {
967             JoinMapper mapper = session.getMapper(JoinMapper.class);
968 
969             // create second table instance for self-join
970             UserDynamicSQLSupport.User user2 = user.withAlias("u2");
971 
972             // get Bamm Bamm's parent - should be Barney
973             SelectStatementProvider selectStatement = select(user.userId, user.userName, user.parentId)
974                     .from(user)
975                     .join(user2).on(user.userId, isEqualTo(user2.parentId))
976                     .where(user2.userId, isEqualTo(4))
977                     .build()
978                     .render(RenderingStrategies.MYBATIS3);
979 
980             String expectedStatement = "select User.user_id, User.user_name, User.parent_id"
981                     + " from User join User u2 on User.user_id = u2.parent_id"
982                     + " where u2.user_id = #{parameters.p1,jdbcType=INTEGER}";
983             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
984 
985             List<User> rows = mapper.selectUsers(selectStatement);
986 
987             assertThat(rows).hasSize(1);
988             User row = rows.get(0);
989             assertThat(row.userId()).isEqualTo(2);
990             assertThat(row.userName()).isEqualTo("Barney");
991             assertThat(row.parentId()).isNull();
992         }
993     }
994 
995     @Test
996     void testSelfWithNewAliasAndOverride() {
997         try (SqlSession session = sqlSessionFactory.openSession()) {
998             JoinMapper mapper = session.getMapper(JoinMapper.class);
999 
1000             // create second table instance for self-join
1001             UserDynamicSQLSupport.User user2 = user.withAlias("other_user");
1002 
1003             // get Bamm Bamm's parent - should be Barney
1004             SelectStatementProvider selectStatement = select(user.userId, user.userName, user.parentId)
1005                     .from(user, "u1")
1006                     .join(user2, "u2").on(user.userId, isEqualTo(user2.parentId))
1007                     .where(user2.userId, isEqualTo(4))
1008                     .build()
1009                     .render(RenderingStrategies.MYBATIS3);
1010 
1011             String expectedStatement = "select u1.user_id, u1.user_name, u1.parent_id"
1012                     + " from User u1 join User u2 on u1.user_id = u2.parent_id"
1013                     + " where u2.user_id = #{parameters.p1,jdbcType=INTEGER}";
1014             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
1015 
1016             List<User> rows = mapper.selectUsers(selectStatement);
1017 
1018             assertThat(rows).hasSize(1);
1019             User row = rows.get(0);
1020             assertThat(row.userId()).isEqualTo(2);
1021             assertThat(row.userName()).isEqualTo("Barney");
1022             assertThat(row.parentId()).isNull();
1023         }
1024     }
1025 
1026     @Test
1027     void testLimitAndOffsetAfterJoin() {
1028         try (SqlSession session = sqlSessionFactory.openSession()) {
1029             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
1030 
1031             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description)
1032                     .from(itemMaster, "im")
1033                     .leftJoin(orderLine, "ol").on(orderLine.itemId, isEqualTo(itemMaster.itemId))
1034                     .limit(2)
1035                     .offset(1)
1036                     .build()
1037                     .render(RenderingStrategies.MYBATIS3);
1038 
1039             String expectedStatement = "select ol.order_id, ol.quantity, im.item_id, im.description"
1040                     + " from ItemMaster im left join OrderLine ol on ol.item_id = im.item_id"
1041                     + " limit #{parameters.p1} offset #{parameters.p2}";
1042             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
1043 
1044             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
1045 
1046             assertThat(rows).hasSize(2);
1047             Map<String, Object> row = rows.get(0);
1048             assertThat(row).containsEntry("ORDER_ID", 2);
1049             assertThat(row).containsEntry("QUANTITY", 1);
1050             assertThat(row).containsEntry("DESCRIPTION", "Helmet");
1051             assertThat(row).containsEntry("ITEM_ID", 22);
1052 
1053             row = rows.get(1);
1054             assertThat(row).containsEntry("ORDER_ID", 1);
1055             assertThat(row).containsEntry("QUANTITY", 1);
1056             assertThat(row).containsEntry("DESCRIPTION", "First Base Glove");
1057             assertThat(row).containsEntry("ITEM_ID", 33);
1058         }
1059     }
1060 
1061     @Test
1062     void testLimitOnlyAfterJoin() {
1063         try (SqlSession session = sqlSessionFactory.openSession()) {
1064             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
1065 
1066             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description)
1067                     .from(itemMaster, "im")
1068                     .leftJoin(orderLine, "ol").on(orderLine.itemId, isEqualTo(itemMaster.itemId))
1069                     .limit(2)
1070                     .build()
1071                     .render(RenderingStrategies.MYBATIS3);
1072 
1073             String expectedStatement = "select ol.order_id, ol.quantity, im.item_id, im.description"
1074                     + " from ItemMaster im left join OrderLine ol on ol.item_id = im.item_id"
1075                     + " limit #{parameters.p1}";
1076             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
1077 
1078             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
1079 
1080             assertThat(rows).hasSize(2);
1081             Map<String, Object> row = rows.get(0);
1082             assertThat(row).containsEntry("ORDER_ID", 1);
1083             assertThat(row).containsEntry("QUANTITY", 1);
1084             assertThat(row).containsEntry("DESCRIPTION", "Helmet");
1085             assertThat(row).containsEntry("ITEM_ID", 22);
1086 
1087             row = rows.get(1);
1088             assertThat(row).containsEntry("ORDER_ID", 2);
1089             assertThat(row).containsEntry("QUANTITY", 1);
1090             assertThat(row).containsEntry("DESCRIPTION", "Helmet");
1091             assertThat(row).containsEntry("ITEM_ID", 22);
1092         }
1093     }
1094 
1095     @Test
1096     void testOffsetOnlyAfterJoin() {
1097         try (SqlSession session = sqlSessionFactory.openSession()) {
1098             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
1099 
1100             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description)
1101                     .from(itemMaster, "im")
1102                     .leftJoin(orderLine, "ol").on(orderLine.itemId, isEqualTo(itemMaster.itemId))
1103                     .offset(2)
1104                     .build()
1105                     .render(RenderingStrategies.MYBATIS3);
1106 
1107             String expectedStatement = "select ol.order_id, ol.quantity, im.item_id, im.description"
1108                     + " from ItemMaster im left join OrderLine ol on ol.item_id = im.item_id"
1109                     + " offset #{parameters.p1} rows";
1110             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
1111 
1112             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
1113 
1114             assertThat(rows).hasSize(3);
1115             Map<String, Object> row = rows.get(0);
1116             assertThat(row).containsEntry("ORDER_ID", 1);
1117             assertThat(row).containsEntry("QUANTITY", 1);
1118             assertThat(row).containsEntry("DESCRIPTION", "First Base Glove");
1119             assertThat(row).containsEntry("ITEM_ID", 33);
1120 
1121             row = rows.get(1);
1122             assertThat(row).containsEntry("ORDER_ID", 2);
1123             assertThat(row).containsEntry("QUANTITY", 1);
1124             assertThat(row).containsEntry("DESCRIPTION", "Outfield Glove");
1125             assertThat(row).containsEntry("ITEM_ID", 44);
1126         }
1127     }
1128 
1129     @Test
1130     void testOffsetAndFetchFirstAfterJoin() {
1131         try (SqlSession session = sqlSessionFactory.openSession()) {
1132             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
1133 
1134             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description)
1135                     .from(itemMaster, "im")
1136                     .leftJoin(orderLine, "ol").on(orderLine.itemId, isEqualTo(itemMaster.itemId))
1137                     .offset(1)
1138                     .fetchFirst(2).rowsOnly()
1139                     .build()
1140                     .render(RenderingStrategies.MYBATIS3);
1141 
1142             String expectedStatement = "select ol.order_id, ol.quantity, im.item_id, im.description"
1143                     + " from ItemMaster im left join OrderLine ol on ol.item_id = im.item_id"
1144                     + " offset #{parameters.p1} rows fetch first #{parameters.p2} rows only";
1145             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
1146 
1147             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
1148 
1149             assertThat(rows).hasSize(2);
1150             Map<String, Object> row = rows.get(0);
1151             assertThat(row).containsEntry("ORDER_ID", 2);
1152             assertThat(row).containsEntry("QUANTITY", 1);
1153             assertThat(row).containsEntry("DESCRIPTION", "Helmet");
1154             assertThat(row).containsEntry("ITEM_ID", 22);
1155 
1156             row = rows.get(1);
1157             assertThat(row).containsEntry("ORDER_ID", 1);
1158             assertThat(row).containsEntry("QUANTITY", 1);
1159             assertThat(row).containsEntry("DESCRIPTION", "First Base Glove");
1160             assertThat(row).containsEntry("ITEM_ID", 33);
1161         }
1162     }
1163 
1164     @Test
1165     void testFetchFirstOnlyAfterJoin() {
1166         try (SqlSession session = sqlSessionFactory.openSession()) {
1167             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
1168 
1169             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description)
1170                     .from(itemMaster, "im")
1171                     .leftJoin(orderLine, "ol").on(orderLine.itemId, isEqualTo(itemMaster.itemId))
1172                     .fetchFirst(2).rowsOnly()
1173                     .build()
1174                     .render(RenderingStrategies.MYBATIS3);
1175 
1176             String expectedStatement = "select ol.order_id, ol.quantity, im.item_id, im.description"
1177                     + " from ItemMaster im left join OrderLine ol on ol.item_id = im.item_id"
1178                     + " fetch first #{parameters.p1} rows only";
1179             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
1180 
1181             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
1182 
1183             assertThat(rows).hasSize(2);
1184             Map<String, Object> row = rows.get(0);
1185             assertThat(row).containsEntry("ORDER_ID", 1);
1186             assertThat(row).containsEntry("QUANTITY", 1);
1187             assertThat(row).containsEntry("DESCRIPTION", "Helmet");
1188             assertThat(row).containsEntry("ITEM_ID", 22);
1189 
1190             row = rows.get(1);
1191             assertThat(row).containsEntry("ORDER_ID", 2);
1192             assertThat(row).containsEntry("QUANTITY", 1);
1193             assertThat(row).containsEntry("DESCRIPTION", "Helmet");
1194             assertThat(row).containsEntry("ITEM_ID", 22);
1195         }
1196     }
1197 
1198     @Test
1199     void testJoinWithParameterValue() {
1200         try (SqlSession session = sqlSessionFactory.openSession()) {
1201             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
1202 
1203             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description)
1204                     .from(itemMaster, "im")
1205                     .join(orderLine, "ol").on(orderLine.itemId, isEqualTo(itemMaster.itemId))
1206                     .and(orderLine.orderId, isEqualTo(1))
1207                     .build()
1208                     .render(RenderingStrategies.MYBATIS3);
1209 
1210             String expectedStatement = "select ol.order_id, ol.quantity, im.item_id, im.description"
1211                     + " from ItemMaster im join OrderLine ol on ol.item_id = im.item_id"
1212                     + " and ol.order_id = #{parameters.p1,jdbcType=INTEGER}";
1213             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
1214 
1215             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
1216 
1217             assertThat(rows).hasSize(2);
1218             Map<String, Object> row = rows.get(0);
1219             assertThat(row).containsEntry("ORDER_ID", 1);
1220             assertThat(row).containsEntry("QUANTITY", 1);
1221             assertThat(row).containsEntry("DESCRIPTION", "Helmet");
1222             assertThat(row).containsEntry("ITEM_ID", 22);
1223 
1224             row = rows.get(1);
1225             assertThat(row).containsEntry("ORDER_ID", 1);
1226             assertThat(row).containsEntry("QUANTITY", 1);
1227             assertThat(row).containsEntry("DESCRIPTION", "First Base Glove");
1228             assertThat(row).containsEntry("ITEM_ID", 33);
1229         }
1230     }
1231 
1232     @Test
1233     void testJoinWithConstant() {
1234         try (SqlSession session = sqlSessionFactory.openSession()) {
1235             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
1236 
1237             SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description)
1238                     .from(itemMaster, "im")
1239                     .join(orderLine, "ol").on(orderLine.itemId, isEqualTo(itemMaster.itemId))
1240                     .and(orderLine.orderId, isEqualTo(constant("1")))
1241                     .build()
1242                     .render(RenderingStrategies.MYBATIS3);
1243 
1244             String expectedStatement = "select ol.order_id, ol.quantity, im.item_id, im.description"
1245                     + " from ItemMaster im join OrderLine ol on ol.item_id = im.item_id"
1246                     + " and ol.order_id = 1";
1247             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
1248 
1249             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
1250 
1251             assertThat(rows).hasSize(2);
1252             Map<String, Object> row = rows.get(0);
1253             assertThat(row).containsEntry("ORDER_ID", 1);
1254             assertThat(row).containsEntry("QUANTITY", 1);
1255             assertThat(row).containsEntry("DESCRIPTION", "Helmet");
1256             assertThat(row).containsEntry("ITEM_ID", 22);
1257 
1258             row = rows.get(1);
1259             assertThat(row).containsEntry("ORDER_ID", 1);
1260             assertThat(row).containsEntry("QUANTITY", 1);
1261             assertThat(row).containsEntry("DESCRIPTION", "First Base Glove");
1262             assertThat(row).containsEntry("ITEM_ID", 33);
1263         }
1264     }
1265 
1266     @Test
1267     void testJoinWithGroupBy() {
1268         try (SqlSession session = sqlSessionFactory.openSession()) {
1269             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
1270 
1271             SelectStatementProvider selectStatement = select(orderMaster.orderId, count().as("linecount"))
1272                     .from(orderMaster, "om")
1273                     .join(orderDetail, "od").on(orderMaster.orderId, isEqualTo(orderDetail.orderId))
1274                     .groupBy(orderMaster.orderId)
1275                     .orderBy(orderDetail.orderId)
1276                     .build()
1277                     .render(RenderingStrategies.MYBATIS3);
1278 
1279             String expectedStatement = "select om.order_id, count(*) as linecount from OrderMaster om join OrderDetail od on om.order_id = od.order_id group by om.order_id order by order_id";
1280             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
1281 
1282             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
1283 
1284             assertThat(rows).hasSize(2);
1285             assertThat(rows.get(0)).containsOnly(entry("ORDER_ID", 1), entry("LINECOUNT", 2L));
1286             assertThat(rows.get(1)).containsOnly(entry("ORDER_ID", 2), entry("LINECOUNT", 1L));
1287         }
1288     }
1289 
1290     @Test
1291     void testSubQuery() {
1292         try (SqlSession session = sqlSessionFactory.openSession()) {
1293             CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
1294 
1295             SelectStatementProvider selectStatement = select(orderMaster.orderId,
1296                     subQuery(select(count())
1297                             .from(orderDetail, "od")
1298                             .where(orderMaster.orderId, isEqualTo(orderDetail.orderId))
1299                     ).as("linecount"))
1300                     .from(orderMaster, "om")
1301                     .orderBy(orderMaster.orderId)
1302                     .build()
1303                     .render(RenderingStrategies.MYBATIS3);
1304 
1305             String expectedStatement = "select om.order_id, (select count(*) from OrderDetail od where om.order_id = od.order_id) as linecount from OrderMaster om order by order_id";
1306             assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatement);
1307 
1308             List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
1309 
1310             assertThat(rows).hasSize(2);
1311             assertThat(rows.get(0)).containsOnly(entry("ORDER_ID", 1), entry("LINECOUNT", 2L));
1312             assertThat(rows.get(1)).containsOnly(entry("ORDER_ID", 2), entry("LINECOUNT", 1L));
1313         }
1314     }
1315 }