Skip to content

Commit 72a21ea

Browse files
authored
Merge pull request #1062 from jeffgbutler/remove-generics2
Significant Refactoring of the DSLs That Support Where Clauses
2 parents 5df2298 + 3368b2e commit 72a21ea

97 files changed

Lines changed: 6179 additions & 1455 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

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

55
## Release 2.0.0 - Unreleased
66

7-
Release 2.0.0 is a significant milestone for the library. We have moved to Java 17 as the minimum version supported. If
8-
you are unable to move to this version of Java then the releases in the 1.x line can be used with Java 8.
7+
Release 2.0.0 is a significant milestone for the library with many enhancements and changes. We have moved to Java 17
8+
as the minimum version supported. If you are unable to move to this version of Java then the releases in the 1.x line
9+
can be used with Java 8.
910

10-
In addition, we have taken the opportunity to make changes to the library that may break existing code. We have
11+
We have taken the opportunity to make changes to the library that may break existing code. We have
1112
worked to make these changes as minimal as possible.
1213

14+
Please read these release notes carefully as there are changes that may impact many users.
15+
16+
### DSL Updates and Migration Plan
17+
18+
With this release, I have started a long-term plan to update the DSLs that have "where" clauses – namely, CountDSL,
19+
DeleteDSL, SelectDSL, and UpdateDSL. All of these DSLs are generic and allow you to specify a function
20+
that can modify the model created when invoking `build()`. I now view this as a design flaw that provides very
21+
little benefit and makes direct use of a DSL more complex. For example, direct usage of a DSL often requires declaring
22+
a variable like `DeleteDSL<DeleteModel>` instead of simply `DeleteDSL`. This gets worse when you have more complex
23+
situations involving DSL inner classes.
24+
25+
Another issue is the tight coupling between `SelectDSL` and `QueryExpressionDSL`. It is confusing to have the `select`
26+
method actually return `QueryExpressionDSL<SelectModel>`.
27+
28+
So this release includes new, non-generic, versions of the DSLs in a new package structure. The new DSLs will live
29+
side by side with the old DSLs for now to maintain backwards compatibility. Eventually, the old DSls will be removed.
30+
31+
Here is the migration plan:
32+
33+
1. In this release I have updated the Kotlin support to use the new DSLs under the covers. This should be completely
34+
transparent to Kotlin users as all the prior complexity was hidden by the Kotlin DSLs.
35+
2. After the 2.0.0 release of this library, I will update MyBatis Generator so that it creates code based on the new DSL
36+
versions.
37+
3. In the next minor release of this library, I will deprecate all the old DSLs. I will also change the methods in
38+
`SqlBuilder` to use the new DSLs. In many cases you will be able to update to the new DSL by simply changing a
39+
package name.
40+
4. In the next major release of this library, I will delete the old DSLs.
41+
42+
I expect these next releases to be a relatively fast follow after the 2.0.0 release.
43+
44+
This table shows the old and new versions of the DSLs:
45+
46+
| New DSL | Old DSL (Will Be Removed Eventually) |
47+
|-----------------------------------------|----------------------------------------------------------------------------------------------------|
48+
| `org.mybatis.dynamic.sql.dsl.CountDSL` | `org.mybatis.dynamic.sql.select.CountDSL` |
49+
| `org.mybatis.dynamic.sql.dsl.DeleteDSL` | `org.mybatis.dynamic.sql.delete.DeleteDSL` |
50+
| `org.mybatis.dynamic.sql.dsl.SelectDSL` | `org.mybatis.dynamic.sql.select.SelectDSL` and `org.mybatis.dynamic.sql.select.QueryExpressionDSL` |
51+
| `org.mybatis.dynamic.sql.dsl.UpdateDSL` | `org.mybatis.dynamic.sql.update.UpdateDSL` |
52+
53+
The "completer" classes are also impacted and will need to change. This table shows the old and new versions:
54+
55+
| New Completer | Old Completer (Will Be Removed Eventually) |
56+
|--------------------------------------------------|-----------------------------------------------------|
57+
| `org.mybatis.dynamic.sql.dsl.CountDSLCompleter` | `org.mybatis.dynamic.sql.select.CountDSLCompleter` |
58+
| `org.mybatis.dynamic.sql.dsl.DeleteDSLCompleter` | `org.mybatis.dynamic.sql.delete.DeleteDSLCompleter` |
59+
| `org.mybatis.dynamic.sql.dsl.SelectDSLCompleter` | `org.mybatis.dynamic.sql.select.SelectDSLCompleter` |
60+
| `org.mybatis.dynamic.sql.dsl.UpdateDSLCompleter` | `org.mybatis.dynamic.sql.update.UpdateDSLCompleter` |
61+
62+
If you are actually using the generic function in the old DSLs to alter the model class produced by a DSL, then you can
63+
replace it with the new `map` method on all affected model classes to achieve the same result.
64+
65+
If you are using the DSLs directly (for example, not with code generated by MyBatis Generator), I encourage you to
66+
update your code to use the new DSLs now. As always, if you experience any difficulties with this new version of the
67+
library or in migrating to the new DSLs, please let us know by opening an issue on GitHub.
68+
69+
### Other Important Changes:
70+
71+
- The library now requires Java 17
72+
- Deprecated code from prior releases is removed
73+
- We now allow CASE expressions in ORDER BY Clauses
74+
- The "In" conditions will now throw `InvalidSqlException` during rendering if the list of values is empty. Previously
75+
an empty In condition would render as invalid SQL and would usually cause a runtime exception from the database.
76+
With this change, the exception thrown is more predictable and the error is caught before sending the SQL to the
77+
database.
78+
- All the paging methods (limit, offset, fetchFirst) now have "WhenPresent" variations that will drop the phrase from
79+
rendering if a null value is passed in
80+
- The JOIN syntax is updated and now allows full boolean expressions like a WHERE clause. The prior JOIN syntax
81+
is deprecated and will be removed in a future release.
82+
- Add support for locking options in select statements (for update, for share, etc.) This is not an abstraction of
83+
these concepts for different databases it simply adds known clauses to a generated SQL statement. You should always
84+
test to make sure these functions work in your target database. Currently, we support and test the options
85+
supported by PostgreSQL.
86+
- Rendering for all the conditions (isEqualTo, etc.) has changed. This should be transparent to most users unless you
87+
have coded a direct implementation of `VisitableCondition`. The change makes it easier to code custom conditions that
88+
are not supported by the library out of the box. The statement renderers now call methods `renderCondition` and
89+
`renderLeftColumn` that you can override to implement any rendering you need. In addition, we've made `filter` and
90+
`map` support optional if you implement custom conditions
91+
- Added support for configuring a Java property name to be associated with an `SqlColumn`. This property name can be
92+
used with the record-based insert methods to reduce the boilerplate code for mapping columns to Java properties.
93+
1394
### Potentially Breaking Changes:
1495

