Skip to content

Commit 450526f

Browse files
committed
Single value conditions do not expect null values
1 parent 16c3880 commit 450526f

17 files changed

+246
-158
lines changed

CHANGELOG.md

Lines changed: 22 additions & 2 deletions
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,27 @@ 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+
specify the null handling properties of this library. In general, the library does not expect that you will pass a null
32+
value into any method. There are two exceptions to this rule:
33+
34+
1. Some builder methods will accept a null value if the target object will properly handle null values through the
35+
use of java.util.Optional
36+
2. Methods with names that include "WhenPresent" will properly handle null parameters
37+
(for example, "isEqualToWhenPresent")
38+
39+
As you might expect, standardizing null handling revealed some issues in the library that may impact you:
40+
41+
Case-insensitive conditions like "isLikeCaseInsensitive" will now throw a NullPointerException if you pass in null
42+
values.
43+
44+
The where conditions (isEqualTo, isLessThan, etc.) can be filtered and result in an "empty" condition -
45+
similar to java.util.Optional. Previously, calling a "value" method of the condition would return null. Now
46+
those methods will throw "NoSuchElementException". This should not impact you in normal usage.
47+
48+
### Other important changes:
2949

3050
- The library now requires Java 17
3151
- Deprecated code from prior releases is removed

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,14 @@
1919
import java.util.function.Predicate;
2020
import java.util.function.Supplier;
2121

