Skip to content

Commit b28ca54

Browse files
authored
Merge pull request #434 from jeffgbutler/gh-430
Add support for CriteriaGroups
2 parents 9e70008 + 9fda0e4 commit b28ca54

File tree

18 files changed

+973
-357
lines changed

18 files changed

+973
-357
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
This log will detail notable changes to MyBatis Dynamic SQL. Full details are available on the GitHub milestone pages.
44

5+
## Release 1.4.0 - Unreleased
6+
7+
GitHub milestone: [https://github.com/mybatis/mybatis-dynamic-sql/issues?q=milestone%3A1.4.0+](https://github.com/mybatis/mybatis-dynamic-sql/issues?q=milestone%3A1.4.0+)
8+
9+
1. Added support for arbitrary placement of nested criteria. For example, it is now
10+
possible to write a where clause like this: `where (a < 5 and B = 3) and ((C = 4 or D = 5) and E = 6)`. Previously
11+
we did not support the grouping of criteria at the beginning of a where clause or the beginning of an and/or
12+
condition. Adding this support required significant refactoring, but that should be transparent to most users.
13+
([#434](https://github.com/mybatis/mybatis-dynamic-sql/pull/434))
14+
515
## Release 1.3.1 - December 18, 2021
616

717
This is a minor release with a few small enhancements. Most deprecated methods will be removed in the next release.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2016-2022 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 java.util.ArrayList;
19+
import java.util.Collections;
20+
import java.util.List;
21+
import java.util.Objects;
22+
23+
/**
24+
* This class represents a criteria group with either an AND or an OR connector.
25+
* This class is intentionally NOT derived from SqlCriterion because we only want it to be
26+
* available where an AND or an OR condition is appropriate.
27+
*
28+
* @author Jeff Butler
29+
* @since 1.4.0
30+
*/
31+
public class AndOrCriteriaGroup {
32+
private final String connector;
33+
private final SqlCriterion initialCriterion;
34+
private final List<AndOrCriteriaGroup> subCriteria;
35+
36+
private AndOrCriteriaGroup(Builder builder) {
37+
connector = Objects.requireNonNull(builder.connector);
38+
initialCriterion = Objects.requireNonNull(builder.initialCriterion);
39+
subCriteria = builder.subCriteria;
40+
}
41+
42+
public String connector() {
43+
return connector;
44+
}
45+
46+
public SqlCriterion initialCriterion() {
47+
return initialCriterion;
48+
}
49+
50+
public List<AndOrCriteriaGroup> subCriteria() {
51+
return Collections.unmodifiableList(subCriteria);
52+
}
53+
54+
public static class Builder {
55+
private String connector;
56+
private SqlCriterion initialCriterion;
57+
private final List<AndOrCriteriaGroup> subCriteria = new ArrayList<>();
58+
59+
public Builder withConnector(String connector) {
60+
this.connector = connector;
61+
return this;
62+
}
63+
64+
public Builder withInitialCriterion(SqlCriterion initialCriterion) {
65+
this.initialCriterion = initialCriterion;
66+
return this;
67+
}
68+
69+
public Builder withSubCriteria(List<AndOrCriteriaGroup> subCriteria) {
70+
this.subCriteria.addAll(subCriteria);
71+
return this;
72+
}
73+
74+
public AndOrCriteriaGroup build() {
75+
return new AndOrCriteriaGroup(this);
76+
}
77+
}
78+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2016-2022 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 java.util.Objects;
19+
20+
/**
21+
* This class represents a criteria group without an AND or an OR connector. This is useful
22+
* in situations where the initial SqlCriterion in a list should be further grouped
23+
* as in an expression like ((A &lt; 5 and B &gt; 6) or C = 3)
24+
*
25+
* @author Jeff Butler, inspired by @JoshuaJeme
26+
* @since 1.4.0
27+
*/
28+
public class CriteriaGroup extends SqlCriterion {
29+
private final SqlCriterion initialCriterion;
30+
31+
private CriteriaGroup(Builder builder) {
32+
super(builder);
33+
initialCriterion = Objects.requireNonNull(builder.initialCriterion);
34+
}
35+
36+
public SqlCriterion initialCriterion() {
37+
return initialCriterion;
38+
}
39+
40+
@Override
41+
public <R> R accept(SqlCriterionVisitor<R> visitor) {
42+
return visitor.visit(this);
43+
}
44+
45+
public static class Builder extends AbstractBuilder<Builder> {
46+
private SqlCriterion initialCriterion;
47+
48+
public Builder withInitialCriterion(SqlCriterion initialCriterion) {
49+
this.initialCriterion = initialCriterion;
50+
return this;
51+
}
52+
53+
public CriteriaGroup build() {
54+
return new CriteriaGroup(this);
55+
}
56+
57+
@Override
58+
protected Builder getThis() {
59+
return this;
60+
}
61+
}
62+
}

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

Lines changed: 66 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2021 the original author or authors.
2+
* Copyright 2016-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -214,82 +214,109 @@ static WhereDSL where() {
214214
return WhereDSL.where();
215215
}
216216

217-
static <T> WhereDSL where(BindableColumn<T> column, VisitableCondition<T> condition) {
218-
return WhereDSL.where().where(column, condition);
219-
}
220-
221217
static <T> WhereDSL where(BindableColumn<T> column, VisitableCondition<T> condition,
222-
SqlCriterion... subCriteria) {
218+
AndOrCriteriaGroup... subCriteria) {
223219
return WhereDSL.where().where(column, condition, subCriteria);
224220
}
225221

226-
static WhereDSL where(ExistsPredicate existsPredicate) {
227-
return WhereDSL.where().where(existsPredicate);
222+
static WhereDSL where(CriteriaGroup criteriaGroup, AndOrCriteriaGroup... subCriteria) {
223+
return WhereDSL.where().where(criteriaGroup, subCriteria);
228224
}
229225

230-
static WhereDSL where(ExistsPredicate existsPredicate, SqlCriterion... subCriteria) {
226+
static WhereDSL where(ExistsPredicate existsPredicate, AndOrCriteriaGroup... subCriteria) {
231227
return WhereDSL.where().where(existsPredicate, subCriteria);
232228
}
233229

234230
// where condition connectors
235-
static <T> SqlCriterion or(BindableColumn<T> column, VisitableCondition<T> condition) {
236-
return ColumnAndConditionCriterion.withColumn(column)
237-
.withConnector("or") //$NON-NLS-1$
238-
.withCondition(condition)
231+
static <T> CriteriaGroup group(BindableColumn<T> column, VisitableCondition<T> condition,
232+
AndOrCriteriaGroup...subCriteria) {
233+
return group(column, condition, Arrays.asList(subCriteria));
234+
}
235+
236+
static <T> CriteriaGroup group(BindableColumn<T> column, VisitableCondition<T> condition,
237+
List<AndOrCriteriaGroup> subCriteria) {
238+
return new CriteriaGroup.Builder()
239+
.withInitialCriterion(new ColumnAndConditionCriterion.Builder<T>().withColumn(column)
240+
.withCondition(condition).build())
241+
.withSubCriteria(subCriteria)
239242
.build();
240243
}
241244

242-
static <T> SqlCriterion or(BindableColumn<T> column, VisitableCondition<T> condition,
243-
SqlCriterion...subCriteria) {
244-
return ColumnAndConditionCriterion.withColumn(column)
245-
.withConnector("or") //$NON-NLS-1$
246-
.withCondition(condition)
247-
.withSubCriteria(Arrays.asList(subCriteria))
245+
static CriteriaGroup group(ExistsPredicate existsPredicate, AndOrCriteriaGroup...subCriteria) {
246+
return group(existsPredicate, Arrays.asList(subCriteria));
247+
}
248+
249+
static CriteriaGroup group(ExistsPredicate existsPredicate, List<AndOrCriteriaGroup> subCriteria) {
250+
return new CriteriaGroup.Builder()
251+
.withInitialCriterion(new ExistsCriterion.Builder()
252+
.withExistsPredicate(existsPredicate).build())
253+
.withSubCriteria(subCriteria)
248254
.build();
249255
}
250256

251-
static SqlCriterion or(ExistsPredicate existsPredicate) {
252-
return new ExistsCriterion.Builder()
257+
static CriteriaGroup group(CriteriaGroup criteriaGroup, AndOrCriteriaGroup...subCriteria) {
258+
return group(criteriaGroup, Arrays.asList(subCriteria));
259+
}
260+
261+
static CriteriaGroup group(CriteriaGroup criteriaGroup, List<AndOrCriteriaGroup> subCriteria) {
262+
return new CriteriaGroup.Builder()
263+
.withInitialCriterion(criteriaGroup)
264+
.withSubCriteria(subCriteria)
265+
.build();
266+
}
267+
268+
static <T> AndOrCriteriaGroup or(BindableColumn<T> column, VisitableCondition<T> condition,
269+
AndOrCriteriaGroup...subCriteria) {
270+
return new AndOrCriteriaGroup.Builder()
271+
.withInitialCriterion(ColumnAndConditionCriterion.withColumn(column)
272+
.withCondition(condition)
273+
.build())
253274
.withConnector("or") //$NON-NLS-1$
254-
.withExistsPredicate(existsPredicate)
275+
.withSubCriteria(Arrays.asList(subCriteria))
255276
.build();
256277
}
257278

258-
static SqlCriterion or(ExistsPredicate existsPredicate, SqlCriterion...subCriteria) {
259-
return new ExistsCriterion.Builder()
279+
static AndOrCriteriaGroup or(ExistsPredicate existsPredicate, AndOrCriteriaGroup...subCriteria) {
280+
return new AndOrCriteriaGroup.Builder()
281+
.withInitialCriterion(new ExistsCriterion.Builder()
282+
.withExistsPredicate(existsPredicate).build())
260283
.withConnector("or") //$NON-NLS-1$
261-
.withExistsPredicate(existsPredicate)
262284
.withSubCriteria(Arrays.asList(subCriteria))
263285
.build();
264286
}
265287

266-
static <T> SqlCriterion and(BindableColumn<T> column, VisitableCondition<T> condition) {
267-
return ColumnAndConditionCriterion.withColumn(column)
268-
.withConnector("and") //$NON-NLS-1$
269-
.withCondition(condition)
288+
static AndOrCriteriaGroup or(CriteriaGroup criteriaGroup, AndOrCriteriaGroup...subCriteria) {
289+
return new AndOrCriteriaGroup.Builder()
290+
.withConnector("or") //$NON-NLS-1$
291+
.withInitialCriterion(criteriaGroup)
292+
.withSubCriteria(Arrays.asList(subCriteria))
270293
.build();
271294
}
272295

273-
static <T> SqlCriterion and(BindableColumn<T> column, VisitableCondition<T> condition,
274-
SqlCriterion...subCriteria) {
275-
return ColumnAndConditionCriterion.withColumn(column)
296+
static <T> AndOrCriteriaGroup and(BindableColumn<T> column, VisitableCondition<T> condition,
297+
AndOrCriteriaGroup...subCriteria) {
298+
return new AndOrCriteriaGroup.Builder()
299+
.withInitialCriterion(ColumnAndConditionCriterion.withColumn(column)
300+
.withCondition(condition)
301+
.build())
276302
.withConnector("and") //$NON-NLS-1$
277-
.withCondition(condition)
278303
.withSubCriteria(Arrays.asList(subCriteria))
279304
.build();
280305
}
281306

282-
static SqlCriterion and(ExistsPredicate existsPredicate) {
283-
return new ExistsCriterion.Builder()
307+
static AndOrCriteriaGroup and(ExistsPredicate existsPredicate, AndOrCriteriaGroup...subCriteria) {
308+
return new AndOrCriteriaGroup.Builder()
309+
.withInitialCriterion(new ExistsCriterion.Builder()
310+
.withExistsPredicate(existsPredicate).build())
284311
.withConnector("and") //$NON-NLS-1$
285-
.withExistsPredicate(existsPredicate)
312+
.withSubCriteria(Arrays.asList(subCriteria))
286313
.build();
287314
}
288315

289-
static SqlCriterion and(ExistsPredicate existsPredicate, SqlCriterion...subCriteria) {
290-
return new ExistsCriterion.Builder()
316+
static AndOrCriteriaGroup and(CriteriaGroup criteriaGroup, AndOrCriteriaGroup...subCriteria) {
317+
return new AndOrCriteriaGroup.Builder()
291318
.withConnector("and") //$NON-NLS-1$
292-
.withExistsPredicate(existsPredicate)
319+
.withInitialCriterion(criteriaGroup)
293320
.withSubCriteria(Arrays.asList(subCriteria))
294321
.build();
295322
}

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

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 the original author or authors.
2+
* Copyright 2016-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,42 +16,27 @@
1616
package org.mybatis.dynamic.sql;
1717

1818
import java.util.ArrayList;
19+
import java.util.Collections;
1920
import java.util.List;
20-
import java.util.Objects;
21-
import java.util.Optional;
22-
import java.util.function.Function;
23-
import java.util.stream.Stream;
2421

2522
public abstract class SqlCriterion {
2623

27-
private final String connector;
28-
private final List<SqlCriterion> subCriteria;
24+
private final List<AndOrCriteriaGroup> subCriteria = new ArrayList<>();
2925

3026
protected SqlCriterion(AbstractBuilder<?> builder) {
31-
connector = builder.connector;
32-
subCriteria = Objects.requireNonNull(builder.subCriteria);
27+
subCriteria.addAll(builder.subCriteria);
3328
}
3429

35-
public Optional<String> connector() {
36-
return Optional.ofNullable(connector);
37-
}
38-
39-
public <R> Stream<R> mapSubCriteria(Function<SqlCriterion, R> mapper) {
40-
return subCriteria.stream().map(mapper);
30+
public List<AndOrCriteriaGroup> subCriteria() {
31+
return Collections.unmodifiableList(subCriteria);
4132
}
4233

4334
public abstract <R> R accept(SqlCriterionVisitor<R> visitor);
4435

4536
protected abstract static class AbstractBuilder<T extends AbstractBuilder<T>> {
46-
private String connector;
47-
private final List<SqlCriterion> subCriteria = new ArrayList<>();
48-
49-
public T withConnector(String connector) {
50-
this.connector = connector;
51-
return getThis();
52-
}
37+
private final List<AndOrCriteriaGroup> subCriteria = new ArrayList<>();
5338

54-
public T withSubCriteria(List<SqlCriterion> subCriteria) {
39+
public T withSubCriteria(List<AndOrCriteriaGroup> subCriteria) {
5540
this.subCriteria.addAll(subCriteria);
5641
return getThis();
5742
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 the original author or authors.
2+
* Copyright 2016-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,4 +19,6 @@ public interface SqlCriterionVisitor<R> {
1919
<T> R visit(ColumnAndConditionCriterion<T> criterion);
2020

2121
R visit(ExistsCriterion criterion);
22+
23+
R visit(CriteriaGroup criterion);
2224
}

0 commit comments

Comments
 (0)