Skip to content

Commit 28ba6f3

Browse files
authored
Merge pull request #794 from jeffgbutler/new-in-rendering
Change "in" Condition Rendering - Render With Empty Lists
2 parents dc169f2 + f5a5318 commit 28ba6f3

22 files changed

+953
-206
lines changed

CHANGELOG.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,17 @@ This log will detail notable changes to MyBatis Dynamic SQL. Full details are av
44

55
## Release 1.5.2 - Unreleased
66

7-
This is a small maintenance release with improvements to the Kotlin DSL for CASE expressions. We worked on this soon
8-
after the 1.5.1 release, so wanted to get it out quickly.
9-
See this PR for details: ([#785](https://github.com/mybatis/mybatis-dynamic-sql/pull/785))
7+
This is a small maintenance release with the following changes:
8+
9+
1. Improvements to the Kotlin DSL for CASE expressions (infix methods for "else" and "then"). See this PR for
10+
details: ([#785](https://github.com/mybatis/mybatis-dynamic-sql/pull/785))
11+
2. **Potentially Breaking Change**: the "in" conditions ("isIn", "isNotIn", "isInCaseInsensitive",
12+
"isNotInCaseInsensitive") will now render if the input list of values is empty. This will lead
13+
to a runtime exception. This change was made out of an abundance of caution and is the safest choice.
14+
If you wish to allow "in" conditions to be removed from where clauses when the list is empty,
15+
then use the "when present" versions of those conditions. If you are unsure how this works, please
16+
read the documentation here: https://mybatis.org/mybatis-dynamic-sql/docs/conditions.html#optionality-with-the-%E2%80%9Cin%E2%80%9D-conditions
17+
For background on the reason for the change, see the discussion here: https://github.com/mybatis/mybatis-dynamic-sql/issues/788
1018

1119
GitHub milestone: [https://github.com/mybatis/mybatis-dynamic-sql/milestone/14?closed=1](https://github.com/mybatis/mybatis-dynamic-sql/milestone/14?closed=1)
1220

@@ -34,8 +42,8 @@ We've tested this extensively and the code is, of course, 100% covered by test c
3442
covered every scenario. Please let us know if you find issues.
3543

3644
Full documentation is available here:
37-
- [Java Case Expression DSL Documentation](caseExpressions.md)
38-
- [Kotlin Case Expression DSL Documentation](kotlinCaseExpressions.md)
45+
- [Java Case Expression DSL Documentation](https://mybatis.org/mybatis-dynamic-sql/docs/caseExpressions.html)
46+
- [Kotlin Case Expression DSL Documentation](https://mybatis.org/mybatis-dynamic-sql/docs/kotlinCaseExpressions.html)
3947

4048
The pull request for this change is ([#761](https://github.com/mybatis/mybatis-dynamic-sql/pull/761))
4149

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

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323
import java.util.stream.Collectors;
2424
import java.util.stream.Stream;
2525

26-
import org.mybatis.dynamic.sql.render.RenderingContext;
27-
2826
public abstract class AbstractListValueCondition<T> implements VisitableCondition<T> {
2927
protected final Collection<T> values;
3028

@@ -41,15 +39,6 @@ public boolean isEmpty() {
4139
return values.isEmpty();
4240
}
4341

44-
@Override
45-
public boolean shouldRender(RenderingContext renderingContext) {
46-
if (isEmpty()) {
47-
return renderingContext.isEmptyListConditionRenderingAllowed();
48-
} else {
49-
return true;
50-
}
51-
}
52-
5342
@Override
5443
public <R> R accept(ConditionVisitor<T, R> visitor) {
5544
return visitor.visit(this);
@@ -85,14 +74,12 @@ protected <R, S extends AbstractListValueCondition<R>> S mapSupport(Function<? s
8574
}
8675

8776
/**
88-
* If renderable, apply the predicate to each value in the list and return a new condition with the filtered values.
89-
* Else returns a condition that will not render (this). If all values are filtered out of the value
90-
* list, then the condition will not render.
77+
* If not empty, apply the predicate to each value in the list and return a new condition with the filtered values.
78+
* Else returns an empty condition (this).
9179
*
92-
* @param predicate
93-
* predicate applied to the values, if renderable
80+
* @param predicate predicate applied to the values, if not empty
9481
*
95-
* @return a new condition with filtered values if renderable, otherwise a condition that will not render.
82+
* @return a new condition with filtered values if renderable, otherwise an empty condition
9683
*/
9784
public abstract AbstractListValueCondition<T> filter(Predicate<? super T> predicate);
9885

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

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@
7676
import org.mybatis.dynamic.sql.where.condition.IsGreaterThanWithSubselect;
7777
import org.mybatis.dynamic.sql.where.condition.IsIn;
7878
import org.mybatis.dynamic.sql.where.condition.IsInCaseInsensitive;
79+
import org.mybatis.dynamic.sql.where.condition.IsInCaseInsensitiveWhenPresent;
80+
import org.mybatis.dynamic.sql.where.condition.IsInWhenPresent;
7981
import org.mybatis.dynamic.sql.where.condition.IsInWithSubselect;
8082
import org.mybatis.dynamic.sql.where.condition.IsLessThan;
8183
import org.mybatis.dynamic.sql.where.condition.IsLessThanColumn;
@@ -91,6 +93,8 @@
9193
import org.mybatis.dynamic.sql.where.condition.IsNotEqualToWithSubselect;
9294
import org.mybatis.dynamic.sql.where.condition.IsNotIn;
9395
import org.mybatis.dynamic.sql.where.condition.IsNotInCaseInsensitive;
96+
import org.mybatis.dynamic.sql.where.condition.IsNotInCaseInsensitiveWhenPresent;
97+
import org.mybatis.dynamic.sql.where.condition.IsNotInWhenPresent;
9498
import org.mybatis.dynamic.sql.where.condition.IsNotInWithSubselect;
9599
import org.mybatis.dynamic.sql.where.condition.IsNotLike;
96100
import org.mybatis.dynamic.sql.where.condition.IsNotLikeCaseInsensitive;
@@ -764,12 +768,12 @@ static <T> IsInWithSubselect<T> isIn(Buildable<SelectModel> selectModelBuilder)
764768
}
765769

766770
@SafeVarargs
767-
static <T> IsIn<T> isInWhenPresent(T... values) {
768-
return IsIn.of(values).filter(Objects::nonNull);
771+
static <T> IsInWhenPresent<T> isInWhenPresent(T... values) {
772+
return IsInWhenPresent.of(values);
769773
}
770774

771-
static <T> IsIn<T> isInWhenPresent(Collection<T> values) {
772-
return values == null ? IsIn.empty() : IsIn.of(values).filter(Objects::nonNull);
775+
static <T> IsInWhenPresent<T> isInWhenPresent(Collection<T> values) {
776+
return values == null ? IsInWhenPresent.empty() : IsInWhenPresent.of(values);
773777
}
774778

775779
@SafeVarargs
@@ -786,12 +790,12 @@ static <T> IsNotInWithSubselect<T> isNotIn(Buildable<SelectModel> selectModelBui
786790
}
787791

788792
@SafeVarargs
789-
static <T> IsNotIn<T> isNotInWhenPresent(T... values) {
790-
return IsNotIn.of(values).filter(Objects::nonNull);
793+
static <T> IsNotInWhenPresent<T> isNotInWhenPresent(T... values) {
794+
return IsNotInWhenPresent.of(values);
791795
}
792796

793-
static <T> IsNotIn<T> isNotInWhenPresent(Collection<T> values) {
794-
return values == null ? IsNotIn.empty() : IsNotIn.of(values).filter(Objects::nonNull);
797+
static <T> IsNotInWhenPresent<T> isNotInWhenPresent(Collection<T> values) {
798+
return values == null ? IsNotInWhenPresent.empty() : IsNotInWhenPresent.of(values);
795799
}
796800

797801
static <T> IsBetween.Builder<T> isBetween(T value1) {
@@ -909,12 +913,12 @@ static IsInCaseInsensitive isInCaseInsensitive(Collection<String> values) {
909913
return IsInCaseInsensitive.of(values);
910914
}
911915

912-
static IsInCaseInsensitive isInCaseInsensitiveWhenPresent(String... values) {
913-
return IsInCaseInsensitive.of(values).filter(Objects::nonNull);
916+
static IsInCaseInsensitiveWhenPresent isInCaseInsensitiveWhenPresent(String... values) {
917+
return IsInCaseInsensitiveWhenPresent.of(values);
914918
}
915919

916-
static IsInCaseInsensitive isInCaseInsensitiveWhenPresent(Collection<String> values) {
917-
return values == null ? IsInCaseInsensitive.empty() : IsInCaseInsensitive.of(values).filter(Objects::nonNull);
920+
static IsInCaseInsensitiveWhenPresent isInCaseInsensitiveWhenPresent(Collection<String> values) {
921+
return values == null ? IsInCaseInsensitiveWhenPresent.empty() : IsInCaseInsensitiveWhenPresent.of(values);
918922
}
919923

920924
static IsNotInCaseInsensitive isNotInCaseInsensitive(String... values) {
@@ -925,13 +929,13 @@ static IsNotInCaseInsensitive isNotInCaseInsensitive(Collection<String> values)
925929
return IsNotInCaseInsensitive.of(values);
926930
}
927931

928-
static IsNotInCaseInsensitive isNotInCaseInsensitiveWhenPresent(String... values) {
929-
return IsNotInCaseInsensitive.of(values).filter(Objects::nonNull);
932+
static IsNotInCaseInsensitiveWhenPresent isNotInCaseInsensitiveWhenPresent(String... values) {
933+
return IsNotInCaseInsensitiveWhenPresent.of(values);
930934
}
931935

932-
static IsNotInCaseInsensitive isNotInCaseInsensitiveWhenPresent(Collection<String> values) {
933-
return values == null ? IsNotInCaseInsensitive.empty() :
934-
IsNotInCaseInsensitive.of(values).filter(Objects::nonNull);
936+
static IsNotInCaseInsensitiveWhenPresent isNotInCaseInsensitiveWhenPresent(Collection<String> values) {
937+
return values == null ? IsNotInCaseInsensitiveWhenPresent.empty() :
938+
IsNotInCaseInsensitiveWhenPresent.of(values);
935939
}
936940

937941
// order by support

src/main/java/org/mybatis/dynamic/sql/configuration/GlobalConfiguration.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ public class GlobalConfiguration {
2626
public static final String CONFIGURATION_FILE_PROPERTY = "mybatis-dynamic-sql.configurationFile"; //$NON-NLS-1$
2727
private static final String DEFAULT_PROPERTY_FILE = "mybatis-dynamic-sql.properties"; //$NON-NLS-1$
2828
private boolean isNonRenderingWhereClauseAllowed = false;
29-
private boolean isEmptyListConditionRenderingAllowed = false;
3029
private final Properties properties = new Properties();
3130

3231
public GlobalConfiguration() {
@@ -66,16 +65,9 @@ void loadProperties(InputStream inputStream, String propertyFile) {
6665
private void initializeKnownProperties() {
6766
String value = properties.getProperty("nonRenderingWhereClauseAllowed", "false"); //$NON-NLS-1$ //$NON-NLS-2$
6867
isNonRenderingWhereClauseAllowed = Boolean.parseBoolean(value);
69-
70-
value = properties.getProperty("emptyListConditionRenderingAllowed", "false"); //$NON-NLS-1$ //$NON-NLS-2$
71-
isEmptyListConditionRenderingAllowed = Boolean.parseBoolean(value);
7268
}
7369

7470
public boolean isIsNonRenderingWhereClauseAllowed() {
7571
return isNonRenderingWhereClauseAllowed;
7672
}
77-
78-
public boolean isEmptyListConditionRenderingAllowed() {
79-
return isEmptyListConditionRenderingAllowed;
80-
}
8173
}

src/main/java/org/mybatis/dynamic/sql/configuration/StatementConfiguration.java

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,6 @@
2525
* Configurable behaviors are detailed below:
2626
*
2727
* <dl>
28-
* <dt>emptyListConditionRenderingAllowed</dt>
29-
* <dd>If false (default), the framework will not render list conditions that are empty in a where clause.
30-
* This is beneficial in that it will not allow the library to generate invalid SQL, but it has a
31-
* potentially dangerous side effect where a statement could be generated that impacts more rows
32-
* then expected. If true, an empty list will be rendered as "in ()", "not in ()", etc. which will likely
33-
* cause an SQLException at runtime.
34-
* </dd>
3528
* <dt>nonRenderingWhereClauseAllowed</dt>
3629
* <dd>If false (default), the framework will throw a {@link NonRenderingWhereClauseException}
3730
* if a where clause is specified in the statement, but it fails to render because all
@@ -51,9 +44,6 @@ public class StatementConfiguration {
5144
private boolean isNonRenderingWhereClauseAllowed =
5245
GlobalContext.getConfiguration().isIsNonRenderingWhereClauseAllowed();
5346

54-
private boolean isEmptyListConditionRenderingAllowed =
55-
GlobalContext.getConfiguration().isEmptyListConditionRenderingAllowed();
56-
5747
public boolean isNonRenderingWhereClauseAllowed() {
5848
return isNonRenderingWhereClauseAllowed;
5949
}
@@ -62,13 +52,4 @@ public StatementConfiguration setNonRenderingWhereClauseAllowed(boolean nonRende
6252
isNonRenderingWhereClauseAllowed = nonRenderingWhereClauseAllowed;
6353
return this;
6454
}
65-
66-
public boolean isEmptyListConditionRenderingAllowed() {
67-
return isEmptyListConditionRenderingAllowed;
68-
}
69-
70-
public StatementConfiguration setEmptyListConditionRenderingAllowed(boolean emptyListConditionRenderingAllowed) {
71-
isEmptyListConditionRenderingAllowed = emptyListConditionRenderingAllowed;
72-
return this;
73-
}
7455
}

src/main/java/org/mybatis/dynamic/sql/render/RenderingContext.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,6 @@ public boolean isNonRenderingClauseAllowed() {
101101
return statementConfiguration.isNonRenderingWhereClauseAllowed();
102102
}
103103

104-
public boolean isEmptyListConditionRenderingAllowed() {
105-
return statementConfiguration.isEmptyListConditionRenderingAllowed();
106-
}
107-
108104
/**
109105
* Create a new rendering context based on this, with the table alias calculator modified to include the
110106
* specified child table alias calculator. This is used by the query expression renderer when the alias calculator

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.function.Predicate;
2323

2424
import org.mybatis.dynamic.sql.AbstractListValueCondition;
25+
import org.mybatis.dynamic.sql.render.RenderingContext;
2526

2627
public class IsIn<T> extends AbstractListValueCondition<T> {
2728
private static final IsIn<?> EMPTY = new IsIn<>(Collections.emptyList());
@@ -36,6 +37,11 @@ protected IsIn(Collection<T> values) {
3637
super(values);
3738
}
3839

40+
@Override
41+
public boolean shouldRender(RenderingContext renderingContext) {
42+
return true;
43+
}
44+
3945
@Override
4046
public String operator() {
4147
return "in"; //$NON-NLS-1$
@@ -47,13 +53,12 @@ public IsIn<T> filter(Predicate<? super T> predicate) {
4753
}
4854

4955
/**
50-
* If renderable, apply the mapping to each value in the list return a new condition with the mapped values.
51-
* Else return a condition that will not render (this).
56+
* If not empty, apply the mapping to each value in the list return a new condition with the mapped values.
57+
* Else return an empty condition (this).
5258
*
53-
* @param mapper a mapping function to apply to the values, if renderable
59+
* @param mapper a mapping function to apply to the values, if not empty
5460
* @param <R> type of the new condition
55-
* @return a new condition with mapped values if renderable, otherwise a condition
56-
* that will not render.
61+
* @return a new condition with mapped values if renderable, otherwise an empty condition
5762
*/
5863
public <R> IsIn<R> map(Function<? super T, ? extends R> mapper) {
5964
Function<Collection<R>, IsIn<R>> constructor = IsIn::new;

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.function.UnaryOperator;
2323

2424
import org.mybatis.dynamic.sql.AbstractListValueCondition;
25+
import org.mybatis.dynamic.sql.render.RenderingContext;
2526
import org.mybatis.dynamic.sql.util.StringUtilities;
2627

2728
public class IsInCaseInsensitive extends AbstractListValueCondition<String>
@@ -36,6 +37,11 @@ protected IsInCaseInsensitive(Collection<String> values) {
3637
super(values);
3738
}
3839

40+
@Override
41+
public boolean shouldRender(RenderingContext renderingContext) {
42+
return true;
43+
}
44+
3945
@Override
4046
public String operator() {
4147
return "in"; //$NON-NLS-1$
@@ -47,12 +53,11 @@ public IsInCaseInsensitive filter(Predicate<? super String> predicate) {
4753
}
4854

4955
/**
50-
* If renderable, apply the mapping to each value in the list return a new condition with the mapped values.
51-
* Else return a condition that will not render (this).
56+
* If not empty, apply the mapping to each value in the list return a new condition with the mapped values.
57+
* Else return an empty condition (this).
5258
*
53-
* @param mapper a mapping function to apply to the values, if renderable
54-
* @return a new condition with mapped values if renderable, otherwise a condition
55-
* that will not render.
59+
* @param mapper a mapping function to apply to the values, if not empty
60+
* @return a new condition with mapped values if renderable, otherwise an empty condition
5661
*/
5762
public IsInCaseInsensitive map(UnaryOperator<String> mapper) {
5863
return mapSupport(mapper, IsInCaseInsensitive::new, IsInCaseInsensitive::empty);
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright 2016-2024 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 org.mybatis.dynamic.sql.where.condition;
17+
18+
import java.util.Arrays;
19+
import java.util.Collection;
20+
import java.util.Collections;
21+
import java.util.Objects;
22+
import java.util.function.Predicate;
23+
import java.util.function.UnaryOperator;
24+
import java.util.stream.Collectors;
25+
26+
import org.mybatis.dynamic.sql.AbstractListValueCondition;
27+
import org.mybatis.dynamic.sql.util.StringUtilities;
28+
29+
public class IsInCaseInsensitiveWhenPresent extends AbstractListValueCondition<String>
30+
implements CaseInsensitiveVisitableCondition {
31+
private static final IsInCaseInsensitiveWhenPresent EMPTY = new IsInCaseInsensitiveWhenPresent(Collections.emptyList());
32+
33+
public static IsInCaseInsensitiveWhenPresent empty() {
34+
return EMPTY;
35+
}
36+
37+
protected IsInCaseInsensitiveWhenPresent(Collection<String> values) {
38+
super(values.stream().filter(Objects::nonNull).collect(Collectors.toList()));
39+
}
40+
41+
@Override
42+
public String operator() {
43+
return "in"; //$NON-NLS-1$
44+
}
45+
46+
@Override
47+
public IsInCaseInsensitiveWhenPresent filter(Predicate<? super String> predicate) {
48+
return filterSupport(predicate, IsInCaseInsensitiveWhenPresent::new, this, IsInCaseInsensitiveWhenPresent::empty);
49+
}
50+
51+
/**
52+
* If not empty, apply the mapping to each value in the list return a new condition with the mapped values.
53+
* Else return an empty condition (this).
54+
*
55+
* @param mapper a mapping function to apply to the values, if not empty
56+
* @return a new condition with mapped values if renderable, otherwise an empty condition
57+
*/
58+
public IsInCaseInsensitiveWhenPresent map(UnaryOperator<String> mapper) {
59+
return mapSupport(mapper, IsInCaseInsensitiveWhenPresent::new, IsInCaseInsensitiveWhenPresent::empty);
60+
}
61+
62+
public static IsInCaseInsensitiveWhenPresent of(String... values) {
63+
return of(Arrays.asList(values));
64+
}
65+
66+
public static IsInCaseInsensitiveWhenPresent of(Collection<String> values) {
67+
return new IsInCaseInsensitiveWhenPresent(values).map(StringUtilities::safelyUpperCase);
68+
}
69+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy