Skip to content

Commit 5afc029

Browse files
authored
Merge pull request #241 from jeffgbutler/implement-empty-callback
Implement Callback for In and NotIn Conditions When the Value List is Empty
2 parents 66cda22 + fb27186 commit 5afc029

18 files changed

+268
-224
lines changed

.github/dependabot.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
#
2+
# Copyright 2016-2020 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+
117
version: 2
218
updates:
319
- package-ecosystem: maven

CHANGELOG.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@
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.2.1 - Unreleased
6+
7+
GitHub milestone: [https://github.com/mybatis/mybatis-dynamic-sql/issues?q=milestone%3A1.2.1+](https://github.com/mybatis/mybatis-dynamic-sql/issues?q=milestone%3A1.2.1+)
8+
9+
### Fixed
10+
11+
- Fixed a bug where the In conditions could render incorrectly in certain circumstances. ([#239](https://github.com/mybatis/mybatis-dynamic-sql/issues/239))
12+
13+
### Added
14+
15+
- Added a callback capability to the "In" conditions that will be called before rendering when the conditions are empty. Also, removed the option that forced the library to render invalid SQL in that case. ([#241](https://github.com/mybatis/mybatis-dynamic-sql/pull/241))
16+
517
## Release 1.2.0 - August 19, 2020
618

719
GitHub milestone: [https://github.com/mybatis/mybatis-dynamic-sql/issues?q=milestone%3A1.2.0+](https://github.com/mybatis/mybatis-dynamic-sql/issues?q=milestone%3A1.2.0+)
@@ -14,7 +26,7 @@ This release includes a significant refactoring of the classes in the "org.mybat
1426

1527
With this release, we deprecated several insert methods because they were inconsistently named or awkward. All deprecated methods have documented direct replacements.
1628

17-
All deprecated code will be removed in the next release.
29+
All deprecated code will be removed in the next minor release.
1830

1931
### Added
2032

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

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,26 @@
2222
import java.util.stream.Collectors;
2323
import java.util.stream.Stream;
2424

25-
public abstract class AbstractListValueCondition<T> implements VisitableCondition<T> {
25+
public abstract class AbstractListValueCondition<T, S extends AbstractListValueCondition<T, S>>
26+
implements VisitableCondition<T> {
2627
protected final Collection<T> values;
2728
protected final UnaryOperator<Stream<T>> valueStreamTransformer;
28-
protected boolean renderWhenEmpty = false;
29+
protected final Callback emptyCallback;
2930

3031
protected AbstractListValueCondition(Collection<T> values) {
31-
this(values, UnaryOperator.identity());
32+
this(values, UnaryOperator.identity(), () -> { });
3233
}
3334

3435
protected AbstractListValueCondition(Collection<T> values, UnaryOperator<Stream<T>> valueStreamTransformer) {
36+
this(values, valueStreamTransformer, () -> { });
37+
}
38+
39+
protected AbstractListValueCondition(Collection<T> values, UnaryOperator<Stream<T>> valueStreamTransformer,
40+
Callback emptyCallback) {
3541
this.valueStreamTransformer = Objects.requireNonNull(valueStreamTransformer);
3642
this.values = valueStreamTransformer.apply(Objects.requireNonNull(values).stream())
3743
.collect(Collectors.toList());
44+
this.emptyCallback = Objects.requireNonNull(emptyCallback);
3845
}
3946

4047
public final <R> Stream<R> mapValues(Function<T, R> mapper) {
@@ -43,20 +50,20 @@ public final <R> Stream<R> mapValues(Function<T, R> mapper) {
4350

4451
@Override
4552
public boolean shouldRender() {
46-
return !values.isEmpty() || renderWhenEmpty;
53+
if (values.isEmpty()) {
54+
emptyCallback.call();
55+
return false;
56+
} else {
57+
return true;
58+
}
4759
}
4860

49-
/**
50-
* Use with caution - this could cause the library to render invalid SQL like "where column in ()".
51-
*/
52-
protected void forceRenderingWhenEmpty() {
53-
renderWhenEmpty = true;
54-
}
55-
5661
@Override
5762
public <R> R accept(ConditionVisitor<T, R> visitor) {
5863
return visitor.visit(this);
5964
}
6065

66+
public abstract S withListEmptyCallback(Callback callback);
67+
6168
public abstract String renderCondition(String columnName, Stream<String> placeholders);
6269
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* Copyright 2016-2020 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.function.Function;
19+
20+
@FunctionalInterface
21+
public interface Callback {
22+
void call();
23+
24+
static Callback exceptionThrowingCallback(String message) {
25+
return exceptionThrowingCallback(message, RuntimeException::new);
26+
}
27+
28+
static Callback exceptionThrowingCallback(String message,
29+
Function<String, ? extends RuntimeException> exceptionBuilder) {
30+
return () -> {
31+
throw exceptionBuilder.apply(message);
32+
};
33+
}
34+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2016-2018 the original author or authors.
2+
* Copyright 2016-2020 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,7 +16,7 @@
1616
package org.mybatis.dynamic.sql;
1717

1818
public interface ConditionVisitor<T, R> {
19-
R visit(AbstractListValueCondition<T> condition);
19+
R visit(AbstractListValueCondition<T, ?> condition);
2020

2121
R visit(AbstractNoValueCondition<T> condition);
2222

src/main/java/org/mybatis/dynamic/sql/util/StringUtilities.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616
package org.mybatis.dynamic.sql.util;
1717

1818
import java.util.Optional;
19-
import java.util.function.UnaryOperator;
20-
import java.util.stream.Stream;
2119

2220
public interface StringUtilities {
2321

@@ -42,9 +40,4 @@ static String spaceBefore(String in) {
4240
static String safelyUpperCase(String s) {
4341
return s == null ? null : s.toUpperCase();
4442
}
45-
46-
static UnaryOperator<Stream<String>> upperCaseAfter(UnaryOperator<Stream<String>> valueModifier) {
47-
UnaryOperator<Stream<String>> ua = s -> s.map(StringUtilities::safelyUpperCase);
48-
return t -> ua.apply(valueModifier.apply(t));
49-
}
5043
}

src/main/java/org/mybatis/dynamic/sql/where/condition/IsIn.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,20 @@
2323
import java.util.stream.Stream;
2424

2525
import org.mybatis.dynamic.sql.AbstractListValueCondition;
26+
import org.mybatis.dynamic.sql.Callback;
2627

27-
public class IsIn<T> extends AbstractListValueCondition<T> {
28+
public class IsIn<T> extends AbstractListValueCondition<T, IsIn<T>> {
29+
30+
protected IsIn(Collection<T> values) {
31+
super(values);
32+
}
2833

2934
protected IsIn(Collection<T> values, UnaryOperator<Stream<T>> valueStreamTransformer) {
3035
super(values, valueStreamTransformer);
3136
}
3237

33-
protected IsIn(Collection<T> values) {
34-
super(values);
38+
protected IsIn(Collection<T> values, UnaryOperator<Stream<T>> valueStreamTransformer, Callback emptyCallback) {
39+
super(values, valueStreamTransformer, emptyCallback);
3540
}
3641

3742
@Override
@@ -40,6 +45,11 @@ public String renderCondition(String columnName, Stream<String> placeholders) {
4045
+ placeholders.collect(Collectors.joining(",", "in (", ")")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
4146
}
4247

48+
@Override
49+
public IsIn<T> withListEmptyCallback(Callback callback) {
50+
return new IsIn<>(values, valueStreamTransformer, callback);
51+
}
52+
4353
/**
4454
* This method allows you to modify the condition's values before they are placed into the parameter map.
4555
* For example, you could filter nulls, or trim strings, etc. This process will run before final rendering of SQL.
@@ -51,9 +61,7 @@ public String renderCondition(String columnName, Stream<String> placeholders) {
5161
* @return new condition with the specified transformer
5262
*/
5363
public IsIn<T> then(UnaryOperator<Stream<T>> valueStreamTransformer) {
54-
IsIn<T> answer = new IsIn<>(values, valueStreamTransformer);
55-
answer.renderWhenEmpty = renderWhenEmpty;
56-
return answer;
64+
return new IsIn<>(values, valueStreamTransformer, emptyCallback);
5765
}
5866

5967
public static <T> IsIn<T> of(Collection<T> values) {

src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitive.java

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,22 @@
2121
import java.util.stream.Stream;
2222

2323
import org.mybatis.dynamic.sql.AbstractListValueCondition;
24+
import org.mybatis.dynamic.sql.Callback;
2425
import org.mybatis.dynamic.sql.util.StringUtilities;
2526

26-
public class IsInCaseInsensitive extends AbstractListValueCondition<String> {
27+
public class IsInCaseInsensitive extends AbstractListValueCondition<String, IsInCaseInsensitive> {
2728

2829
protected IsInCaseInsensitive(Collection<String> values) {
2930
super(values, s -> s.map(StringUtilities::safelyUpperCase));
3031
}
3132

3233
protected IsInCaseInsensitive(Collection<String> values, UnaryOperator<Stream<String>> valueStreamTransformer) {
33-
super(values, StringUtilities.upperCaseAfter(valueStreamTransformer));
34+
super(values, valueStreamTransformer);
35+
}
36+
37+
protected IsInCaseInsensitive(Collection<String> values, UnaryOperator<Stream<String>> valueStreamTransformer,
38+
Callback emptyCallback) {
39+
super(values, valueStreamTransformer, emptyCallback);
3440
}
3541

3642
@Override
@@ -39,20 +45,9 @@ public String renderCondition(String columnName, Stream<String> placeholders) {
3945
placeholders.collect(Collectors.joining(",", "in (", ")")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
4046
}
4147

42-
/**
43-
* This method allows you to modify the condition's values before they are placed into the parameter map.
44-
* For example, you could filter nulls, or trim strings, etc. This process will run before final rendering of SQL.
45-
* If you filter values out of the stream, then final condition will not reference those values. If you filter all
46-
* values out of the stream, then the condition will not render.
47-
*
48-
* @param valueStreamTransformer a UnaryOperator that will transform the value stream before
49-
* the values are placed in the parameter map
50-
* @return new condition with the specified transformer
51-
*/
52-
public IsInCaseInsensitive then(UnaryOperator<Stream<String>> valueStreamTransformer) {
53-
IsInCaseInsensitive answer = new IsInCaseInsensitive(values, valueStreamTransformer);
54-
answer.renderWhenEmpty = renderWhenEmpty;
55-
return answer;
48+
@Override
49+
public IsInCaseInsensitive withListEmptyCallback(Callback callback) {
50+
return new IsInCaseInsensitive(values, valueStreamTransformer, callback);
5651
}
5752

5853
public static IsInCaseInsensitive of(Collection<String> values) {

src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitiveWhenPresent.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2016-2019 the original author or authors.
2+
* Copyright 2016-2020 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.
@@ -17,11 +17,25 @@
1717

1818
import java.util.Collection;
1919
import java.util.Objects;
20+
import java.util.function.UnaryOperator;
21+
import java.util.stream.Stream;
22+
import org.mybatis.dynamic.sql.Callback;
23+
import org.mybatis.dynamic.sql.util.StringUtilities;
2024

2125
public class IsInCaseInsensitiveWhenPresent extends IsInCaseInsensitive {
2226

2327
protected IsInCaseInsensitiveWhenPresent(Collection<String> values) {
24-
super(values, s -> s.filter(Objects::nonNull));
28+
super(values, s -> s.filter(Objects::nonNull).map(StringUtilities::safelyUpperCase));
29+
}
30+
31+
protected IsInCaseInsensitiveWhenPresent(Collection<String> values,
32+
UnaryOperator<Stream<String>> valueStreamTransformer, Callback callback) {
33+
super(values, valueStreamTransformer, callback);
34+
}
35+
36+
@Override
37+
public IsInCaseInsensitiveWhenPresent withListEmptyCallback(Callback callback) {
38+
return new IsInCaseInsensitiveWhenPresent(values, valueStreamTransformer, callback);
2539
}
2640

2741
public static IsInCaseInsensitiveWhenPresent of(Collection<String> values) {

src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotIn.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,20 @@
2323
import java.util.stream.Stream;
2424

2525
import org.mybatis.dynamic.sql.AbstractListValueCondition;
26+
import org.mybatis.dynamic.sql.Callback;
2627

27-
public class IsNotIn<T> extends AbstractListValueCondition<T> {
28+
public class IsNotIn<T> extends AbstractListValueCondition<T, IsNotIn<T>> {
29+
30+
protected IsNotIn(Collection<T> values) {
31+
super(values);
32+
}
2833

2934
protected IsNotIn(Collection<T> values, UnaryOperator<Stream<T>> valueStreamTransformer) {
3035
super(values, valueStreamTransformer);
3136
}
3237

33-
protected IsNotIn(Collection<T> values) {
34-
super(values);
38+
protected IsNotIn(Collection<T> values, UnaryOperator<Stream<T>> valueStreamTransformer, Callback emptyCallback) {
39+
super(values, valueStreamTransformer, emptyCallback);
3540
}
3641

3742
@Override
@@ -41,6 +46,11 @@ public String renderCondition(String columnName, Stream<String> placeholders) {
4146
Collectors.joining(",", "not in (", ")")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
4247
}
4348

49+
@Override
50+
public IsNotIn<T> withListEmptyCallback(Callback callback) {
51+
return new IsNotIn<>(values, valueStreamTransformer, callback);
52+
}
53+
4454
/**
4555
* This method allows you to modify the condition's values before they are placed into the parameter map.
4656
* For example, you could filter nulls, or trim strings, etc. This process will run before final rendering of SQL.
@@ -52,9 +62,7 @@ public String renderCondition(String columnName, Stream<String> placeholders) {
5262
* @return new condition with the specified transformer
5363
*/
5464
public IsNotIn<T> then(UnaryOperator<Stream<T>> valueStreamTransformer) {
55-
IsNotIn<T> answer = new IsNotIn<>(values, valueStreamTransformer);
56-
answer.renderWhenEmpty = renderWhenEmpty;
57-
return answer;
65+
return new IsNotIn<>(values, valueStreamTransformer, emptyCallback);
5866
}
5967

6068
public static <T> IsNotIn<T> of(Collection<T> values) {

0 commit comments

Comments
 (0)