Skip to content

Commit 0e91dad

Browse files
authored
Merge pull request #764 from jeffgbutler/render-empty-lists
Optionally Allow Empty List Conditions to Render
2 parents 60cb080 + 45da95e commit 0e91dad

File tree

7 files changed

+66
-13
lines changed

7 files changed

+66
-13
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ types - which is a rare usage. Please let us know if this causes an undo hardshi
6363
3. Refactored the conditions to separate the concept of an empty condition from that of a renderable condition. This
6464
will enable a future change where conditions could decide to allow rendering even if they are considered empty (such
6565
as rendering empty lists). This change should be transparent to users unless they have implemented custom conditions.
66+
4. Added a configuration setting to allow empty list conditions to render. This could generate invalid SQL, but might be
67+
a good safety measure in some cases.
6668

6769
## Release 1.5.0 - April 21, 2023
6870

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

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

26+
import org.mybatis.dynamic.sql.render.RenderingContext;
27+
2628
public abstract class AbstractListValueCondition<T> implements VisitableCondition<T> {
2729
protected final Collection<T> values;
2830

@@ -39,6 +41,15 @@ public boolean isEmpty() {
3941
return values.isEmpty();
4042
}
4143

44+
@Override
45+
public boolean shouldRender(RenderingContext renderingContext) {
46+
if (isEmpty()) {
47+
return renderingContext.isEmptyListConditionRenderingAllowed();
48+
} else {
49+
return true;
50+
}
51+
}
52+
4253
@Override
4354
public <R> R accept(ConditionVisitor<T, R> visitor) {
4455
return visitor.visit(this);

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ 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;
2930
private final Properties properties = new Properties();
3031

3132
public GlobalConfiguration() {
@@ -34,7 +35,7 @@ public GlobalConfiguration() {
3435

3536
private void initialize() {
3637
initializeProperties();
37-
initializeNonRenderingWhereClauseAllowed();
38+
initializeKnownProperties();
3839
}
3940

4041
private void initializeProperties() {
@@ -62,12 +63,19 @@ void loadProperties(InputStream inputStream, String propertyFile) {
6263
}
6364
}
6465

65-
private void initializeNonRenderingWhereClauseAllowed() {
66+
private void initializeKnownProperties() {
6667
String value = properties.getProperty("nonRenderingWhereClauseAllowed", "false"); //$NON-NLS-1$ //$NON-NLS-2$
6768
isNonRenderingWhereClauseAllowed = Boolean.parseBoolean(value);
69+
70+
value = properties.getProperty("emptyListConditionRenderingAllowed", "false"); //$NON-NLS-1$ //$NON-NLS-2$
71+
isEmptyListConditionRenderingAllowed = Boolean.parseBoolean(value);
6872
}
6973

7074
public boolean isIsNonRenderingWhereClauseAllowed() {
7175
return isNonRenderingWhereClauseAllowed;
7276
}
77+
78+
public boolean isEmptyListConditionRenderingAllowed() {
79+
return isEmptyListConditionRenderingAllowed;
80+
}
7381
}

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@
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>
2835
* <dt>nonRenderingWhereClauseAllowed</dt>
2936
* <dd>If false (default), the framework will throw a {@link NonRenderingWhereClauseException}
3037
* if a where clause is specified in the statement, but it fails to render because all
@@ -44,11 +51,24 @@ public class StatementConfiguration {
4451
private boolean isNonRenderingWhereClauseAllowed =
4552
GlobalContext.getConfiguration().isIsNonRenderingWhereClauseAllowed();
4653

54+
private boolean isEmptyListConditionRenderingAllowed =
55+
GlobalContext.getConfiguration().isEmptyListConditionRenderingAllowed();
56+
4757
public boolean isNonRenderingWhereClauseAllowed() {
4858
return isNonRenderingWhereClauseAllowed;
4959
}
5060

51-
public void setNonRenderingWhereClauseAllowed(boolean nonRenderingWhereClauseAllowed) {
52-
this.isNonRenderingWhereClauseAllowed = nonRenderingWhereClauseAllowed;
61+
public StatementConfiguration setNonRenderingWhereClauseAllowed(boolean nonRenderingWhereClauseAllowed) {
62+
isNonRenderingWhereClauseAllowed = nonRenderingWhereClauseAllowed;
63+
return this;
64+
}
65+
66+
public boolean isEmptyListConditionRenderingAllowed() {
67+
return isEmptyListConditionRenderingAllowed;
68+
}
69+
70+
public StatementConfiguration setEmptyListConditionRenderingAllowed(boolean emptyListConditionRenderingAllowed) {
71+
isEmptyListConditionRenderingAllowed = emptyListConditionRenderingAllowed;
72+
return this;
5373
}
5474
}

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

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

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

src/site/markdown/docs/configuration.md

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,6 @@ The library can be configured globally - which will change the behavior for all
77
can be configured. There are sensible defaults for all configuration values, so configuration is not strictly necessary.
88
If you want to change any of the default behaviors of the library, then the information on this page will help.
99

10-
Prior to version 1.4.1 of the library, there was no configurable behavior in the library. In version 1.4.1 we changed
11-
the default behavior of the library to throw an exception if a where clause fails to render. We did this out of an
12-
abundance of caution because the optional conditions in a where clause could lead to a statement that affected all
13-
rows in a table (for example, a delete statement could inadvertently delete all rows in a table). If you want the library
14-
to function as it did before version 1.4.1 , then you can change the global configuration as shown below.
15-
1610
## Global Configuration
1711

1812
On first use the library will initialize the global configuration. The global configuration can be specified via a property
@@ -24,9 +18,10 @@ The configuration file is a standard Java properties file. The possible values a
2418

2519
## Global Configuration Properties
2620

27-
| Property | Default | Meaning |
28-
|--------------------------------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
29-
| nonRenderingWhereClauseAllowed | false | If a where clause is specified, but fails to render, then the library will throw a `NonRenderingWhereClauseException` by default. If you set this value to true, then no exception will be thrown. This could enable statements to be rendered without where clauses that affect all rows in a table. |
21+
| Property | Default | Available in Version | Meaning |
22+
|------------------------------------|---------|----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
23+
| emptyListConditionRenderingAllowed | false | 1.5.1+ | If a list condition ("in", "not in", etc.) has an empty list - either though its initial values, or through filtering, the condition will be dropped from the where clause by default. If you set this value to true, then the condition will render even though the resulting SQL will be invalid. This will likely cause an SQLException at runtime, but it could be viewed as a protective measure so that statements do not effect more rows than desired. |
24+
| nonRenderingWhereClauseAllowed | false | 1.4.1+ | If a where clause is specified, but fails to render, then the library will throw a `NonRenderingWhereClauseException` by default. If you set this value to true, then no exception will be thrown. This could enable statements to be rendered without where clauses that affect all rows in a table. |
3025

3126
## Statement Configuration
3227

src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperTest.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -905,4 +905,17 @@ class PersonMapperTest {
905905
"select id, first_name, last_name, birth_date, employed, occupation, address_id from Person"
906906
assertThat(insertStatement.insertStatement).isEqualTo(expected)
907907
}
908+
909+
@Test
910+
fun testRenderingEmptyList() {
911+
val selectStatement = select(id, firstName, lastName, birthDate, employed, occupation, addressId) {
912+
from(person)
913+
where { id isIn emptyList() }
914+
configureStatement { isEmptyListConditionRenderingAllowed = true }
915+
}
916+
917+
val expected = "select id, first_name, last_name, birth_date, employed, occupation, address_id from Person " +
918+
"where id in ()"
919+
assertThat(selectStatement.selectStatement).isEqualTo(expected)
920+
}
908921
}

0 commit comments

Comments
 (0)