Skip to content

Commit 729e831

Browse files
committed
Add basic support for sub-queries in where clauses
1 parent ab1cfe6 commit 729e831

File tree

12 files changed

+307
-16
lines changed

12 files changed

+307
-16
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Copyright 2016-2017 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+
* http://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 org.mybatis.dynamic.sql;
17+
18+
import org.mybatis.dynamic.sql.select.SelectModel;
19+
import org.mybatis.dynamic.sql.select.SelectModelBuilder;
20+
21+
public abstract class AbstractSubselectCondition<T> extends Condition<T> {
22+
private SelectModel selectModel;
23+
24+
protected AbstractSubselectCondition (SelectModelBuilder.Buildable selectModelBuilder) {
25+
this.selectModel = selectModelBuilder.build();
26+
}
27+
28+
public SelectModel selectModel() {
29+
return selectModel;
30+
}
31+
32+
@Override
33+
public <R> R accept(ConditionVisitor<T, R> visitor) {
34+
return visitor.visit(this);
35+
}
36+
37+
public abstract String renderCondition(String columnName, String renderedSelectStatement);
38+
}

src/main/java/org/mybatis/dynamic/sql/ConditionVisitor.java

+2
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,6 @@ public interface ConditionVisitor<T, R> {
2323
R visit(AbstractSingleValueCondition<T> condition);
2424

2525
R visit(AbstractTwoValueCondition<T> condition);
26+
27+
R visit(AbstractSubselectCondition<T> condition);
2628
}

src/main/java/org/mybatis/dynamic/sql/SqlConditions.java

+11
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@
1818
import java.util.Arrays;
1919
import java.util.stream.Stream;
2020