22-
import org.jspecify.annotations.Nullable;
23-
2422
public abstract class AbstractSingleValueCondition<T> implements VisitableCondition<T> {
25-
protected final @Nullable T value;
23+
protected final T value;
2624

27-
protected AbstractSingleValueCondition(@Nullable T value) {
25+
protected AbstractSingleValueCondition(T value) {
2826
this.value = value;
2927
}
3028

31-
public @Nullable T value() {
29+
public T value() {
3230
return value;
3331
}
3432

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

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -618,11 +618,11 @@ static <T> IsNotNull<T> isNotNull() {
618618
return new IsNotNull<>();
619619
}
620620

621-
static <T> IsEqualTo<T> isEqualTo(@Nullable T value) {
621+
static <T> IsEqualTo<T> isEqualTo(T value) {
622622
return IsEqualTo.of(value);
623623
}
624624

625-
static <T> IsEqualTo<T> isEqualTo(Supplier<@Nullable T> valueSupplier) {
625+
static <T> IsEqualTo<T> isEqualTo(Supplier<T> valueSupplier) {
626626
return isEqualTo(valueSupplier.get());
627627
}
628628

@@ -635,18 +635,18 @@ static <T> IsEqualToColumn<T> isEqualTo(BasicColumn column) {
635635
}
636636

637637
static <T> IsEqualTo<T> isEqualToWhenPresent(@Nullable T value) {
638-
return IsEqualTo.of(value).filter(Objects::nonNull);
638+
return value == null ? IsEqualTo.empty() : IsEqualTo.of(value);
639639
}
640640

641641
static <T> IsEqualTo<T> isEqualToWhenPresent(Supplier<@Nullable T> valueSupplier) {
642642
return isEqualToWhenPresent(valueSupplier.get());
643643
}
644644

645-
static <T> IsNotEqualTo<T> isNotEqualTo(@Nullable T value) {
645+
static <T> IsNotEqualTo<T> isNotEqualTo(T value) {
646646
return IsNotEqualTo.of(value);
647647
}
648648

649-
static <T> IsNotEqualTo<T> isNotEqualTo(Supplier<@Nullable T> valueSupplier) {
649+
static <T> IsNotEqualTo<T> isNotEqualTo(Supplier<T> valueSupplier) {
650650
return isNotEqualTo(valueSupplier.get());
651651
}
652652

@@ -659,18 +659,18 @@ static <T> IsNotEqualToColumn<T> isNotEqualTo(BasicColumn column) {
659659
}
660660

661661
static <T> IsNotEqualTo<T> isNotEqualToWhenPresent(@Nullable T value) {
662-
return IsNotEqualTo.of(value).filter(Objects::nonNull);
662+
return value == null ? IsNotEqualTo.empty() : IsNotEqualTo.of(value);
663663
}
664664

665665
static <T> IsNotEqualTo<T> isNotEqualToWhenPresent(Supplier<@Nullable T> valueSupplier) {
666666
return isNotEqualToWhenPresent(valueSupplier.get());
667667
}
668668

669-
static <T> IsGreaterThan<T> isGreaterThan(@Nullable T value) {
669+
static <T> IsGreaterThan<T> isGreaterThan(T value) {
670670
return IsGreaterThan.of(value);
671671
}
672672

673-
static <T> IsGreaterThan<T> isGreaterThan(Supplier<@Nullable T> valueSupplier) {
673+
static <T> IsGreaterThan<T> isGreaterThan(Supplier<T> valueSupplier) {
674674
return isGreaterThan(valueSupplier.get());
675675
}
676676

@@ -683,18 +683,18 @@ static <T> IsGreaterThanColumn<T> isGreaterThan(BasicColumn column) {
683683
}
684684

685685
static <T> IsGreaterThan<T> isGreaterThanWhenPresent(@Nullable T value) {
686-
return IsGreaterThan.of(value).filter(Objects::nonNull);
686+
return value == null ? IsGreaterThan.empty() : IsGreaterThan.of(value);
687687
}
688688

689689
static <T> IsGreaterThan<T> isGreaterThanWhenPresent(Supplier<@Nullable T> valueSupplier) {
690690
return isGreaterThanWhenPresent(valueSupplier.get());
691691
}
692692

693-
static <T> IsGreaterThanOrEqualTo<T> isGreaterThanOrEqualTo(@Nullable T value) {
693+
static <T> IsGreaterThanOrEqualTo<T> isGreaterThanOrEqualTo(T value) {
694694
return IsGreaterThanOrEqualTo.of(value);
695695
}
696696

697-
static <T> IsGreaterThanOrEqualTo<T> isGreaterThanOrEqualTo(Supplier<@Nullable T> valueSupplier) {
697+
static <T> IsGreaterThanOrEqualTo<T> isGreaterThanOrEqualTo(Supplier<T> valueSupplier) {
698698
return isGreaterThanOrEqualTo(valueSupplier.get());
699699
}
700700

@@ -708,18 +708,18 @@ static <T> IsGreaterThanOrEqualToColumn<T> isGreaterThanOrEqualTo(BasicColumn co
708708
}
709709

710710
static <T> IsGreaterThanOrEqualTo<T> isGreaterThanOrEqualToWhenPresent(@Nullable T value) {
711-
return IsGreaterThanOrEqualTo.of(value).filter(Objects::nonNull);
711+
return value == null ? IsGreaterThanOrEqualTo.empty() : IsGreaterThanOrEqualTo.of(value);
712712
}
713713

714714
static <T> IsGreaterThanOrEqualTo<T> isGreaterThanOrEqualToWhenPresent(Supplier<@Nullable T> valueSupplier) {
715715
return isGreaterThanOrEqualToWhenPresent(valueSupplier.get());
716716
}
717717

718-
static <T> IsLessThan<T> isLessThan(@Nullable T value) {
718+
static <T> IsLessThan<T> isLessThan(T value) {
719719
return IsLessThan.of(value);
720720
}
721721

722-
static <T> IsLessThan<T> isLessThan(Supplier<@Nullable T> valueSupplier) {
722+
static <T> IsLessThan<T> isLessThan(Supplier<T> valueSupplier) {
723723
return isLessThan(valueSupplier.get());
724724
}
725725

@@ -732,18 +732,18 @@ static <T> IsLessThanColumn<T> isLessThan(BasicColumn column) {
732732
}
733733

734734
static <T> IsLessThan<T> isLessThanWhenPresent(@Nullable T value) {
735-
return IsLessThan.of(value).filter(Objects::nonNull);
735+
return value == null ? IsLessThan.empty() : IsLessThan.of(value);
736736
}
737737

738738
static <T> IsLessThan<T> isLessThanWhenPresent(Supplier<@Nullable T> valueSupplier) {
739739
return isLessThanWhenPresent(valueSupplier.get());
740740
}
741741

742-
static <T> IsLessThanOrEqualTo<T> isLessThanOrEqualTo(@Nullable T value) {
742+
static <T> IsLessThanOrEqualTo<T> isLessThanOrEqualTo(T value) {
743743
return IsLessThanOrEqualTo.of(value);
744744
}
745745

746-
static <T> IsLessThanOrEqualTo<T> isLessThanOrEqualTo(Supplier<@Nullable T> valueSupplier) {
746+
static <T> IsLessThanOrEqualTo<T> isLessThanOrEqualTo(Supplier<T> valueSupplier) {
747747
return isLessThanOrEqualTo(valueSupplier.get());
748748
}
749749

@@ -756,7 +756,7 @@ static <T> IsLessThanOrEqualToColumn<T> isLessThanOrEqualTo(BasicColumn column)
756756
}
757757

758758
static <T> IsLessThanOrEqualTo<T> isLessThanOrEqualToWhenPresent(@Nullable T value) {
759-
return IsLessThanOrEqualTo.of(value).filter(Objects::nonNull);
759+
return value == null ? IsLessThanOrEqualTo.empty() : IsLessThanOrEqualTo.of(value);
760760
}
761761

762762
static <T> IsLessThanOrEqualTo<T> isLessThanOrEqualToWhenPresent(Supplier<@Nullable T> valueSupplier) {
@@ -840,32 +840,32 @@ static <T> IsNotBetween.WhenPresentBuilder<T> isNotBetweenWhenPresent(Supplier<@
840840
}
841841

842842
// for string columns, but generic for columns with type handlers
843-
static <T> IsLike<T> isLike(@Nullable T value) {
843+
static <T> IsLike<T> isLike(T value) {
844844
return IsLike.of(value);
845845
}
846846

847-
static <T> IsLike<T> isLike(Supplier<@Nullable T> valueSupplier) {
847+
static <T> IsLike<T> isLike(Supplier<T> valueSupplier) {
848848
return isLike(valueSupplier.get());
849849
}
850850

851851
static <T> IsLike<T> isLikeWhenPresent(@Nullable T value) {
852-
return IsLike.of(value).filter(Objects::nonNull);
852+
return value == null ? IsLike.empty() : IsLike.of(value);
853853
}
854854

855855
static <T> IsLike<T> isLikeWhenPresent(Supplier<@Nullable T> valueSupplier) {
856856
return isLikeWhenPresent(valueSupplier.get());
857857
}
858858

859-
static <T> IsNotLike<T> isNotLike(@Nullable T value) {
859+
static <T> IsNotLike<T> isNotLike(T value) {
860860
return IsNotLike.of(value);
861861
}
862862

863-
static <T> IsNotLike<T> isNotLike(Supplier<@Nullable T> valueSupplier) {
863+
static <T> IsNotLike<T> isNotLike(Supplier<T> valueSupplier) {
864864
return isNotLike(valueSupplier.get());
865865
}
866866

867867
static <T> IsNotLike<T> isNotLikeWhenPresent(@Nullable T value) {
868-
return IsNotLike.of(value).filter(Objects::nonNull);
868+
return value == null ? IsNotLike.empty() : IsNotLike.of(value);
869869
}
870870

871871
static <T> IsNotLike<T> isNotLikeWhenPresent(Supplier<@Nullable T> valueSupplier) {
@@ -882,32 +882,32 @@ static IsEqualTo<Boolean> isFalse() {
882882
}
883883

884884
// conditions for strings only
885-
static IsLikeCaseInsensitive isLikeCaseInsensitive(@Nullable String value) {
885+
static IsLikeCaseInsensitive isLikeCaseInsensitive(String value) {
886886
return IsLikeCaseInsensitive.of(value);
887887
}
888888

889-
static IsLikeCaseInsensitive isLikeCaseInsensitive(Supplier<@Nullable String> valueSupplier) {
889+
static IsLikeCaseInsensitive isLikeCaseInsensitive(Supplier<String> valueSupplier) {
890890
return isLikeCaseInsensitive(valueSupplier.get());
891891
}
892892

893893
static IsLikeCaseInsensitive isLikeCaseInsensitiveWhenPresent(@Nullable String value) {
894-
return IsLikeCaseInsensitive.of(value).filter(Objects::nonNull);
894+
return value == null ? IsLikeCaseInsensitive.empty() : IsLikeCaseInsensitive.of(value);
895895
}
896896

897897
static IsLikeCaseInsensitive isLikeCaseInsensitiveWhenPresent(Supplier<@Nullable String> valueSupplier) {
898898
return isLikeCaseInsensitiveWhenPresent(valueSupplier.get());
899899
}
900900

901-
static IsNotLikeCaseInsensitive isNotLikeCaseInsensitive(@Nullable String value) {
901+
static IsNotLikeCaseInsensitive isNotLikeCaseInsensitive(String value) {
902902
return IsNotLikeCaseInsensitive.of(value);
903903
}
904904

905-
static IsNotLikeCaseInsensitive isNotLikeCaseInsensitive(Supplier<@Nullable String> valueSupplier) {
905+
static IsNotLikeCaseInsensitive isNotLikeCaseInsensitive(Supplier<String> valueSupplier) {
906906
return isNotLikeCaseInsensitive(valueSupplier.get());
907907
}
908908

909909
static IsNotLikeCaseInsensitive isNotLikeCaseInsensitiveWhenPresent(@Nullable String value) {
910-
return IsNotLikeCaseInsensitive.of(value).filter(Objects::nonNull);
910+
return value == null ? IsNotLikeCaseInsensitive.empty() : IsNotLikeCaseInsensitive.of(value);
911911
}
912912

913913
static IsNotLikeCaseInsensitive isNotLikeCaseInsensitiveWhenPresent(Supplier<@Nullable String> valueSupplier) {

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,20 @@
1515
*/
1616
package org.mybatis.dynamic.sql.where.condition;
1717

18+
import java.util.NoSuchElementException;
1819
import java.util.function.Function;
1920
import java.util.function.Predicate;
2021

21-
import org.jspecify.annotations.Nullable;
2222
import org.mybatis.dynamic.sql.AbstractSingleValueCondition;
2323

2424
public class IsEqualTo<T> extends AbstractSingleValueCondition<T> {
2525

26-
private static final IsEqualTo<?> EMPTY = new IsEqualTo<>(null) {
26+
private static final IsEqualTo<?> EMPTY = new IsEqualTo<Object>(-1) {
27+
@Override
28+
public Object value() {
29+
throw new NoSuchElementException("No value present"); //$NON-NLS-1$
30+
}
31+
2732
@Override
2833
public boolean isEmpty() {
2934
return true;
@@ -36,7 +41,7 @@ public static <T> IsEqualTo<T> empty() {
3641
return t;
3742
}
3843

39-
protected IsEqualTo(@Nullable T value) {
44+
protected IsEqualTo(T value) {
4045
super(value);
4146
}
4247

@@ -45,7 +50,7 @@ public String operator() {
4550
return "="; //$NON-NLS-1$
4651
}
4752

48-
public static <T> IsEqualTo<T> of(@Nullable T value) {
53+
public static <T> IsEqualTo<T> of(T value) {
4954
return new IsEqualTo<>(value);
5055
}
5156

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,19 @@
1515
*/
1616
package org.mybatis.dynamic.sql.where.condition;
1717

18+
import java.util.NoSuchElementException;
1819
import java.util.function.Function;
1920
import java.util.function.Predicate;
2021

21-
import org.jspecify.annotations.Nullable;
2222
import org.mybatis.dynamic.sql.AbstractSingleValueCondition;
2323

2424
public class IsGreaterThan<T> extends AbstractSingleValueCondition<T> {
25-
private static final IsGreaterThan<?> EMPTY = new IsGreaterThan<>(null) {
25+
private static final IsGreaterThan<?> EMPTY = new IsGreaterThan<Object>(-1) {
26+
@Override
27+
public Object value() {
28+
throw new NoSuchElementException("No value present"); //$NON-NLS-1$
29+
}
30+
2631
@Override
2732
public boolean isEmpty() {
2833
return true;
@@ -35,7 +40,7 @@ public static <T> IsGreaterThan<T> empty() {
3540
return t;
3641
}
3742

38-
protected IsGreaterThan(@Nullable T value) {
43+
protected IsGreaterThan(T value) {
3944
super(value);
4045
}
4146

@@ -44,7 +49,7 @@ public String operator() {
4449
return ">"; //$NON-NLS-1$
4550
}
4651

47-
public static <T> IsGreaterThan<T> of(@Nullable T value) {
52+
public static <T> IsGreaterThan<T> of(T value) {
4853
return new IsGreaterThan<>(value);
4954
}
5055

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,19 @@
1515
*/
1616
package org.mybatis.dynamic.sql.where.condition;
1717

18+
import java.util.NoSuchElementException;
1819
import java.util.function.Function;
1920
import java.util.function.Predicate;
2021

21-
import org.jspecify.annotations.Nullable;
2222
import org.mybatis.dynamic.sql.AbstractSingleValueCondition;
2323

2424
public class IsGreaterThanOrEqualTo<T> extends AbstractSingleValueCondition<T> {
25-
private static final IsGreaterThanOrEqualTo<?> EMPTY = new IsGreaterThanOrEqualTo<>(null) {
25+
private static final IsGreaterThanOrEqualTo<?> EMPTY = new IsGreaterThanOrEqualTo<Object>(-1) {
26+
@Override
27+
public Object value() {
28+
throw new NoSuchElementException("No value present"); //$NON-NLS-1$
29+
}
30+
2631
@Override
2732
public boolean isEmpty() {
2833
return true;
@@ -35,7 +40,7 @@ public static <T> IsGreaterThanOrEqualTo<T> empty() {
3540
return t;
3641
}
3742

38-
protected IsGreaterThanOrEqualTo(@Nullable T value) {
43+
protected IsGreaterThanOrEqualTo(T value) {
3944
super(value);
4045
}
4146

@@ -44,7 +49,7 @@ public String operator() {
4449
return ">="; //$NON-NLS-1$
4550
}
4651

47-
public static <T> IsGreaterThanOrEqualTo<T> of(@Nullable T value) {
52+
public static <T> IsGreaterThanOrEqualTo<T> of(T value) {
4853
return new IsGreaterThanOrEqualTo<>(value);
4954
}
5055

0 commit comments

Comments
 (0)