Skip to content

Commit 7ad1c45

Browse files
authored
Merge pull request #336 from jeffgbutler/map-can-change-type
Condition Refactoring: Map Methods Can Change Datatypes
2 parents d468f1f + 74f57a2 commit 7ad1c45

29 files changed

+398
-198
lines changed

CHANGELOG.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,16 @@ However, there are some changes in behavior and one breaking change.
2626
methods that accept Suppliers will call the `Supplier.get()` method when the condition is constructed. This should
2727
have no impact unless you were somehow relying on the delay in obtaining a value until the condition was rendered.
2828
1. The existing "then" and "when" methods have been deprecated and replaced with "map" and "filter" respectively.
29-
The new method names are more familiar and more representative of what these methods actually do. In effect,
29+
The new method names are more familiar and more representative of what these methods actually do. In effect
3030
these methods mimic the function of the "map" and "filter" methods on "java.util.Optional" and they are used
3131
for a similar purpose.
32-
1. The new "filter" method works a bit differently than the "when" method it replaces. The "when" method could not
32+
1. The new "filter" method works a bit differently than the "when" method it replaces. The old "when" method could not
3333
be chained - if it was called multiple times, only the last call would take effect. The new "filter" methods works
3434
as it should and every call will take effect.
35+
1. The new "map" method will allow you to change the datatype of a condition as is normal for a "map" method. You
36+
can use this method to apply a type conversion directly within condition.
3537
1. All the "WhenPresent" conditions have been removed as separate classes. The methods that produced these conditions
36-
in the SqlBuilder remain, and they will now produce a condition with a "NotNull" filter applied. So at the API level,
38+
in the SqlBuilder remain, and they will now produce a condition with a "NotNull" filter applied. So at the API level
3739
things will function exactly as before, but the intermediate classes will be different.
3840
1. One breaking change is that the builder for List value conditions has been removed without replacement. If you
3941
were using this builder to supply a "value stream transformer", then the replacement is to build a new List value
@@ -93,7 +95,7 @@ Kotlin DSL.
9395
- Added Kotlin DSL updates to support sub-queries in select statements, where clauses, and insert statements ([#282](https://github.com/mybatis/mybatis-dynamic-sql/pull/282))
9496
- Added subquery support for "join" clauses in a select statement ([#293](https://github.com/mybatis/mybatis-dynamic-sql/pull/293))
9597
- Added support for the "exists" and "not exists" operator in where clauses ([#296](https://github.com/mybatis/mybatis-dynamic-sql/pull/296))
96-
- Refactored the built-in conditions ([#331](https://github.com/mybatis/mybatis-dynamic-sql/pull/331))
98+
- Refactored the built-in conditions ([#331](https://github.com/mybatis/mybatis-dynamic-sql/pull/331)) ([#336](https://github.com/mybatis/mybatis-dynamic-sql/pull/336))
9799
- Added composition functions for WhereApplier ([#335](https://github.com/mybatis/mybatis-dynamic-sql/pull/335))
98100

99101
## Release 1.2.1 - September 29, 2020

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

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@
2020
import java.util.function.BiFunction;
2121
import java.util.function.Function;
2222
import java.util.function.Predicate;
23-
import java.util.function.UnaryOperator;
23+
import java.util.function.Supplier;
2424
import java.util.stream.Collectors;
2525
import java.util.stream.Stream;
2626

27-
public abstract class AbstractListValueCondition<T, S extends AbstractListValueCondition<T, S>>
27+
public abstract class AbstractListValueCondition<T>
2828
implements VisitableCondition<T> {
2929
protected final Collection<T> values;
3030
protected final Callback emptyCallback;
@@ -44,67 +44,60 @@ public final <R> Stream<R> mapValues(Function<T, R> mapper) {
4444

4545
@Override
4646
public boolean shouldRender() {
47-
if (values.isEmpty()) {
48-
emptyCallback.call();
49-
return false;
50-
} else {
51-
return true;
52-
}
47+
return !values.isEmpty();
48+
}
49+
50+
@Override
51+
public void renderingSkipped() {
52+
emptyCallback.call();
5353
}
5454

5555
@Override
5656
public <R> R accept(ConditionVisitor<T, R> visitor) {
5757
return visitor.visit(this);
5858
}
5959

60-
private Collection<T> applyMapper(UnaryOperator<T> mapper) {
60+
private <R> Collection<R> applyMapper(Function<? super T, ? extends R> mapper) {
6161
Objects.requireNonNull(mapper);
6262
return values.stream().map(mapper).collect(Collectors.toList());
6363
}
6464

65-
private Collection<T> applyFilter(Predicate<T> predicate) {
65+
private Collection<T> applyFilter(Predicate<? super T> predicate) {
6666
Objects.requireNonNull(predicate);
6767
return values.stream().filter(predicate).collect(Collectors.toList());
6868
}
6969

70-
protected S filter(Predicate<T> predicate, BiFunction<Collection<T>, Callback, S> constructor, S self) {
70+
protected <S> S filterSupport(Predicate<? super T> predicate,
71+
BiFunction<Collection<T>, Callback, S> constructor, S self, Supplier<S> empty) {
7172
if (shouldRender()) {
72-
return constructor.apply(applyFilter(predicate), emptyCallback);
73+
Collection<T> filtered = applyFilter(predicate);
74+
return filtered.isEmpty() ? empty.get() : constructor.apply(filtered, emptyCallback);
7375
} else {
7476
return self;
7577
}
7678
}
7779

78-
/**
79-
* If renderable, apply the predicate to each value in the list and return a new condition with the filtered values.
80-
* Else returns a condition that will not render (this). If all values are filtered out of the value
81-
* list, then the condition will not render.
82-
*
83-
* @param predicate predicate applied to the values, if renderable
84-
* @return a new condition with filtered values if renderable, otherwise a condition
85-
* that will not render.
86-
*/
87-
public abstract S filter(Predicate<T> predicate);
88-
89-
protected S map(UnaryOperator<T> mapper, BiFunction<Collection<T>, Callback, S> constructor, S self) {
80+
protected <R, S> S mapSupport(Function<? super T, ? extends R> mapper,
81+
BiFunction<Collection<R>, Callback, S> constructor, Supplier<S> empty) {
9082
if (shouldRender()) {
9183
return constructor.apply(applyMapper(mapper), emptyCallback);
9284
} else {
93-
return self;
85+
return empty.get();
9486
}
9587
}
9688

9789
/**
98-
* If renderable, apply the mapping to each value in the list return a new condition with the mapped values.
99-
* Else return a condition that will not render (this).
90+
* If renderable, apply the predicate to each value in the list and return a new condition with the filtered values.
91+
* Else returns a condition that will not render (this). If all values are filtered out of the value
92+
* list, then the condition will not render.
10093
*
101-
* @param mapper a mapping function to apply to the values, if renderable
102-
* @return a new condition with mapped values if renderable, otherwise a condition
94+
* @param predicate predicate applied to the values, if renderable
95+
* @return a new condition with filtered values if renderable, otherwise a condition
10396
* that will not render.
10497
*/
105-
public abstract S map(UnaryOperator<T> mapper);
98+
public abstract AbstractListValueCondition<T> filter(Predicate<? super T> predicate);
10699

107-
public abstract S withListEmptyCallback(Callback callback);
100+
public abstract AbstractListValueCondition<T> withListEmptyCallback(Callback callback);
108101

109102
public abstract String renderCondition(String columnName, Stream<String> placeholders);
110103
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public <R> R accept(ConditionVisitor<T, R> visitor) {
2525
return visitor.visit(this);
2626
}
2727

28-
protected <S> S filter(BooleanSupplier booleanSupplier, Supplier<S> empty, S self) {
28+
protected <S> S filterSupport(BooleanSupplier booleanSupplier, Supplier<S> empty, S self) {
2929
if (shouldRender()) {
3030
return booleanSupplier.getAsBoolean() ? self : empty.get();
3131
} else {

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

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@
1818
import java.util.function.Function;
1919
import java.util.function.Predicate;
2020
import java.util.function.Supplier;
21-
import java.util.function.UnaryOperator;
2221

23-
public abstract class AbstractSingleValueCondition<T, S extends AbstractSingleValueCondition<T, S>>
24-
implements VisitableCondition<T> {
22+
public abstract class AbstractSingleValueCondition<T> implements VisitableCondition<T> {
2523
protected final T value;
2624

2725
protected AbstractSingleValueCondition(T value) {
@@ -37,41 +35,32 @@ public <R> R accept(ConditionVisitor<T, R> visitor) {
3735
return visitor.visit(this);
3836
}
3937

40-
protected S filter(Predicate<T> predicate, Supplier<S> empty, S self) {
38+
protected <S> S filterSupport(Predicate<? super T> predicate, Supplier<S> empty, S self) {
4139
if (shouldRender()) {
4240
return predicate.test(value) ? self : empty.get();
4341
} else {
4442
return self;
4543
}
4644
}
4745

48-
/**
49-
* If renderable and the value matches the predicate, returns this condition. Else returns a condition
50-
* that will not render.
51-
*
52-
* @param predicate predicate applied to the value, if renderable
53-
* @return this condition if renderable and the value matches the predicate, otherwise a condition
54-
* that will not render.
55-
*/
56-
public abstract S filter(Predicate<T> predicate);
57-
58-
protected S map(UnaryOperator<T> mapper, Function<T, S> constructor, S self) {
46+
protected <R, S> S mapSupport(Function<? super T, ? extends R> mapper, Function<R, S> constructor,
47+
Supplier<S> empty) {
5948
if (shouldRender()) {
6049
return constructor.apply(mapper.apply(value));
6150
} else {
62-
return self;
51+
return empty.get();
6352
}
6453
}
6554

6655
/**
67-
* If renderable, apply the mapping to the value and return a new condition with the new value. Else return a
68-
* condition that will not render (this).
56+
* If renderable and the value matches the predicate, returns this condition. Else returns a condition
57+
* that will not render.
6958
*
70-
* @param mapper a mapping function to apply to the value, if renderable
71-
* @return a new condition with the result of applying the mapper to the value of this condition,
72-
* if renderable, otherwise a condition that will not render.
59+
* @param predicate predicate applied to the value, if renderable
60+
* @return this condition if renderable and the value matches the predicate, otherwise a condition
61+
* that will not render.
7362
*/
74-
public abstract S map(UnaryOperator<T> mapper);
63+
public abstract AbstractSingleValueCondition<T> filter(Predicate<? super T> predicate);
7564

7665
public abstract String renderCondition(String columnName, String placeholder);
7766
}

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

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@
1717

1818
import java.util.function.BiFunction;
1919
import java.util.function.BiPredicate;
20+
import java.util.function.Function;
2021
import java.util.function.Supplier;
21-
import java.util.function.UnaryOperator;
2222

23-
public abstract class AbstractTwoValueCondition<T, S extends AbstractTwoValueCondition<T, S>>
23+
public abstract class AbstractTwoValueCondition<T>
2424
implements VisitableCondition<T> {
2525
protected final T value1;
2626
protected final T value2;
@@ -43,42 +43,34 @@ public <R> R accept(ConditionVisitor<T, R> visitor) {
4343
return visitor.visit(this);
4444
}
4545

46-
protected S filter(BiPredicate<T, T> predicate, Supplier<S> empty, S self) {
46+
protected <S> S filterSupport(BiPredicate<? super T, ? super T> predicate, Supplier<S> empty, S self) {
4747
if (shouldRender()) {
4848
return predicate.test(value1, value2) ? self : empty.get();
4949
} else {
5050
return self;
5151
}
5252
}
5353

54-
/**
55-
* If renderable and the values match the predicate, returns this condition. Else returns a condition
56-
* that will not render.
57-
*
58-
* @param predicate predicate applied to the values, if renderable
59-
* @return this condition if renderable and the values match the predicate, otherwise a condition
60-
* that will not render.
61-
*/
62-
public abstract S filter(BiPredicate<T, T> predicate);
63-
64-
protected S map(UnaryOperator<T> mapper1, UnaryOperator<T> mapper2, BiFunction<T, T, S> constructor, S self) {
54+
protected <R, S> S mapSupport(Function<? super T, ? extends R> mapper1,
55+
Function<? super T, ? extends R> mapper2,
56+
BiFunction<R, R, S> constructor,
57+
Supplier<S> empty) {
6558
if (shouldRender()) {
6659
return constructor.apply(mapper1.apply(value1), mapper2.apply(value2));
6760
} else {
68-
return self;
61+
return empty.get();
6962
}
7063
}
7164

7265
/**
73-
* If renderable, apply the mappings to the values and return a new condition with the new values. Else return a
74-
* condition that will not render (this).
66+
* If renderable and the values match the predicate, returns this condition. Else returns a condition
67+
* that will not render.
7568
*
76-
* @param mapper1 a mapping function to apply to the first value, if renderable
77-
* @param mapper2 a mapping function to apply to the second value, if renderable
78-
* @return a new condition with the result of applying the mappers to the values of this condition,
79-
* if renderable, otherwise a condition that will not render.
69+
* @param predicate predicate applied to the values, if renderable
70+
* @return this condition if renderable and the values match the predicate, otherwise a condition
71+
* that will not render.
8072
*/
81-
public abstract S map(UnaryOperator<T> mapper1, UnaryOperator<T> mapper2);
73+
public abstract AbstractTwoValueCondition<T> filter(BiPredicate<? super T, ? super T> predicate);
8274

8375
public abstract String renderCondition(String columnName, String placeholder1, String placeholder2);
8476
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616
package org.mybatis.dynamic.sql;
1717

1818
public interface ConditionVisitor<T, R> {
19-
R visit(AbstractListValueCondition<T, ?> condition);
19+
R visit(AbstractListValueCondition<T> condition);
2020

2121
R visit(AbstractNoValueCondition<T> condition);
2222

23-
R visit(AbstractSingleValueCondition<T, ?> condition);
23+
R visit(AbstractSingleValueCondition<T> condition);
2424

25-
R visit(AbstractTwoValueCondition<T, ?> condition);
25+
R visit(AbstractTwoValueCondition<T> condition);
2626

2727
R visit(AbstractSubselectCondition<T> condition);
2828

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 the original author or authors.
2+
* Copyright 2016-2021 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.
@@ -28,4 +28,10 @@ public interface VisitableCondition<T> {
2828
default boolean shouldRender() {
2929
return true;
3030
}
31+
32+
/**
33+
* This method will be called during rendering when {@link VisitableCondition#shouldRender()}
34+
* returns false.
35+
*/
36+
default void renderingSkipped() {}
3137
}

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

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616
package org.mybatis.dynamic.sql.where.condition;
1717

1818
import java.util.function.BiPredicate;
19+
import java.util.function.Function;
1920
import java.util.function.UnaryOperator;
2021

2122
import org.mybatis.dynamic.sql.AbstractTwoValueCondition;
2223
import org.mybatis.dynamic.sql.util.Predicates;
2324

24-
public class IsBetween<T> extends AbstractTwoValueCondition<T, IsBetween<T>> {
25+
public class IsBetween<T> extends AbstractTwoValueCondition<T> {
2526
private static final IsBetween<?> EMPTY = new IsBetween<Object>(null, null) {
2627
@Override
2728
public boolean shouldRender() {
@@ -62,7 +63,7 @@ public IsBetween<T> when(BiPredicate<T, T> predicate) {
6263
* If renderable, apply the mappings to the values and return a new condition with the new values. Else return a
6364
* condition that will not render (this).
6465
*
65-
* @deprecated replaced by {@link IsBetween#map(UnaryOperator, UnaryOperator)}
66+
* @deprecated replaced by {@link IsBetween#map(Function, Function)}
6667
* @param mapper1 a mapping function to apply to the first value, if renderable
6768
* @param mapper2 a mapping function to apply to the second value, if renderable
6869
* @return a new condition with the result of applying the mappers to the values of this condition,
@@ -74,13 +75,22 @@ public IsBetween<T> then(UnaryOperator<T> mapper1, UnaryOperator<T> mapper2) {
7475
}
7576

7677
@Override
77-
public IsBetween<T> filter(BiPredicate<T, T> predicate) {
78-
return filter(predicate, IsBetween::empty, this);
78+
public IsBetween<T> filter(BiPredicate<? super T, ? super T> predicate) {
79+
return filterSupport(predicate, IsBetween::empty, this);
7980
}
8081

81-
@Override
82-
public IsBetween<T> map(UnaryOperator<T> mapper1, UnaryOperator<T> mapper2) {
83-
return map(mapper1, mapper2, IsBetween::new, this);
82+
/**
83+
* If renderable, apply the mappings to the values and return a new condition with the new values. Else return a
84+
* condition that will not render (this).
85+
*
86+
* @param mapper1 a mapping function to apply to the first value, if renderable
87+
* @param mapper2 a mapping function to apply to the second value, if renderable
88+
* @param <R> type of the new condition
89+
* @return a new condition with the result of applying the mappers to the values of this condition,
90+
* if renderable, otherwise a condition that will not render.
91+
*/
92+
public <R> IsBetween<R> map(Function<? super T, ? extends R> mapper1, Function<? super T, ? extends R> mapper2) {
93+
return mapSupport(mapper1, mapper2, IsBetween::new, IsBetween::empty);
8494
}
8595

8696
public static <T> Builder<T> isBetween(T value1) {

0 commit comments

Comments
 (0)