diff --git a/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java b/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java index 5aa8df44a..49b52f5c4 100644 --- a/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java +++ b/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java @@ -43,9 +43,10 @@ import org.mybatis.dynamic.sql.select.function.Substring; import org.mybatis.dynamic.sql.select.function.Subtract; import org.mybatis.dynamic.sql.select.function.Upper; +import org.mybatis.dynamic.sql.select.join.AndJoinCriterion; import org.mybatis.dynamic.sql.select.join.EqualTo; import org.mybatis.dynamic.sql.select.join.JoinCondition; -import org.mybatis.dynamic.sql.select.join.JoinCriterion; +import org.mybatis.dynamic.sql.select.join.OnJoinCriterion; import org.mybatis.dynamic.sql.update.UpdateDSL; import org.mybatis.dynamic.sql.update.UpdateModel; import org.mybatis.dynamic.sql.util.Buildable; @@ -191,10 +192,17 @@ static SqlCriterion and(BindableColumn column, VisitableCondition c } // join support - static JoinCriterion and(BasicColumn joinColumn, JoinCondition joinCondition) { - return JoinCriterion.withJoinColumn(joinColumn) + static AndJoinCriterion and(BasicColumn joinColumn, JoinCondition joinCondition) { + return new AndJoinCriterion.Builder() + .withJoinColumn(joinColumn) + .withJoinCondition(joinCondition) + .build(); + } + + static OnJoinCriterion on(BasicColumn joinColumn, JoinCondition joinCondition) { + return new OnJoinCriterion.Builder() + .withJoinColumn(joinColumn) .withJoinCondition(joinCondition) - .withConnector("and") //$NON-NLS-1$ .build(); } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/CompletableQuery.java b/src/main/java/org/mybatis/dynamic/sql/select/CompletableQuery.java deleted file mode 100644 index c0c80eed9..000000000 --- a/src/main/java/org/mybatis/dynamic/sql/select/CompletableQuery.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright 2016-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.mybatis.dynamic.sql.select; - -import org.mybatis.dynamic.sql.BasicColumn; -import org.mybatis.dynamic.sql.BindableColumn; -import org.mybatis.dynamic.sql.SortSpecification; -import org.mybatis.dynamic.sql.SqlCriterion; -import org.mybatis.dynamic.sql.VisitableCondition; -import org.mybatis.dynamic.sql.util.Buildable; - -/** - * This interface describes operations allowed for a select statement after the from and join clauses. This is - * primarily to support {@link SelectDSLCompleter}. - * - * @author Jeff Butler - * - * @param the model type created by these operations - * - */ -public interface CompletableQuery extends Buildable { - QueryExpressionDSL.QueryExpressionWhereBuilder where(); - - QueryExpressionDSL.QueryExpressionWhereBuilder where(BindableColumn column, - VisitableCondition condition); - - QueryExpressionDSL.QueryExpressionWhereBuilder where(BindableColumn column, - VisitableCondition condition, SqlCriterion... subCriteria); - - QueryExpressionDSL.GroupByFinisher groupBy(BasicColumn...columns); - - SelectDSL orderBy(SortSpecification...columns); - - SelectDSL.LimitFinisher limit(long limit); - - SelectDSL.OffsetFirstFinisher offset(long offset); - - SelectDSL.FetchFirstFinisher fetchFirst(long fetchFirstRows); -} diff --git a/src/main/java/org/mybatis/dynamic/sql/select/QueryExpressionDSL.java b/src/main/java/org/mybatis/dynamic/sql/select/QueryExpressionDSL.java index c6557c58c..e404144d6 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/QueryExpressionDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/QueryExpressionDSL.java @@ -29,15 +29,17 @@ import org.mybatis.dynamic.sql.SqlCriterion; import org.mybatis.dynamic.sql.SqlTable; import org.mybatis.dynamic.sql.VisitableCondition; +import org.mybatis.dynamic.sql.select.join.AndJoinCriterion; import org.mybatis.dynamic.sql.select.join.JoinCondition; import org.mybatis.dynamic.sql.select.join.JoinCriterion; import org.mybatis.dynamic.sql.select.join.JoinModel; import org.mybatis.dynamic.sql.select.join.JoinSpecification; import org.mybatis.dynamic.sql.select.join.JoinType; +import org.mybatis.dynamic.sql.select.join.OnJoinCriterion; import org.mybatis.dynamic.sql.util.Buildable; import org.mybatis.dynamic.sql.where.AbstractWhereDSL; -public class QueryExpressionDSL implements CompletableQuery { +public class QueryExpressionDSL implements Buildable { private String connector; private SelectDSL selectDSL; @@ -62,19 +64,16 @@ public class QueryExpressionDSL implements CompletableQuery { tableAliases.put(table, tableAlias); } - @Override public QueryExpressionWhereBuilder where() { whereBuilder = new QueryExpressionWhereBuilder(); return whereBuilder; } - @Override public QueryExpressionWhereBuilder where(BindableColumn column, VisitableCondition condition) { whereBuilder = new QueryExpressionWhereBuilder(column, condition); return whereBuilder; } - @Override public QueryExpressionWhereBuilder where(BindableColumn column, VisitableCondition condition, SqlCriterion...subCriteria) { whereBuilder = new QueryExpressionWhereBuilder(column, condition, subCriteria); @@ -95,6 +94,18 @@ public JoinSpecificationStarter join(SqlTable joinTable, String tableAlias) { return join(joinTable); } + public QueryExpressionDSL join(SqlTable joinTable, OnJoinCriterion joinCriterion, + AndJoinCriterion...joinCriteria) { + addJoinSpecificationBuilder(joinTable, joinCriterion, JoinType.INNER, joinCriteria); + return this; + } + + public QueryExpressionDSL join(SqlTable joinTable, String tableAlias, OnJoinCriterion joinCriterion, + AndJoinCriterion...joinCriteria) { + tableAliases.put(joinTable, tableAlias); + return join(joinTable, joinCriterion, joinCriteria); + } + public JoinSpecificationStarter leftJoin(SqlTable joinTable) { return new JoinSpecificationStarter(joinTable, JoinType.LEFT); } @@ -104,6 +115,18 @@ public JoinSpecificationStarter leftJoin(SqlTable joinTable, String tableAlias) return leftJoin(joinTable); } + public QueryExpressionDSL leftJoin(SqlTable joinTable, OnJoinCriterion joinCriterion, + AndJoinCriterion...joinCriteria) { + addJoinSpecificationBuilder(joinTable, joinCriterion, JoinType.LEFT, joinCriteria); + return this; + } + + public QueryExpressionDSL leftJoin(SqlTable joinTable, String tableAlias, OnJoinCriterion joinCriterion, + AndJoinCriterion...joinCriteria) { + tableAliases.put(joinTable, tableAlias); + return leftJoin(joinTable, joinCriterion, joinCriteria); + } + public JoinSpecificationStarter rightJoin(SqlTable joinTable) { return new JoinSpecificationStarter(joinTable, JoinType.RIGHT); } @@ -113,6 +136,18 @@ public JoinSpecificationStarter rightJoin(SqlTable joinTable, String tableAlias) return rightJoin(joinTable); } + public QueryExpressionDSL rightJoin(SqlTable joinTable, OnJoinCriterion joinCriterion, + AndJoinCriterion...joinCriteria) { + addJoinSpecificationBuilder(joinTable, joinCriterion, JoinType.RIGHT, joinCriteria); + return this; + } + + public QueryExpressionDSL rightJoin(SqlTable joinTable, String tableAlias, OnJoinCriterion joinCriterion, + AndJoinCriterion...joinCriteria) { + tableAliases.put(joinTable, tableAlias); + return rightJoin(joinTable, joinCriterion, joinCriteria); + } + public JoinSpecificationStarter fullJoin(SqlTable joinTable) { return new JoinSpecificationStarter(joinTable, JoinType.FULL); } @@ -122,13 +157,32 @@ public JoinSpecificationStarter fullJoin(SqlTable joinTable, String tableAlias) return fullJoin(joinTable); } - @Override + public QueryExpressionDSL fullJoin(SqlTable joinTable, OnJoinCriterion joinCriterion, + AndJoinCriterion...joinCriteria) { + addJoinSpecificationBuilder(joinTable, joinCriterion, JoinType.FULL, joinCriteria); + return this; + } + + public QueryExpressionDSL fullJoin(SqlTable joinTable, String tableAlias, OnJoinCriterion joinCriterion, + AndJoinCriterion...joinCriteria) { + tableAliases.put(joinTable, tableAlias); + return fullJoin(joinTable, joinCriterion, joinCriteria); + } + + private void addJoinSpecificationBuilder(SqlTable joinTable, OnJoinCriterion joinCriterion, JoinType joinType, + AndJoinCriterion...joinCriteria) { + joinSpecificationBuilders.add(new JoinSpecification.Builder() + .withJoinTable(joinTable) + .withJoinType(joinType) + .withJoinCriterion(joinCriterion) + .withJoinCriteria(Arrays.asList(joinCriteria))); + } + public GroupByFinisher groupBy(BasicColumn...columns) { groupByModel = GroupByModel.of(columns); return new GroupByFinisher(); } - @Override public SelectDSL orderBy(SortSpecification...columns) { selectDSL.orderBy(columns); return selectDSL; @@ -160,17 +214,14 @@ private JoinModel buildJoinModel() { .collect(Collectors.toList())); } - @Override public SelectDSL.LimitFinisher limit(long limit) { return selectDSL.limit(limit); } - @Override public SelectDSL.OffsetFirstFinisher offset(long offset) { return selectDSL.offset(offset); } - @Override public SelectDSL.FetchFirstFinisher fetchFirst(long fetchFirstRows) { return selectDSL.fetchFirst(fetchFirstRows); } @@ -298,19 +349,19 @@ public JoinSpecificationFinisher on(BasicColumn joinColumn, JoinCondition joinCo } public JoinSpecificationFinisher on(BasicColumn joinColumn, JoinCondition joinCondition, - JoinCriterion...joinCriteria) { + AndJoinCriterion...joinCriteria) { return new JoinSpecificationFinisher(joinTable, joinColumn, joinCondition, joinType, joinCriteria); } } - public class JoinSpecificationFinisher implements CompletableQuery { + public class JoinSpecificationFinisher implements Buildable { private JoinSpecification.Builder joinSpecificationBuilder; public JoinSpecificationFinisher(SqlTable table, BasicColumn joinColumn, JoinCondition joinCondition, JoinType joinType) { - JoinCriterion joinCriterion = JoinCriterion.withJoinColumn(joinColumn) + JoinCriterion joinCriterion = new OnJoinCriterion.Builder() + .withJoinColumn(joinColumn) .withJoinCondition(joinCondition) - .withConnector("on") //$NON-NLS-1$ .build(); joinSpecificationBuilder = JoinSpecification.withJoinTable(table) @@ -321,10 +372,10 @@ public JoinSpecificationFinisher(SqlTable table, BasicColumn joinColumn, } public JoinSpecificationFinisher(SqlTable table, BasicColumn joinColumn, - JoinCondition joinCondition, JoinType joinType, JoinCriterion...joinCriteria) { - JoinCriterion joinCriterion = JoinCriterion.withJoinColumn(joinColumn) + JoinCondition joinCondition, JoinType joinType, AndJoinCriterion...joinCriteria) { + JoinCriterion joinCriterion = new OnJoinCriterion.Builder() + .withJoinColumn(joinColumn) .withJoinCondition(joinCondition) - .withConnector("on") //$NON-NLS-1$ .build(); joinSpecificationBuilder = JoinSpecification.withJoinTable(table) @@ -340,26 +391,23 @@ public R build() { return QueryExpressionDSL.this.build(); } - @Override public QueryExpressionWhereBuilder where() { return QueryExpressionDSL.this.where(); } - @Override public QueryExpressionWhereBuilder where(BindableColumn column, VisitableCondition condition) { return QueryExpressionDSL.this.where(column, condition); } - @Override public QueryExpressionWhereBuilder where(BindableColumn column, VisitableCondition condition, SqlCriterion...subCriteria) { return QueryExpressionDSL.this.where(column, condition, subCriteria); } public JoinSpecificationFinisher and(BasicColumn joinColumn, JoinCondition joinCondition) { - JoinCriterion joinCriterion = JoinCriterion.withJoinColumn(joinColumn) + JoinCriterion joinCriterion = new AndJoinCriterion.Builder() + .withJoinColumn(joinColumn) .withJoinCondition(joinCondition) - .withConnector("and") //$NON-NLS-1$ .build(); joinSpecificationBuilder.withJoinCriterion(joinCriterion); return this; @@ -397,7 +445,6 @@ public JoinSpecificationStarter fullJoin(SqlTable joinTable, String tableAlias) return QueryExpressionDSL.this.fullJoin(joinTable, tableAlias); } - @Override public GroupByFinisher groupBy(BasicColumn...columns) { return QueryExpressionDSL.this.groupBy(columns); } @@ -410,22 +457,18 @@ public UnionBuilder unionAll() { return QueryExpressionDSL.this.unionAll(); } - @Override public SelectDSL orderBy(SortSpecification...columns) { return QueryExpressionDSL.this.orderBy(columns); } - @Override public SelectDSL.LimitFinisher limit(long limit) { return QueryExpressionDSL.this.limit(limit); } - @Override public SelectDSL.OffsetFirstFinisher offset(long offset) { return QueryExpressionDSL.this.offset(offset); } - @Override public SelectDSL.FetchFirstFinisher fetchFirst(long fetchFirstRows) { return QueryExpressionDSL.this.fetchFirst(fetchFirstRows); } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/SelectDSLCompleter.java b/src/main/java/org/mybatis/dynamic/sql/select/SelectDSLCompleter.java index 14c57efbe..427ebb351 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/SelectDSLCompleter.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/SelectDSLCompleter.java @@ -63,7 +63,7 @@ */ @FunctionalInterface public interface SelectDSLCompleter extends - Function, Buildable> { + Function, Buildable> { /** * Returns a completer that can be used to select every row in a table. diff --git a/src/main/java/org/mybatis/dynamic/sql/select/join/AndJoinCriterion.java b/src/main/java/org/mybatis/dynamic/sql/select/join/AndJoinCriterion.java new file mode 100644 index 000000000..a6733808c --- /dev/null +++ b/src/main/java/org/mybatis/dynamic/sql/select/join/AndJoinCriterion.java @@ -0,0 +1,39 @@ +/** + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mybatis.dynamic.sql.select.join; + +public class AndJoinCriterion extends JoinCriterion { + + private AndJoinCriterion(Builder builder) { + super(builder); + } + + @Override + public String connector() { + return "and"; //$NON-NLS-1$ + } + + public static class Builder extends AbstractBuilder { + @Override + protected Builder getThis() { + return this; + } + + public AndJoinCriterion build() { + return new AndJoinCriterion(this); + } + } +} diff --git a/src/main/java/org/mybatis/dynamic/sql/select/join/JoinCriterion.java b/src/main/java/org/mybatis/dynamic/sql/select/join/JoinCriterion.java index 623f15568..ca19176d8 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/join/JoinCriterion.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/join/JoinCriterion.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,21 +19,17 @@ import org.mybatis.dynamic.sql.BasicColumn; -public class JoinCriterion { +public abstract class JoinCriterion { - private String connector; private BasicColumn leftColumn; private JoinCondition joinCondition; - private JoinCriterion(Builder builder) { - connector = Objects.requireNonNull(builder.connector); + protected JoinCriterion(AbstractBuilder builder) { leftColumn = Objects.requireNonNull(builder.joinColumn); joinCondition = Objects.requireNonNull(builder.joinCondition); } - public String connector() { - return connector; - } + public abstract String connector(); public BasicColumn leftColumn() { return leftColumn; @@ -47,32 +43,20 @@ public String operator() { return joinCondition.operator(); } - public static Builder withJoinColumn(BasicColumn joinColumn) { - return new Builder().withJoinColumn(joinColumn); - } - - public static class Builder { + public abstract static class AbstractBuilder> { private BasicColumn joinColumn; private JoinCondition joinCondition; - private String connector; - public Builder withJoinColumn(BasicColumn joinColumn) { + public T withJoinColumn(BasicColumn joinColumn) { this.joinColumn = joinColumn; - return this; + return getThis(); } - public Builder withJoinCondition(JoinCondition joinCondition) { + public T withJoinCondition(JoinCondition joinCondition) { this.joinCondition = joinCondition; - return this; + return getThis(); } - public Builder withConnector(String connector) { - this.connector = connector; - return this; - } - - public JoinCriterion build() { - return new JoinCriterion(this); - } + protected abstract T getThis(); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/join/OnJoinCriterion.java b/src/main/java/org/mybatis/dynamic/sql/select/join/OnJoinCriterion.java new file mode 100644 index 000000000..c8238b948 --- /dev/null +++ b/src/main/java/org/mybatis/dynamic/sql/select/join/OnJoinCriterion.java @@ -0,0 +1,39 @@ +/** + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mybatis.dynamic.sql.select.join; + +public class OnJoinCriterion extends JoinCriterion { + + private OnJoinCriterion(Builder builder) { + super(builder); + } + + @Override + public String connector() { + return "on"; //$NON-NLS-1$ + } + + public static class Builder extends AbstractBuilder { + @Override + protected Builder getThis() { + return this; + } + + public OnJoinCriterion build() { + return new OnJoinCriterion(this); + } + } +} diff --git a/src/main/java/org/mybatis/dynamic/sql/util/mybatis3/MyBatis3Utils.java b/src/main/java/org/mybatis/dynamic/sql/util/mybatis3/MyBatis3Utils.java index f38140711..0c5eb9e54 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/mybatis3/MyBatis3Utils.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/mybatis3/MyBatis3Utils.java @@ -33,7 +33,6 @@ import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider; import org.mybatis.dynamic.sql.insert.render.MultiRowInsertStatementProvider; import org.mybatis.dynamic.sql.render.RenderingStrategies; -import org.mybatis.dynamic.sql.select.CompletableQuery; import org.mybatis.dynamic.sql.select.QueryExpressionDSL; import org.mybatis.dynamic.sql.select.SelectDSL; import org.mybatis.dynamic.sql.select.SelectDSLCompleter; @@ -88,7 +87,7 @@ public static List selectDistinct(Function List selectDistinct(Function> mapper, - CompletableQuery start, SelectDSLCompleter completer) { + QueryExpressionDSL start, SelectDSLCompleter completer) { return mapper.apply(completer.apply(start).build().render(RenderingStrategies.MYBATIS3)); } @@ -98,7 +97,7 @@ public static List selectList(Function> } public static List selectList(Function> mapper, - CompletableQuery start, SelectDSLCompleter completer) { + QueryExpressionDSL start, SelectDSLCompleter completer) { return mapper.apply(completer.apply(start).build().render(RenderingStrategies.MYBATIS3)); } @@ -108,7 +107,7 @@ public static R selectOne(Function mapper, } public static R selectOne(Function mapper, - CompletableQuery start, + QueryExpressionDSL start, SelectDSLCompleter completer) { return mapper.apply(completer.apply(start).build().render(RenderingStrategies.MYBATIS3)); } diff --git a/src/test/java/examples/joins/JoinMapperTest.java b/src/test/java/examples/joins/JoinMapperTest.java index 2d7f6a8ee..7038d6be2 100644 --- a/src/test/java/examples/joins/JoinMapperTest.java +++ b/src/test/java/examples/joins/JoinMapperTest.java @@ -69,7 +69,7 @@ public void setup() throws Exception { } @Test - public void testSingleTableJoin() { + public void testSingleTableJoin1() { try (SqlSession session = sqlSessionFactory.openSession()) { JoinMapper mapper = session.getMapper(JoinMapper.class); @@ -102,6 +102,40 @@ public void testSingleTableJoin() { } } + @Test + public void testSingleTableJoin2() { + try (SqlSession session = sqlSessionFactory.openSession()) { + JoinMapper mapper = session.getMapper(JoinMapper.class); + + SelectStatementProvider selectStatement = select(orderMaster.orderId, orderDate, orderDetail.lineNumber, orderDetail.description, orderDetail.quantity) + .from(orderMaster, "om") + .join(orderDetail, "od", on(orderMaster.orderId, equalTo(orderDetail.orderId))) + .build() + .render(RenderingStrategies.MYBATIS3); + + String expectedStatment = "select om.order_id, om.order_date, od.line_number, od.description, od.quantity" + + " from OrderMaster om join OrderDetail od on om.order_id = od.order_id"; + assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatment); + + List rows = mapper.selectMany(selectStatement); + + assertThat(rows.size()).isEqualTo(2); + OrderMaster orderMaster = rows.get(0); + assertThat(orderMaster.getId()).isEqualTo(1); + assertThat(orderMaster.getDetails().size()).isEqualTo(2); + OrderDetail orderDetail = orderMaster.getDetails().get(0); + assertThat(orderDetail.getLineNumber()).isEqualTo(1); + orderDetail = orderMaster.getDetails().get(1); + assertThat(orderDetail.getLineNumber()).isEqualTo(2); + + orderMaster = rows.get(1); + assertThat(orderMaster.getId()).isEqualTo(2); + assertThat(orderMaster.getDetails().size()).isEqualTo(1); + orderDetail = orderMaster.getDetails().get(0); + assertThat(orderDetail.getLineNumber()).isEqualTo(1); + } + } + @Test public void testCompoundJoin1() { // this is a nonsensical join, but it does test the "and" capability @@ -131,6 +165,62 @@ public void testCompoundJoin2() { assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatment); } + @Test + public void testCompoundJoin3() { + // this is a nonsensical join, but it does test the "and" capability + SelectStatementProvider selectStatement = select(orderMaster.orderId, orderDate, orderDetail.lineNumber, orderDetail.description, orderDetail.quantity) + .from(orderMaster, "om") + .join(orderDetail, "od", on(orderMaster.orderId, equalTo(orderDetail.orderId)), and(orderMaster.orderId, equalTo(orderDetail.orderId))) + .build() + .render(RenderingStrategies.MYBATIS3); + + String expectedStatment = "select om.order_id, om.order_date, od.line_number, od.description, od.quantity" + + " from OrderMaster om join OrderDetail od on om.order_id = od.order_id and om.order_id = od.order_id"; + assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatment); + } + + @Test + public void testCompoundJoin4() { + // this is a nonsensical join, but it does test the "and" capability + SelectStatementProvider selectStatement = select(orderMaster.orderId, orderDate, orderDetail.lineNumber, orderDetail.description, orderDetail.quantity) + .from(orderMaster, "om") + .leftJoin(orderDetail, "od", on(orderMaster.orderId, equalTo(orderDetail.orderId)), and(orderMaster.orderId, equalTo(orderDetail.orderId))) + .build() + .render(RenderingStrategies.MYBATIS3); + + String expectedStatment = "select om.order_id, om.order_date, od.line_number, od.description, od.quantity" + + " from OrderMaster om left join OrderDetail od on om.order_id = od.order_id and om.order_id = od.order_id"; + assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatment); + } + + @Test + public void testCompoundJoin5() { + // this is a nonsensical join, but it does test the "and" capability + SelectStatementProvider selectStatement = select(orderMaster.orderId, orderDate, orderDetail.lineNumber, orderDetail.description, orderDetail.quantity) + .from(orderMaster, "om") + .rightJoin(orderDetail, "od", on(orderMaster.orderId, equalTo(orderDetail.orderId)), and(orderMaster.orderId, equalTo(orderDetail.orderId))) + .build() + .render(RenderingStrategies.MYBATIS3); + + String expectedStatment = "select om.order_id, om.order_date, od.line_number, od.description, od.quantity" + + " from OrderMaster om right join OrderDetail od on om.order_id = od.order_id and om.order_id = od.order_id"; + assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatment); + } + + @Test + public void testCompoundJoin6() { + // this is a nonsensical join, but it does test the "and" capability + SelectStatementProvider selectStatement = select(orderMaster.orderId, orderDate, orderDetail.lineNumber, orderDetail.description, orderDetail.quantity) + .from(orderMaster, "om") + .fullJoin(orderDetail, "od", on(orderMaster.orderId, equalTo(orderDetail.orderId)), and(orderMaster.orderId, equalTo(orderDetail.orderId))) + .build() + .render(RenderingStrategies.MYBATIS3); + + String expectedStatment = "select om.order_id, om.order_date, od.line_number, od.description, od.quantity" + + " from OrderMaster om full join OrderDetail od on om.order_id = od.order_id and om.order_id = od.order_id"; + assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatment); + } + @Test public void testMultipleTableJoinWithWhereClause() { try (SqlSession session = sqlSessionFactory.openSession()) { @@ -331,6 +421,42 @@ public void testRightJoin2() { } } + @Test + public void testRightJoin3() { + try (SqlSession session = sqlSessionFactory.openSession()) { + JoinMapper mapper = session.getMapper(JoinMapper.class); + + SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description) + .from(orderMaster, "om") + .join(orderLine, "ol", on(orderMaster.orderId, equalTo(orderLine.orderId))) + .rightJoin(itemMaster, "im", on(orderLine.itemId, equalTo(itemMaster.itemId))) + .orderBy(orderLine.orderId, itemMaster.itemId) + .build() + .render(RenderingStrategies.MYBATIS3); + + String expectedStatment = "select ol.order_id, ol.quantity, im.item_id, im.description" + + " from OrderMaster om join OrderLine ol on om.order_id = ol.order_id" + + " right join ItemMaster im on ol.item_id = im.item_id" + + " order by order_id, item_id"; + assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatment); + + List> rows = mapper.generalSelect(selectStatement); + + assertThat(rows.size()).isEqualTo(5); + Map row = rows.get(0); + assertThat(row.get("ORDER_ID")).isNull(); + assertThat(row.get("QUANTITY")).isNull(); + assertThat(row.get("DESCRIPTION")).isEqualTo("Catcher Glove"); + assertThat(row.get("ITEM_ID")).isEqualTo(55); + + row = rows.get(4); + assertThat(row.get("ORDER_ID")).isEqualTo(2); + assertThat(row.get("QUANTITY")).isEqualTo(1); + assertThat(row.get("DESCRIPTION")).isEqualTo("Outfield Glove"); + assertThat(row.get("ITEM_ID")).isEqualTo(44); + } + } + @Test public void testRightJoinNoAliases() { try (SqlSession session = sqlSessionFactory.openSession()) { @@ -437,6 +563,42 @@ public void testLeftJoin2() { } } + @Test + public void testLeftJoin3() { + try (SqlSession session = sqlSessionFactory.openSession()) { + JoinMapper mapper = session.getMapper(JoinMapper.class); + + SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description) + .from(orderMaster, "om") + .join(orderLine, "ol", on(orderMaster.orderId, equalTo(orderLine.orderId))) + .leftJoin(itemMaster, "im", on(orderLine.itemId, equalTo(itemMaster.itemId))) + .orderBy(orderLine.orderId, itemMaster.itemId) + .build() + .render(RenderingStrategies.MYBATIS3); + + String expectedStatment = "select ol.order_id, ol.quantity, im.item_id, im.description" + + " from OrderMaster om join OrderLine ol on om.order_id = ol.order_id" + + " left join ItemMaster im on ol.item_id = im.item_id" + + " order by order_id, item_id"; + assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatment); + + List> rows = mapper.generalSelect(selectStatement); + + assertThat(rows.size()).isEqualTo(5); + Map row = rows.get(2); + assertThat(row.get("ORDER_ID")).isEqualTo(2); + assertThat(row.get("QUANTITY")).isEqualTo(6); + assertThat(row.get("DESCRIPTION")).isNull(); + assertThat(row.get("ITEM_ID")).isNull(); + + row = rows.get(4); + assertThat(row.get("ORDER_ID")).isEqualTo(2); + assertThat(row.get("QUANTITY")).isEqualTo(1); + assertThat(row.get("DESCRIPTION")).isEqualTo("Outfield Glove"); + assertThat(row.get("ITEM_ID")).isEqualTo(44); + } + } + @Test public void testLeftJoinNoAliases() { try (SqlSession session = sqlSessionFactory.openSession()) { @@ -556,6 +718,48 @@ public void testFullJoin2() { } } + @Test + public void testFullJoin3() { + try (SqlSession session = sqlSessionFactory.openSession()) { + JoinMapper mapper = session.getMapper(JoinMapper.class); + + SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description) + .from(orderMaster, "om") + .join(orderLine, "ol", on(orderMaster.orderId, equalTo(orderLine.orderId))) + .fullJoin(itemMaster, "im", on(orderLine.itemId, equalTo(itemMaster.itemId))) + .orderBy(orderLine.orderId, itemMaster.itemId) + .build() + .render(RenderingStrategies.MYBATIS3); + + String expectedStatment = "select ol.order_id, ol.quantity, im.item_id, im.description" + + " from OrderMaster om join OrderLine ol on om.order_id = ol.order_id" + + " full join ItemMaster im on ol.item_id = im.item_id" + + " order by order_id, item_id"; + assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatment); + + List> rows = mapper.generalSelect(selectStatement); + + assertThat(rows.size()).isEqualTo(6); + Map row = rows.get(0); + assertThat(row.get("ORDER_ID")).isNull(); + assertThat(row.get("QUANTITY")).isNull(); + assertThat(row.get("DESCRIPTION")).isEqualTo("Catcher Glove"); + assertThat(row.get("ITEM_ID")).isEqualTo(55); + + row = rows.get(3); + assertThat(row.get("ORDER_ID")).isEqualTo(2); + assertThat(row.get("QUANTITY")).isEqualTo(6); + assertThat(row.get("DESCRIPTION")).isNull(); + assertThat(row.get("ITEM_ID")).isNull(); + + row = rows.get(5); + assertThat(row.get("ORDER_ID")).isEqualTo(2); + assertThat(row.get("QUANTITY")).isEqualTo(1); + assertThat(row.get("DESCRIPTION")).isEqualTo("Outfield Glove"); + assertThat(row.get("ITEM_ID")).isEqualTo(44); + } + } + @Test public void testFullJoinNoAliases() { try (SqlSession session = sqlSessionFactory.openSession()) { diff --git a/src/test/java/examples/simple/PersonWithAddressMapper.java b/src/test/java/examples/simple/PersonWithAddressMapper.java index 7c9b21444..5f9b59fd3 100644 --- a/src/test/java/examples/simple/PersonWithAddressMapper.java +++ b/src/test/java/examples/simple/PersonWithAddressMapper.java @@ -36,7 +36,7 @@ import org.apache.ibatis.type.JdbcType; import org.mybatis.dynamic.sql.BasicColumn; import org.mybatis.dynamic.sql.SqlBuilder; -import org.mybatis.dynamic.sql.select.CompletableQuery; +import org.mybatis.dynamic.sql.select.QueryExpressionDSL; import org.mybatis.dynamic.sql.select.SelectDSLCompleter; import org.mybatis.dynamic.sql.select.SelectModel; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; @@ -75,14 +75,14 @@ public interface PersonWithAddressMapper { address.streetAddress, address.city, address.state); default Optional selectOne(SelectDSLCompleter completer) { - CompletableQuery start = SqlBuilder.select(selectList).from(person) - .fullJoin(address).on(person.addressId, equalTo(address.id)); + QueryExpressionDSL start = SqlBuilder.select(selectList).from(person) + .join(address, on(person.addressId, equalTo(address.id))); return MyBatis3Utils.selectOne(this::selectOne, start, completer); } default List select(SelectDSLCompleter completer) { - CompletableQuery start = SqlBuilder.select(selectList).from(person) - .fullJoin(address).on(person.addressId, equalTo(address.id)); + QueryExpressionDSL start = SqlBuilder.select(selectList).from(person) + .join(address, on(person.addressId, equalTo(address.id))); return MyBatis3Utils.selectList(this::selectMany, start, completer); }