Skip to content

Commit 697b1dc

Browse files
authored
Merge pull request #890 from jeffgbutler/jspecify
Adopt JSpecify
2 parents fb789ac + 4f81c07 commit 697b1dc

File tree

180 files changed

+1459
-710
lines changed

Some content is hidden

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

180 files changed

+1459
-710
lines changed

CHANGELOG.md

+60-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ you are unable to move to this version of Java then the releases in the 1.x line
1010
In addition, we have taken the opportunity to make changes to the library that may break existing code. We have
1111
worked to make these changes as minimal as possible.
1212

13-
**Potentially Breaking Changes:**
13+
### Potentially Breaking Changes:
1414

1515
- If you use this library with MyBatis' Spring Batch integration, you will need to make changes as we have
1616
refactored that support to be more flexible. Please see the
@@ -25,7 +25,65 @@ worked to make these changes as minimal as possible.
2525
[Extending the Library](https://mybatis.org/mybatis-dynamic-sql/docs/extending.html) page, the change should be
2626
limited to changing the private constructor to accept `BasicColumn` rather than `BindableColumn`.
2727

28-
Other important changes:
28+
### Adoption of JSpecify (https://jspecify.dev/)
29+
30+
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
32+
recommended practice with JSpecify.
33+
34+
This change should not impact the running of any existing code, but depending on your usage you may see new IDE or
35+
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!
37+
38+
In general, the library does not expect that you will pass a null value into any method. There are two exceptions to
39+
this rule:
40+
41+
1. Some builder methods will accept a null value if the target object will properly handle null values through the
42+
use of java.util.Optional
43+
2. Methods with names that include "WhenPresent" will properly handle null parameters
44+
(for example, "isEqualToWhenPresent")
45+
46+
As you might expect, standardizing null handling revealed some issues in the library that may impact you.
47+
48+
Fixing compiler warnings and errors:
49+
50+
1. We expect that most of the warnings you encounter will be related to passing null values into a where condition.
51+
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.
53+
2. Java Classes that extend "AliasableSqlTable" will likely see IDE warnings about non-null type arguments. This can be
54+
resolved by adding a "@NullMarked" annotation to the class or package. This issue does not affect Kotlin classes
55+
that extend "AliasableSqlTable".
56+
3. Similarly, if you have coded any functions for use with your queries, you can resolve most IDE warnings by adding
57+
the "@NullMarked" annotation.
58+
4. If you have coded any Kotlin functions that operate on a generic Java class from the library, then you should
59+
change the type parameter definition to specify a non-nullable type. For example...
60+
61+
```kotlin
62+
import org.mybatis.dynamic.sql.SqlColumn
63+
64+
fun <T> foo(column: SqlColumn<T>) {
65+
}
66+
```
67+
68+
Should change to:
69+
70+
```kotlin
71+
import org.mybatis.dynamic.sql.SqlColumn
72+
73+
fun <T : Any> foo(column: SqlColumn<T>) {
74+
}
75+
```
76+
77+
Runtime behavior changes:
78+
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.
82+
2. We have updated the "ParameterTypeConverter" used in Spring applications to maintain compatibility with Spring's
83+
"Converter" interface. The primary change is that the framework will no longer call a type converter if the
84+
input value is null. This should simplify the coding of converters and foster reuse with existing Spring converters.
85+
86+
### Other important changes:
2987

3088
- The library now requires Java 17
3189
- Deprecated code from prior releases is removed

README.md

+6-2
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ The library test cases provide several complete examples of using the library in
7878
| Kotlin | Spring JDBC | Example using Kotlin utility classes for Spring JDBC Template | [../examples/kotlin/spring/canonical](src/test/kotlin/examples/kotlin/spring/canonical) |
7979

8080

81-
## Requirements
81+
## Requirements and Dependencies
8282

83-
The library has no dependencies. Version 2.x requires Java 17. Version 1.x requires Java 8.
83+
Version 2.x requires Java 17 and has a required runtime dependency on JSpecify (https://jspecify.dev/). Version 1.x
84+
requires Java 8 and has no required runtime dependencies.
85+
86+
All versions have support for MyBatis3, Spring Framework, and Kotlin - all those dependencies are optional. The library
87+
should work in those environments as the dependencies will be made available at runtime.

pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@
8686
</properties>
8787

8888
<dependencies>
89+
<dependency>
90+
<groupId>org.jspecify</groupId>
91+
<artifactId>jspecify</artifactId>
92+
<version>1.0.0</version>
93+
</dependency>
8994
<dependency>
9095
<groupId>org.jetbrains.kotlin</groupId>
9196
<artifactId>kotlin-stdlib-jdk8</artifactId>

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919
import java.util.Optional;
2020
import java.util.function.Supplier;
2121

22+
import org.jspecify.annotations.Nullable;
23+
2224
public abstract class AliasableSqlTable<T extends AliasableSqlTable<T>> extends SqlTable {
2325

24-
private String tableAlias;
26+
private @Nullable String tableAlias;
2527
private final Supplier<T> constructor;
2628

2729
protected AliasableSqlTable(String tableName, Supplier<T> constructor) {

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

+6-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import java.util.Objects;
2222
import java.util.Optional;
2323

24+
import org.jspecify.annotations.Nullable;
25+
2426
/**
2527
* This class represents a criteria group with either an AND or an OR connector.
2628
* This class is intentionally NOT derived from SqlCriterion because we only want it to be
@@ -32,7 +34,7 @@
3234
*/
3335
public class AndOrCriteriaGroup {
3436
private final String connector;
35-
private final SqlCriterion initialCriterion;
37+
private final @Nullable SqlCriterion initialCriterion;
3638
private final List<AndOrCriteriaGroup> subCriteria;
3739

3840
private AndOrCriteriaGroup(Builder builder) {
@@ -54,16 +56,16 @@ public List<AndOrCriteriaGroup> subCriteria() {
5456
}
5557

5658
public static class Builder {
57-
private String connector;
58-
private SqlCriterion initialCriterion;
59+
private @Nullable String connector;
60+
private @Nullable SqlCriterion initialCriterion;
5961
private final List<AndOrCriteriaGroup> subCriteria = new ArrayList<>();
6062

6163
public Builder withConnector(String connector) {
6264
this.connector = connector;
6365
return this;
6466
}
6567

66-
public Builder withInitialCriterion(SqlCriterion initialCriterion) {
68+
public Builder withInitialCriterion(@Nullable SqlCriterion initialCriterion) {
6769
this.initialCriterion = initialCriterion;
6870
return this;
6971
}

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
import java.util.Optional;
1919

20+
import org.jspecify.annotations.Nullable;
21+
2022
/**
2123
* Describes a column with a known data type. The type is only used by the compiler to assure type safety
2224
* when building clauses with conditions.
@@ -34,7 +36,7 @@ public interface BindableColumn<T> extends BasicColumn {
3436
@Override
3537
BindableColumn<T> as(String alias);
3638

37-
default Object convertParameterType(T value) {
39+
default @Nullable Object convertParameterType(T value) {
3840
return value;
3941
}
4042

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
import java.util.Objects;
1919

20+
import org.jspecify.annotations.Nullable;
21+
2022
public class ColumnAndConditionCriterion<T> extends SqlCriterion {
2123
private final BindableColumn<T> column;
2224
private final VisitableCondition<T> condition;
@@ -45,8 +47,8 @@ public static <T> Builder<T> withColumn(BindableColumn<T> column) {
4547
}
4648

4749
public static class Builder<T> extends AbstractBuilder<Builder<T>> {
48-
private BindableColumn<T> column;
49-
private VisitableCondition<T> condition;
50+
private @Nullable BindableColumn<T> column;
51+
private @Nullable VisitableCondition<T> condition;
5052

5153
public Builder<T> withColumn(BindableColumn<T> column) {
5254
this.column = column;

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,20 @@
1818
import java.util.Objects;
1919
import java.util.Optional;
2020

21+
import org.jspecify.annotations.Nullable;
2122
import org.mybatis.dynamic.sql.render.RenderingContext;
2223
import org.mybatis.dynamic.sql.util.FragmentAndParameters;
2324

2425
public class Constant<T> implements BindableColumn<T> {
2526

26-
private final String alias;
27+
private final @Nullable String alias;
2728
private final String value;
2829

2930
private Constant(String value) {
3031
this(value, null);
3132
}
3233

33-
private Constant(String value, String alias) {
34+
private Constant(String value, @Nullable String alias) {
3435
this.value = Objects.requireNonNull(value);
3536
this.alias = alias;
3637
}

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

+5-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
import java.util.Optional;
1919

20+
import org.jspecify.annotations.Nullable;
21+
2022
/**
2123
* This class represents a criteria group without an AND or an OR connector. This is useful
2224
* in situations where the initial SqlCriterion in a list should be further grouped
@@ -27,7 +29,7 @@
2729
* @since 1.4.0
2830
*/
2931
public class CriteriaGroup extends SqlCriterion {
30-
private final SqlCriterion initialCriterion;
32+
private final @Nullable SqlCriterion initialCriterion;
3133

3234
protected CriteriaGroup(AbstractGroupBuilder<?> builder) {
3335
super(builder);
@@ -44,9 +46,9 @@ public <R> R accept(SqlCriterionVisitor<R> visitor) {
4446
}
4547

4648
public abstract static class AbstractGroupBuilder<T extends AbstractGroupBuilder<T>> extends AbstractBuilder<T> {
47-
private SqlCriterion initialCriterion;
49+
private @Nullable SqlCriterion initialCriterion;
4850

49-
public T withInitialCriterion(SqlCriterion initialCriterion) {
51+
public T withInitialCriterion(@Nullable SqlCriterion initialCriterion) {
5052
this.initialCriterion = initialCriterion;
5153
return getThis();
5254
}

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

+13-12
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.Objects;
2020
import java.util.Optional;
2121

22+
import org.jspecify.annotations.Nullable;
2223
import org.mybatis.dynamic.sql.render.RenderingContext;
2324
import org.mybatis.dynamic.sql.util.FragmentAndParameters;
2425

@@ -34,10 +35,10 @@
3435
*/
3536
public class DerivedColumn<T> implements BindableColumn<T> {
3637
private final String name;
37-
private final String tableQualifier;
38-
private final String columnAlias;
39-
private final JDBCType jdbcType;
40-
private final String typeHandler;
38+
private final @Nullable String tableQualifier;
39+
private final @Nullable String columnAlias;
40+
private final @Nullable JDBCType jdbcType;
41+
private final @Nullable String typeHandler;
4142

4243
protected DerivedColumn(Builder<T> builder) {
4344
this.name = Objects.requireNonNull(builder.name);
@@ -93,18 +94,18 @@ public static <T> DerivedColumn<T> of(String name, String tableQualifier) {
9394
}
9495

9596
public static class Builder<T> {
96-
private String name;
97-
private String tableQualifier;
98-
private String columnAlias;
99-
private JDBCType jdbcType;
100-
private String typeHandler;
97+
private @Nullable String name;
98+
private @Nullable String tableQualifier;
99+
private @Nullable String columnAlias;
100+
private @Nullable JDBCType jdbcType;
101+
private @Nullable String typeHandler;
101102

102103
public Builder<T> withName(String name) {
103104
this.name = name;
104105
return this;
105106
}
106107

107-
public Builder<T> withTableQualifier(String tableQualifier) {
108+
public Builder<T> withTableQualifier(@Nullable String tableQualifier) {
108109
this.tableQualifier = tableQualifier;
109110
return this;
110111
}
@@ -114,12 +115,12 @@ public Builder<T> withColumnAlias(String columnAlias) {
114115
return this;
115116
}
116117

117-
public Builder<T> withJdbcType(JDBCType jdbcType) {
118+
public Builder<T> withJdbcType(@Nullable JDBCType jdbcType) {
118119
this.jdbcType = jdbcType;
119120
return this;
120121
}
121122

122-
public Builder<T> withTypeHandler(String typeHandler) {
123+
public Builder<T> withTypeHandler(@Nullable String typeHandler) {
123124
this.typeHandler = typeHandler;
124125
return this;
125126
}

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
import java.util.Objects;
1919

20+
import org.jspecify.annotations.Nullable;
21+
2022
public class ExistsCriterion extends SqlCriterion {
2123
private final ExistsPredicate existsPredicate;
2224

@@ -35,7 +37,7 @@ public <R> R accept(SqlCriterionVisitor<R> visitor) {
3537
}
3638

3739
public static class Builder extends AbstractBuilder<Builder> {
38-
private ExistsPredicate existsPredicate;
40+
private @Nullable ExistsPredicate existsPredicate;
3941

4042
public Builder withExistsPredicate(ExistsPredicate existsPredicate) {
4143
this.existsPredicate = existsPredicate;

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

-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
import java.util.Objects;
1919

20-
import org.jetbrains.annotations.NotNull;
2120
import org.mybatis.dynamic.sql.select.SelectModel;
2221
import org.mybatis.dynamic.sql.util.Buildable;
2322

@@ -38,12 +37,10 @@ public Buildable<SelectModel> selectModelBuilder() {
3837
return selectModelBuilder;
3938
}
4039

41-
@NotNull
4240
public static ExistsPredicate exists(Buildable<SelectModel> selectModelBuilder) {
4341
return new ExistsPredicate("exists", selectModelBuilder); //$NON-NLS-1$
4442
}
4543

46-
@NotNull
4744
public static ExistsPredicate notExists(Buildable<SelectModel> selectModelBuilder) {
4845
return new ExistsPredicate("not exists", selectModelBuilder); //$NON-NLS-1$
4946
}

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

+11-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.mybatis.dynamic.sql;
1717

18+
import org.jspecify.annotations.Nullable;
19+
1820
/**
1921
* A parameter type converter is used to change a parameter value from one type to another
2022
* during statement rendering and before the parameter is placed into the parameter map. This can be used
@@ -50,5 +52,13 @@
5052
*/
5153
@FunctionalInterface
5254
public interface ParameterTypeConverter<S, T> {
53-
T convert(S source);
55+
/**
56+
* Convert the value from one value to another.
57+
*
58+
* <p>The input value will never be null - the framework will automatically handle nulls.
59+
*
60+
* @param source value as specified in the condition, or after a map operation. Never null.
61+
* @return Possibly null converted value.
62+
*/
63+
@Nullable T convert(S source);
5464
}

0 commit comments

Comments
 (0)