21+
import org.mybatis.dynamic.sql.select.SelectModelBuilder;
2122
import org.mybatis.dynamic.sql.select.aggregate.Count;
2223
import org.mybatis.dynamic.sql.where.condition.IsBetween;
2324
import org.mybatis.dynamic.sql.where.condition.IsEqualTo;
2425
import org.mybatis.dynamic.sql.where.condition.IsGreaterThan;
2526
import org.mybatis.dynamic.sql.where.condition.IsGreaterThanOrEqualTo;
2627
import org.mybatis.dynamic.sql.where.condition.IsIn;
2728
import org.mybatis.dynamic.sql.where.condition.IsInCaseInsensitive;
29+
import org.mybatis.dynamic.sql.where.condition.IsInWithSubselect;
2830
import org.mybatis.dynamic.sql.where.condition.IsLessThan;
2931
import org.mybatis.dynamic.sql.where.condition.IsLessThanOrEqualTo;
3032
import org.mybatis.dynamic.sql.where.condition.IsLike;
@@ -33,6 +35,7 @@
3335
import org.mybatis.dynamic.sql.where.condition.IsNotEqualTo;
3436
import org.mybatis.dynamic.sql.where.condition.IsNotIn;
3537
import org.mybatis.dynamic.sql.where.condition.IsNotInCaseInsensitive;
38+
import org.mybatis.dynamic.sql.where.condition.IsNotInWithSubselect;
3639
import org.mybatis.dynamic.sql.where.condition.IsNotLike;
3740
import org.mybatis.dynamic.sql.where.condition.IsNotLikeCaseInsensitive;
3841
import org.mybatis.dynamic.sql.where.condition.IsNotNull;
@@ -120,6 +123,10 @@ static <T> IsIn<T> isIn(T...values) {
120123
static <T> IsIn<T> isIn(Stream<T> values) {
121124
return IsIn.of(values);
122125
}
126+
127+
static <T> IsInWithSubselect<T> isIn(SelectModelBuilder.Buildable selectModelBuilder) {
128+
return IsInWithSubselect.of(selectModelBuilder);
129+
}
123130

124131
@SafeVarargs
125132
static <T> IsNotIn<T> isNotIn(T...values) {
@@ -130,6 +137,10 @@ static <T> IsNotIn<T> isNotIn(Stream<T> values) {
130137
return IsNotIn.of(values);
131138
}
132139

140+
static <T> IsNotInWithSubselect<T> isNotIn(SelectModelBuilder.Buildable selectModelBuilder) {
141+
return IsNotInWithSubselect.of(selectModelBuilder);
142+
}
143+
133144
static <T> IsBetween.Builder<T> isBetween(T value1) {
134145
return IsBetween.isBetween(value1);
135146
}

src/main/java/org/mybatis/dynamic/sql/delete/render/DeleteRenderer.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ public DeleteSupport render(RenderingStrategy renderingStrategy) {
3636
}
3737

3838
private DeleteSupport renderWithWhereClause(WhereModel whereModel, RenderingStrategy renderingStrategy) {
39-
WhereRenderer whereRenderer = WhereRenderer.of(whereModel, renderingStrategy, Collections.emptyMap());
40-
WhereSupport whereSupport = whereRenderer.render();
39+
WhereSupport whereSupport = new WhereRenderer.Builder(whereModel, renderingStrategy, Collections.emptyMap())
40+
.build()
41+
.render();
4142
return DeleteSupport.of(deleteModel.table().name(), whereSupport.getWhereClause(), whereSupport.getParameters());
4243
}
4344

src/main/java/org/mybatis/dynamic/sql/select/SelectModelBuilder.java

+12-3
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,12 @@ protected SelectModel buildModel() {
6969
.build();
7070
}
7171

72-
public class SelectSupportAfterFromBuilder {
72+
@FunctionalInterface
73+
public interface Buildable {
74+
SelectModel build();
75+
}
76+
77+
public class SelectSupportAfterFromBuilder implements Buildable {
7378
private SelectSupportAfterFromBuilder() {
7479
super();
7580
}
@@ -88,12 +93,14 @@ public SelectSupportAfterOrderByBuilder orderBy(SqlColumn<?>...columns) {
8893
return new SelectSupportAfterOrderByBuilder();
8994
}
9095

96+
@Override
9197
public SelectModel build() {
9298
return buildModel();
9399
}
94100
}
95101

96-
public class SelectSupportWhereBuilder extends AbstractWhereModelBuilder<SelectSupportWhereBuilder> {
102+
public class SelectSupportWhereBuilder extends AbstractWhereModelBuilder<SelectSupportWhereBuilder>
103+
implements Buildable {
97104
private <T> SelectSupportWhereBuilder(SqlColumn<T> column, Condition<T> condition) {
98105
super(column, condition);
99106
}
@@ -109,6 +116,7 @@ public SelectSupportAfterOrderByBuilder orderBy(SqlColumn<?>...columns) {
109116
return new SelectSupportAfterOrderByBuilder();
110117
}
111118

119+
@Override
112120
public SelectModel build() {
113121
whereModel = buildWhereModel();
114122
return buildModel();
@@ -120,11 +128,12 @@ protected SelectSupportWhereBuilder getThis() {
120128
}
121129
}
122130

123-
public class SelectSupportAfterOrderByBuilder {
131+
public class SelectSupportAfterOrderByBuilder implements Buildable {
124132
private SelectSupportAfterOrderByBuilder() {
125133
super();
126134
}
127135

136+
@Override
128137
public SelectModel build() {
129138
return buildModel();
130139
}

src/main/java/org/mybatis/dynamic/sql/select/render/SelectRenderer.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.mybatis.dynamic.sql.select.render;
1717

1818
import java.util.Optional;
19+
import java.util.concurrent.atomic.AtomicInteger;
1920
import java.util.stream.Collectors;
2021
import java.util.stream.Stream;
2122

@@ -35,13 +36,21 @@ private SelectRenderer(SelectModel selectModel) {
3536
}
3637

3738
public SelectSupport render(RenderingStrategy renderingStrategy) {
39+
return render(renderingStrategy, null);
40+
}
41+
42+
public SelectSupport render(RenderingStrategy renderingStrategy, AtomicInteger sequence) {
3843
SelectSupport.Builder builder = new SelectSupport.Builder(calculateTableName(selectModel.table()))
3944
.isDistinct(selectModel.isDistinct())
4045
.withColumnList(calculateColumnList())
4146
.withOrderByClause(calculateOrderByPhrase());
4247

4348
selectModel.whereModel().ifPresent(wm -> {
44-
WhereSupport whereSupport = WhereRenderer.of(wm, renderingStrategy, selectModel.tableAliases()).render();
49+
WhereSupport whereSupport = new WhereRenderer.Builder(wm, renderingStrategy, selectModel.tableAliases())
50+
.withSequence(sequence)
51+
.build()
52+
.render();
53+
4554
builder.withWhereClause(whereSupport.getWhereClause())
4655
.withParameters(whereSupport.getParameters());
4756
});

src/main/java/org/mybatis/dynamic/sql/update/render/UpdateRenderer.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.mybatis.dynamic.sql.update.UpdateModel;
2323
import org.mybatis.dynamic.sql.util.FragmentAndParameters;
2424
import org.mybatis.dynamic.sql.util.UpdateMapping;
25+
import org.mybatis.dynamic.sql.where.WhereModel;
2526
import org.mybatis.dynamic.sql.where.render.WhereRenderer;
2627
import org.mybatis.dynamic.sql.where.render.WhereSupport;
2728

@@ -42,8 +43,15 @@ public UpdateSupport render(RenderingStrategy renderingStrategy) {
4243
}
4344

4445
private Optional<WhereSupport> renderWhere(RenderingStrategy renderingStrategy) {
45-
return updateModel.whereModel().flatMap(
46-
wm -> Optional.of(WhereRenderer.of(wm, renderingStrategy, Collections.emptyMap()).render()));
46+
return updateModel.whereModel()
47+
.flatMap(wm -> renderWhere(wm, renderingStrategy));
48+
}
49+
50+
private Optional<WhereSupport> renderWhere(WhereModel whereModel, RenderingStrategy renderingStrategy) {
51+
WhereSupport whereSupport = new WhereRenderer.Builder(whereModel, renderingStrategy, Collections.emptyMap())
52+
.build()
53+
.render();
54+
return Optional.of(whereSupport);
4755
}
4856

4957
private FragmentAndParameters transform(UpdateMapping columnAndValue, SetPhraseVisitor visitor) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* Copyright 2016-2017 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+
* http://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 org.mybatis.dynamic.sql.where.condition;
17+
18+
import org.mybatis.dynamic.sql.AbstractSubselectCondition;
19+
import org.mybatis.dynamic.sql.select.SelectModelBuilder;
20+
21+
public class IsInWithSubselect<T> extends AbstractSubselectCondition<T> {
22+
23+
protected IsInWithSubselect(SelectModelBuilder.Buildable selectModelBuilder) {
24+
super(selectModelBuilder);
25+
}
26+
27+
public static <T> IsInWithSubselect<T> of(SelectModelBuilder.Buildable selectModelBuilder) {
28+
return new IsInWithSubselect<>(selectModelBuilder);
29+
}
30+
31+
@Override
32+
public String renderCondition(String columnName, String renderedSelectStatement) {
33+
return columnName + " in (" + renderedSelectStatement + ")"; //$NON-NLS-1$ //$NON-NLS-2$
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* Copyright 2016-2017 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+
* http://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 org.mybatis.dynamic.sql.where.condition;
17+
18+
import org.mybatis.dynamic.sql.AbstractSubselectCondition;
19+
import org.mybatis.dynamic.sql.select.SelectModelBuilder;
20+
21+
public class IsNotInWithSubselect<T> extends AbstractSubselectCondition<T> {
22+
23+
protected IsNotInWithSubselect(SelectModelBuilder.Buildable selectModelBuilder) {
24+
super(selectModelBuilder);
25+
}
26+
27+
public static <T> IsNotInWithSubselect<T> of(SelectModelBuilder.Buildable selectModelBuilder) {
28+
return new IsNotInWithSubselect<>(selectModelBuilder);
29+
}
30+
31+
@Override
32+
public String renderCondition(String columnName, String renderedSelectStatement) {
33+
return columnName + " not in (" + renderedSelectStatement + ")"; //$NON-NLS-1$ //$NON-NLS-2$
34+
}
35+
}

src/main/java/org/mybatis/dynamic/sql/where/render/WhereConditionVisitor.java

+13
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@
2222
import org.mybatis.dynamic.sql.AbstractListValueCondition;
2323
import org.mybatis.dynamic.sql.AbstractNoValueCondition;
2424
import org.mybatis.dynamic.sql.AbstractSingleValueCondition;
25+
import org.mybatis.dynamic.sql.AbstractSubselectCondition;
2526
import org.mybatis.dynamic.sql.AbstractTwoValueCondition;
2627
import org.mybatis.dynamic.sql.ConditionVisitor;
2728
import org.mybatis.dynamic.sql.SqlColumn;
2829
import org.mybatis.dynamic.sql.SqlTable;
2930
import org.mybatis.dynamic.sql.render.RenderingStrategy;
31+
import org.mybatis.dynamic.sql.select.render.SelectRenderer;
32+
import org.mybatis.dynamic.sql.select.render.SelectSupport;
3033
import org.mybatis.dynamic.sql.util.FragmentAndParameters;
3134
import org.mybatis.dynamic.sql.where.render.WhereFragmentCollector.Triple;
3235

@@ -87,6 +90,16 @@ public FragmentAndParameters visit(AbstractTwoValueCondition<T> condition) {
8790
.build();
8891
}
8992

93+
94+
@Override
95+
public FragmentAndParameters visit(AbstractSubselectCondition<T> condition) {
96+
SelectSupport ss = SelectRenderer.of(condition.selectModel()).render(renderingStrategy, sequence);
97+
98+
return new FragmentAndParameters.Builder(condition.renderCondition(columnName(), ss.getFullSelectStatement()))
99+
.withParameters(ss.getParameters())
100+
.build();
101+
}
102+
90103
private Triple toTriple(Object value) {
91104
String mapKey = formatParameterMapKey(sequence.getAndIncrement());
92105
return Triple.of(mapKey, getFormattedJdbcPlaceholder(mapKey), value);

src/main/java/org/mybatis/dynamic/sql/where/render/WhereRenderer.java

+32-8
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.mybatis.dynamic.sql.where.render;
1717

1818
import java.util.Map;
19+
import java.util.Optional;
1920
import java.util.concurrent.atomic.AtomicInteger;
2021

2122
import org.mybatis.dynamic.sql.SqlCriterion;
@@ -26,14 +27,15 @@
2627

2728
public class WhereRenderer {
2829
private WhereModel model;
29-
private AtomicInteger sequence = new AtomicInteger(1);
30+
private AtomicInteger sequence;
3031
private RenderingStrategy renderingStrategy;
3132
private Map<SqlTable, String> tableAliases;
3233

33-
private WhereRenderer(WhereModel model, RenderingStrategy renderingStrategy, Map<SqlTable, String> tableAliases) {
34-
this.model = model;
35-
this.renderingStrategy = renderingStrategy;
36-
this.tableAliases = tableAliases;
34+
private WhereRenderer(Builder builder) {
35+
model = builder.model;
36+
renderingStrategy = builder.renderingStrategy;
37+
tableAliases = builder.tableAliases;
38+
sequence = builder.sequence().orElse(new AtomicInteger(1));
3739
}
3840

3941
public WhereSupport render() {
@@ -47,8 +49,30 @@ private FragmentAndParameters render(SqlCriterion<?> criterion) {
4749
.render(criterion);
4850
}
4951

50-
public static WhereRenderer of(WhereModel model, RenderingStrategy renderingStrategy,
51-
Map<SqlTable, String> tableAliases) {
52-
return new WhereRenderer(model, renderingStrategy, tableAliases);
52+
public static class Builder {
53+
private WhereModel model;
54+
private RenderingStrategy renderingStrategy;
55+
private Map<SqlTable, String> tableAliases;
56+
private AtomicInteger sequence;
57+
58+
public Builder(WhereModel model, RenderingStrategy renderingStrategy,
59+
Map<SqlTable, String> tableAliases) {
60+
this.model = model;
61+
this.renderingStrategy = renderingStrategy;
62+
this.tableAliases = tableAliases;
63+
}
64+
65+
public Builder withSequence(AtomicInteger sequence) {
66+
this.sequence = sequence;
67+
return this;
68+
}
69+
70+
private Optional<AtomicInteger> sequence() {
71+
return Optional.ofNullable(sequence);
72+
}
73+
74+
public WhereRenderer build() {
75+
return new WhereRenderer(this);
76+
}
5377
}
5478
}

0 commit comments

Comments
 (0)