1596
- If you use this library with MyBatis' Spring Batch integration, you will need to make changes as we have
@@ -21,24 +102,24 @@ worked to make these changes as minimal as possible.
21102
are removed in favor of a new method `renderForOrderBy`
22103
- If you have implemented any custom functions, you will likely need to make changes. The supplied base classes now
23104
hold an instance of `BasicColumn` rather than `BindableColumn`. This change was made to make the functions more
24-
useful in variety of circumstances. If you follow the patterns shown on the
105+
useful in a variety of circumstances. If you follow the patterns shown on the
25106
[Extending the Library](https://mybatis.org/mybatis-dynamic-sql/docs/extending.html) page, the change should be
26107
limited to changing the private constructor to accept `BasicColumn` rather than `BindableColumn`.
27108

28109
### Adoption of JSpecify (https://jspecify.dev/)
29110

30111
Following the lead of many other projects (including The Spring Framework), we have adopted JSpecify to fully
31-
document the null handling properties of this library. JSpecify is now a runtime dependency - as is
112+
document the null handling properties of this library. JSpecify is now a runtime dependency as is
32113
recommended practice with JSpecify.
33114

34-
This change should not impact the running of any existing code, but depending on your usage you may see new IDE or
115+
This change should not impact the running of any existing code, but depending on your usage, you may see new IDE or
35116
tooling warnings based on the declared nullability of methods in the library. You may choose to ignore the
36-
warnings and things should continue to function. Of course, we recommend that you do not ignore these warnings!
117+
warnings, and things should continue to function. Of course, we recommend that you do not ignore these warnings!
37118

38119
In general, the library does not expect that you will pass a null value into any method. There are two exceptions to
39120
this rule:
40121

41-
1. Some builder methods will accept a null value if the target object will properly handle null values through the
122+
1. Some builder methods will accept a null value if the target object properly handles null values through the
42123
use of java.util.Optional
43124
2. Methods with names that include "WhenPresent" will properly handle null parameters
44125
(for example, "isEqualToWhenPresent")
@@ -49,7 +130,7 @@ Fixing compiler warnings and errors:
49130

50131
1. We expect that most of the warnings you encounter will be related to passing null values into a where condition.
51132
These warnings should be resolved by changing your code to use the "WhenPresent" versions of methods as those
52-
methods handle null values in a predictable way.
133+
methods handle null values predictably.
53134
2. Java Classes that extend "AliasableSqlTable" will likely see IDE warnings about non-null type arguments. This can be
54135
resolved by adding a "@NullMarked" annotation to the class or package. This issue does not affect Kotlin classes
55136
that extend "AliasableSqlTable".
@@ -59,57 +140,28 @@ Fixing compiler warnings and errors:
59140
change the type parameter definition to specify a non-nullable type. For example...
60141

61142
```kotlin
62-
import org.mybatis.dynamic.sql.SqlColumn
63-
64143
fun <T> foo(column: SqlColumn<T>) {
65144
}
66145
```
67146

68147
Should change to:
69148

70149
```kotlin
71-
import org.mybatis.dynamic.sql.SqlColumn
72-
73150
fun <T : Any> foo(column: SqlColumn<T>) {
74151
}
75152
```
76153

77154
Runtime behavior changes:
78155

79-
1. The where conditions (isEqualTo, isLessThan, etc.) can be filtered and result in an "empty" condition -
80-
similar to java.util.Optional. Previously, calling a "value" method of the condition would return null. Now
81-
those methods will throw "NoSuchElementException". This should not impact you in normal usage.
156+
1. The where conditions (isEqualTo, isLessThan, etc.) can be filtered and result in an "empty" condition
157+
similar to `java.util.Optional`. Previously, calling a "value" method of the condition would return null. Now
158+
those methods will throw `java.util.NoSuchElementException`. This should not impact you in normal usage.
82159
2. We have updated the "ParameterTypeConverter" used in Spring applications to maintain compatibility with Spring's
83160
"Converter" interface. The primary change is that the framework will no longer call a type converter if the
84161
input value is null. This should simplify the coding of converters and foster reuse with existing Spring converters.
85162
3. The "map" method on the "WhenPresent" conditions will accept a mapper function that may return a null value. The
86163
conditions will now properly handle this outcome
87164

88-
### Other important changes:
89-
90-
- The library now requires Java 17
91-
- Deprecated code from prior releases is removed
92-
- We now allow CASE expressions in ORDER BY Clauses
93-
- The "In" conditions will now throw `InvalidSqlException` during rendering if the list of values is empty. Previously
94-
an empty In condition would render as invalid SQL and would usually cause a runtime exception from the database.
95-
With this change, the exception thrown is more predictable and the error is caught before sending the SQL to the
96-
database.
97-
- All the paging methods (limit, offset, fetchFirst) now have "WhenPresent" variations that will drop the phrase from
98-
rendering if a null value is passed in
99-
- The JOIN syntax is updated and now allows full boolean expressions like a WHERE clause. The prior JOIN syntax
100-
is deprecated and will be removed in a future release.
101-
- Add support for locking options in select statements (for update, for share, etc.) This is not an abstraction of
102-
these concepts for different databases it simply adds known clauses to a generated SQL statement. You should always
103-
test to make sure these functions work in your target database. Currently, we support, and test, the options
104-
supported by PostgreSQL.
105-
- Rendering for all the conditions (isEqualTo, etc.) has changed. This should be transparent to most users unless you
106-
have coded a direct implementation of `VisitableCondition`. The change makes it easier to code custom conditions that
107-
are not supported by the library out of the box. The statement renderers now call methods `renderCondition` and
108-
`renderLeftColumn` that you can override to implement any rendering you need. In addition, we've made `filter` and
109-
`map` support optional if you implement custom conditions
110-
- Added support for configuring a Java property name to be associated with an `SqlColumn`. This property name can be
111-
used with the record based insert methods to reduce the boilerplate code for mapping columns to Java properties.
112-
113165
## Release 1.5.2 - June 3, 2024
114166

115167
This is a small maintenance release with the following changes:

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

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2025 the original author or authors.
2+
* Copyright 2016-2026 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,7 +19,6 @@
1919
import java.util.Collections;
2020
import java.util.List;
2121
import java.util.Objects;
22-
import java.util.Optional;
2322

2423
import org.jspecify.annotations.Nullable;
2524

@@ -34,21 +33,21 @@
3433
*/
3534
public class AndOrCriteriaGroup {
3635
private final String connector;
37-
private final @Nullable SqlCriterion initialCriterion;
36+
private final SqlCriterion initialCriterion;
3837
private final List<AndOrCriteriaGroup> subCriteria;
3938

4039
private AndOrCriteriaGroup(Builder builder) {
4140
connector = Objects.requireNonNull(builder.connector);
42-
initialCriterion = builder.initialCriterion;
41+
initialCriterion = Objects.requireNonNull(builder.initialCriterion);
4342
subCriteria = builder.subCriteria;
4443
}
4544

4645
public String connector() {
4746
return connector;
4847
}
4948

50-
public Optional<SqlCriterion> initialCriterion() {
51-
return Optional.ofNullable(initialCriterion);
49+
public SqlCriterion initialCriterion() {
50+
return initialCriterion;
5251
}
5352

5453
public List<AndOrCriteriaGroup> subCriteria() {
@@ -65,7 +64,7 @@ public Builder withConnector(String connector) {
6564
return this;
6665
}
6766

68-
public Builder withInitialCriterion(@Nullable SqlCriterion initialCriterion) {
67+
public Builder withInitialCriterion(SqlCriterion initialCriterion) {
6968
this.initialCriterion = initialCriterion;
7069
return this;
7170
}

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

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

18-
import java.util.Optional;
18+
import java.util.Objects;
1919

2020
import org.jspecify.annotations.Nullable;
2121

@@ -29,15 +29,15 @@
2929
* @since 1.4.0
3030
*/
3131
public class CriteriaGroup extends SqlCriterion {
32-
private final @Nullable SqlCriterion initialCriterion;
32+
private final SqlCriterion initialCriterion;
3333

3434
protected CriteriaGroup(AbstractGroupBuilder<?> builder) {
3535
super(builder);
36-
initialCriterion = builder.initialCriterion;
36+
initialCriterion = Objects.requireNonNull(builder.initialCriterion);
3737
}
3838

39-
public Optional<SqlCriterion> initialCriterion() {
40-
return Optional.ofNullable(initialCriterion);
39+
public SqlCriterion initialCriterion() {
40+
return initialCriterion;
4141
}
4242

4343
@Override
@@ -48,7 +48,7 @@ public <R> R accept(SqlCriterionVisitor<R> visitor) {
4848
public abstract static class AbstractGroupBuilder<T extends AbstractGroupBuilder<T>> extends AbstractBuilder<T> {
4949
private @Nullable SqlCriterion initialCriterion;
5050

51-
public T withInitialCriterion(@Nullable SqlCriterion initialCriterion) {
51+
public T withInitialCriterion(SqlCriterion initialCriterion) {
5252
this.initialCriterion = initialCriterion;
5353
return getThis();
5454
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2016-2026 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;
17+
18+
/**
19+
* A simple SqlCriterion that does nothing and will not render. This is to solve the problem of
20+
* awkward nullability operations in various classes. We can now say that an initial criterion is never null.
21+
*/
22+
public class NullCriterion extends SqlCriterion {
23+
@Override
24+
public <R> R accept(SqlCriterionVisitor<R> visitor) {
25+
return visitor.visit(this);
26+
}
27+
}

0 commit comments

Comments
 (0)