Skip to content

Commit 15f8681

Browse files
committed
DATAJDBC-514 - Add support for Between and Not Like to Criteria API and SQL generation.
We now support Conditions.between, notBetween, and notLike as additional criteria conditions and support case-insensitive comparisons. For LIKE escaping we pick up the Escaper configured at Dialect level. The newly introduced ValueFunction allows string transformation before computing a value by applying the Escaper to the raw value. Escaping is required for StartingWith, Contains and EndsWith PartTree operations. Original pull request: spring-projects/spring-data-r2dbc#295.
1 parent d61651a commit 15f8681

26 files changed

+819
-83
lines changed

spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
*/
1616
package org.springframework.data.relational.core.dialect;
1717

18-
import org.springframework.data.relational.core.sql.render.SelectRenderContext;
1918
import org.springframework.data.relational.core.sql.IdentifierProcessing;
2019
import org.springframework.data.relational.core.sql.SqlIdentifier;
20+
import org.springframework.data.relational.core.sql.render.SelectRenderContext;
2121

2222
/**
2323
* Represents a dialect that is implemented by a particular database. Please note that not all features are supported by
@@ -63,4 +63,14 @@ default ArrayColumns getArraySupport() {
6363
default IdentifierProcessing getIdentifierProcessing() {
6464
return IdentifierProcessing.ANSI;
6565
}
66+
67+
/**
68+
* Returns the {@link Escaper} used for {@code LIKE} value escaping.
69+
*
70+
* @return the {@link Escaper} used for {@code LIKE} value escaping.
71+
* @since 2.0
72+
*/
73+
default Escaper getLikeEscaper() {
74+
return Escaper.DEFAULT;
75+
}
6676
}

spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/SqlServerDialect.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public class SqlServerDialect extends AbstractDialect {
3131
*/
3232
public static final SqlServerDialect INSTANCE = new SqlServerDialect();
3333

34-
protected SqlServerDialect() { }
34+
protected SqlServerDialect() {}
3535

3636
private static final LimitClause LIMIT_CLAUSE = new LimitClause() {
3737

@@ -84,6 +84,15 @@ public LimitClause limit() {
8484
return LIMIT_CLAUSE;
8585
}
8686

87+
/*
88+
* (non-Javadoc)
89+
* @see org.springframework.data.relational.core.dialect.Dialect#getLikeEscaper()
90+
*/
91+
@Override
92+
public Escaper getLikeEscaper() {
93+
return Escaper.DEFAULT.withRewriteFor("[", "]");
94+
}
95+
8796
/*
8897
* (non-Javadoc)
8998
* @see org.springframework.data.relational.core.dialect.AbstractDialect#getSelectContext()

spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Criteria.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import org.springframework.dao.InvalidDataAccessApiUsageException;
2424
import org.springframework.data.relational.core.sql.SqlIdentifier;
25+
import org.springframework.data.util.Pair;
2526
import org.springframework.lang.Nullable;
2627
import org.springframework.util.Assert;
2728

@@ -93,7 +94,6 @@ private Criteria(@Nullable Criteria previous, Combinator combinator, List<Criter
9394
this.ignoreCase = false;
9495
}
9596

96-
9797
/**
9898
* Static factory method to create an empty Criteria.
9999
*
@@ -417,6 +417,24 @@ public interface CriteriaStep {
417417
*/
418418
Criteria notIn(Collection<?> values);
419419

420+
/**
421+
* Creates a {@link Criteria} using between ({@literal BETWEEN begin AND end}).
422+
*
423+
* @param begin must not be {@literal null}.
424+
* @param end must not be {@literal null}.
425+
* @since 2.2
426+
*/
427+
Criteria between(Object begin, Object end);
428+
429+
/**
430+
* Creates a {@link Criteria} using not between ({@literal NOT BETWEEN begin AND end}).
431+
*
432+
* @param begin must not be {@literal null}.
433+
* @param end must not be {@literal null}.
434+
* @since 2.2
435+
*/
436+
Criteria notBetween(Object begin, Object end);
437+
420438
/**
421439
* Creates a {@link Criteria} using less-than ({@literal <}).
422440
*
@@ -582,6 +600,32 @@ public Criteria notIn(Collection<?> values) {
582600
return createCriteria(Comparator.NOT_IN, values);
583601
}
584602

603+
/*
604+
* (non-Javadoc)
605+
* @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#between(java.lang.Object, java.lang.Object)
606+
*/
607+
@Override
608+
public Criteria between(Object begin, Object end) {
609+
610+
Assert.notNull(begin, "Begin value must not be null!");
611+
Assert.notNull(end, "End value must not be null!");
612+
613+
return createCriteria(Comparator.BETWEEN, Pair.of(begin, end));
614+
}
615+
616+
/*
617+
* (non-Javadoc)
618+
* @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#notBetween(java.lang.Object, java.lang.Object)
619+
*/
620+
@Override
621+
public Criteria notBetween(Object begin, Object end) {
622+
623+
Assert.notNull(begin, "Begin value must not be null!");
624+
Assert.notNull(end, "End value must not be null!");
625+
626+
return createCriteria(Comparator.NOT_BETWEEN, Pair.of(begin, end));
627+
}
628+
585629
/*
586630
* (non-Javadoc)
587631
* @see org.springframework.data.r2dbc.function.query.Criteria.CriteriaStep#lessThan(java.lang.Object)

spring-data-relational/src/main/java/org/springframework/data/relational/core/query/CriteriaDefinition.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,6 @@ enum Combinator {
135135
}
136136

137137
enum Comparator {
138-
INITIAL, EQ, NEQ, LT, LTE, GT, GTE, IS_NULL, IS_NOT_NULL, LIKE, NOT_LIKE, NOT_IN, IN, IS_TRUE, IS_FALSE
138+
INITIAL, EQ, NEQ, BETWEEN, NOT_BETWEEN, LT, LTE, GT, GTE, IS_NULL, IS_NOT_NULL, LIKE, NOT_LIKE, NOT_IN, IN, IS_TRUE, IS_FALSE
139139
}
140140
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright 2020 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.springframework.data.relational.core.query;
17+
18+
import java.util.function.Function;
19+
import java.util.function.Supplier;
20+
21+
import org.springframework.data.relational.core.dialect.Escaper;
22+
import org.springframework.lang.Nullable;
23+
import org.springframework.util.Assert;
24+
25+
/**
26+
* Represents a value function to return arbitrary values that can be escaped before returning the actual value. Can be
27+
* used with the criteria API for deferred value retrieval.
28+
*
29+
* @author Mark Paluch
30+
* @since 2.0
31+
* @see Escaper
32+
* @see Supplier
33+
*/
34+
@FunctionalInterface
35+
public interface ValueFunction<T> extends Function<Escaper, T> {
36+
37+
/**
38+
* Produces a value by considering the given {@link Escaper}.
39+
*
40+
* @param escaper the escaper to use.
41+
* @return the return value, may be {@literal null}.
42+
*/
43+
@Nullable
44+
@Override
45+
T apply(Escaper escaper);
46+
47+
/**
48+
* Adapts this value factory into a {@link Supplier} by using the given {@link Escaper}.
49+
*
50+
* @param escaper the escaper to use.
51+
* @return the value factory
52+
*/
53+
default Supplier<T> toSupplier(Escaper escaper) {
54+
55+
Assert.notNull(escaper, "Escaper must not be null");
56+
57+
return () -> apply(escaper);
58+
}
59+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright 2019-2020 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.springframework.data.relational.core.sql;
17+
18+
import org.springframework.util.Assert;
19+
20+
/**
21+
* BETWEEN {@link Condition} comparing between {@link Expression}s.
22+
* <p/>
23+
* Results in a rendered condition: {@code <left> BETWEEN <begin> AND <end>}.
24+
*
25+
* @author Mark Paluch
26+
* @since 2.2
27+
*/
28+
public class Between extends AbstractSegment implements Condition {
29+
30+
private final Expression column;
31+
private final Expression begin;
32+
private final Expression end;
33+
private final boolean negated;
34+
35+
private Between(Expression column, Expression begin, Expression end, boolean negated) {
36+
37+
super(column, begin, end);
38+
39+
this.column = column;
40+
this.begin = begin;
41+
this.end = end;
42+
this.negated = negated;
43+
}
44+
45+
/**
46+
* Creates a new {@link Between} {@link Condition} given two {@link Expression}s.
47+
*
48+
* @param columnOrExpression left side of the comparison.
49+
* @param begin begin value of the comparison.
50+
* @param end end value of the comparison.
51+
* @return the {@link Between} condition.
52+
*/
53+
public static Between create(Expression columnOrExpression, Expression begin, Expression end) {
54+
55+
Assert.notNull(columnOrExpression, "Column or expression must not be null!");
56+
Assert.notNull(begin, "Begin value must not be null!");
57+
Assert.notNull(end, "end value must not be null!");
58+
59+
return new Between(columnOrExpression, begin, end, false);
60+
}
61+
62+
/**
63+
* @return the column {@link Expression}.
64+
*/
65+
public Expression getColumn() {
66+
return column;
67+
}
68+
69+
/**
70+
* @return the begin {@link Expression}.
71+
*/
72+
public Expression getBegin() {
73+
return begin;
74+
}
75+
76+
/**
77+
* @return the end {@link Expression}.
78+
*/
79+
public Expression getEnd() {
80+
return end;
81+
}
82+
83+
public boolean isNegated() {
84+
return negated;
85+
}
86+
87+
@Override
88+
public Between not() {
89+
return new Between(this.column, this.begin, this.end, !negated);
90+
}
91+
92+
@Override
93+
public String toString() {
94+
return column.toString() + " BETWEEN " + begin.toString() + " AND " + end.toString();
95+
}
96+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2019-2020 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.springframework.data.relational.core.sql;
17+
18+
/**
19+
* Represents a {@link Boolean} literal.
20+
*
21+
* @author Mark Paluch
22+
* @since 2.0
23+
*/
24+
public class BooleanLiteral extends Literal<Boolean> {
25+
26+
BooleanLiteral(boolean content) {
27+
super(content);
28+
}
29+
30+
/*
31+
* (non-Javadoc)
32+
* @see org.springframework.data.relational.core.sql.Literal#getContent()
33+
*/
34+
@Override
35+
public Boolean getContent() {
36+
return super.getContent();
37+
}
38+
39+
/*
40+
* (non-Javadoc)
41+
* @see org.springframework.data.relational.core.sql.Literal#toString()
42+
*/
43+
@Override
44+
public String toString() {
45+
return getContent() ? "TRUE" : "FALSE";
46+
}
47+
}

0 commit comments

Comments
 (0)