Add query derivation support for NOT_IN, NOT_LIKE and NEGATING_SIMPLE_PROPERTY predicates.
Closes #603 Original pull request: #604
This commit is contained in:
committed by
Mark Paluch
parent
e32d8e25cb
commit
768d0a8a67
@@ -41,6 +41,7 @@ import org.springframework.util.ObjectUtils;
|
||||
* {@link AbstractQueryCreator} to create {@link Predicate}-based {@link KeyValueQuery}s.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Tom Van Wemmel
|
||||
* @since 3.3
|
||||
*/
|
||||
public class PredicateQueryCreator extends AbstractQueryCreator<KeyValueQuery<Predicate<?>>, Predicate<?>> {
|
||||
@@ -61,12 +62,16 @@ public class PredicateQueryCreator extends AbstractQueryCreator<KeyValueQuery<Pr
|
||||
return PredicateBuilder.propertyValueOf(part).isFalse();
|
||||
case SIMPLE_PROPERTY:
|
||||
return PredicateBuilder.propertyValueOf(part).isEqualTo(iterator.next());
|
||||
case NEGATING_SIMPLE_PROPERTY:
|
||||
return PredicateBuilder.propertyValueOf(part).isEqualTo(iterator.next()).negate();
|
||||
case IS_NULL:
|
||||
return PredicateBuilder.propertyValueOf(part).isNull();
|
||||
case IS_NOT_NULL:
|
||||
return PredicateBuilder.propertyValueOf(part).isNotNull();
|
||||
case LIKE:
|
||||
return PredicateBuilder.propertyValueOf(part).contains(iterator.next());
|
||||
case NOT_LIKE:
|
||||
return PredicateBuilder.propertyValueOf(part).contains(iterator.next()).negate();
|
||||
case STARTING_WITH:
|
||||
return PredicateBuilder.propertyValueOf(part).startsWith(iterator.next());
|
||||
case AFTER:
|
||||
@@ -88,6 +93,8 @@ public class PredicateQueryCreator extends AbstractQueryCreator<KeyValueQuery<Pr
|
||||
return PredicateBuilder.propertyValueOf(part).matches(iterator.next());
|
||||
case IN:
|
||||
return PredicateBuilder.propertyValueOf(part).in(iterator.next());
|
||||
case NOT_IN:
|
||||
return PredicateBuilder.propertyValueOf(part).in(iterator.next()).negate();
|
||||
default:
|
||||
throw new InvalidDataAccessApiUsageException(String.format("Found invalid part '%s' in query", part.getType()));
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
* @author Tom Van Wemmel
|
||||
*/
|
||||
public class SpelQueryCreator extends AbstractQueryCreator<KeyValueQuery<SpelExpression>, String> {
|
||||
|
||||
@@ -120,6 +121,9 @@ public class SpelQueryCreator extends AbstractQueryCreator<KeyValueQuery<SpelExp
|
||||
case SIMPLE_PROPERTY:
|
||||
partBuilder.append("?.equals(").append("[").append(parameterIndex++).append("])");
|
||||
break;
|
||||
case NEGATING_SIMPLE_PROPERTY:
|
||||
partBuilder.append("?.equals(").append("[").append(parameterIndex++).append("]) == false");
|
||||
break;
|
||||
case IS_NULL:
|
||||
partBuilder.append(" == null");
|
||||
break;
|
||||
@@ -129,6 +133,9 @@ public class SpelQueryCreator extends AbstractQueryCreator<KeyValueQuery<SpelExp
|
||||
case LIKE:
|
||||
partBuilder.append("?.contains(").append("[").append(parameterIndex++).append("])");
|
||||
break;
|
||||
case NOT_LIKE:
|
||||
partBuilder.append("?.contains(").append("[").append(parameterIndex++).append("]) == false");
|
||||
break;
|
||||
case STARTING_WITH:
|
||||
partBuilder.append("?.startsWith(").append("[").append(parameterIndex++).append("])");
|
||||
break;
|
||||
@@ -175,9 +182,16 @@ public class SpelQueryCreator extends AbstractQueryCreator<KeyValueQuery<SpelExp
|
||||
partBuilder.append(")");
|
||||
break;
|
||||
|
||||
case NOT_IN:
|
||||
|
||||
partBuilder.append("[").append(parameterIndex++).append("].contains(");
|
||||
partBuilder.append("#it?.");
|
||||
partBuilder.append(part.getProperty().toDotPath().replace(".", "?."));
|
||||
partBuilder.append(") == false");
|
||||
break;
|
||||
|
||||
case CONTAINING:
|
||||
case NOT_CONTAINING:
|
||||
case NEGATING_SIMPLE_PROPERTY:
|
||||
case EXISTS:
|
||||
default:
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
@@ -206,6 +220,6 @@ public class SpelQueryCreator extends AbstractQueryCreator<KeyValueQuery<SpelExp
|
||||
}
|
||||
|
||||
private static boolean requiresInverseLookup(Part part) {
|
||||
return part.getType() == Type.IN;
|
||||
return part.getType() == Type.IN || part.getType() == Type.NOT_IN;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Tom Van Wemmel
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public abstract class AbstractQueryCreatorTestBase<QUERY_CREATOR extends AbstractQueryCreator<KeyValueQuery<CRITERIA>, ?>, CRITERIA> {
|
||||
@@ -70,6 +71,17 @@ public abstract class AbstractQueryCreatorTestBase<QUERY_CREATOR extends Abstrac
|
||||
assertThat(evaluate("findByFirstname", BRAN.firstname).against(RICKON)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
// GH-603
|
||||
void notEqualsReturnsTrueWhenMatching() {
|
||||
assertThat(evaluate("findByFirstnameNot", BRAN.firstname).against(RICKON)).isTrue();
|
||||
}
|
||||
|
||||
@Test // GH-603
|
||||
void notEqualsReturnsFalseWhenNotMatching() {
|
||||
assertThat(evaluate("findByFirstnameNot", BRAN.firstname).against(BRAN)).isFalse();
|
||||
}
|
||||
|
||||
@Test // DATACMNS-525
|
||||
void isTrueAssertedProperlyWhenTrue() {
|
||||
assertThat(evaluate("findBySkinChangerIsTrue").against(BRAN)).isTrue();
|
||||
@@ -130,6 +142,16 @@ public abstract class AbstractQueryCreatorTestBase<QUERY_CREATOR extends Abstrac
|
||||
assertThat(evaluate("findByFirstnameLike", "ra").against(ROBB)).isFalse();
|
||||
}
|
||||
|
||||
@Test // GH-603
|
||||
void notLikeReturnsTrueWhenMatching() {
|
||||
assertThat(evaluate("findByFirstnameNotLike", "ra").against(ROBB)).isTrue();
|
||||
}
|
||||
|
||||
@Test // GH-603
|
||||
void notLikeReturnsFalseWhenNotMatching() {
|
||||
assertThat(evaluate("findByFirstnameNotLike", "ob").against(ROBB)).isFalse();
|
||||
}
|
||||
|
||||
@Test // DATACMNS-525
|
||||
void endsWithReturnsTrueWhenMatching() {
|
||||
assertThat(evaluate("findByFirstnameEndingWith", "bb").against(ROBB)).isTrue();
|
||||
@@ -310,6 +332,53 @@ public abstract class AbstractQueryCreatorTestBase<QUERY_CREATOR extends Abstrac
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test // GH-603
|
||||
void notInReturnsMatchCorrectly() {
|
||||
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.add(ROBB.firstname);
|
||||
|
||||
assertThat(evaluate("findByFirstnameNotIn", list).against(JON)).isTrue();
|
||||
}
|
||||
|
||||
@Test // GH-603
|
||||
void notInNotMatchingReturnsCorrectly() {
|
||||
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.add(ROBB.firstname);
|
||||
|
||||
assertThat(evaluate("findByFirstnameNotIn", list).against(ROBB)).isFalse();
|
||||
}
|
||||
|
||||
@Test // GH-603
|
||||
void notInWithNullCompareValuesCorrectly() {
|
||||
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.add(null);
|
||||
|
||||
assertThat(evaluate("findByFirstnameNotIn", list).against(JON)).isTrue();
|
||||
}
|
||||
|
||||
@Test // GH-603
|
||||
void notInWithNullSourceValuesMatchesCorrectly() {
|
||||
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.add(ROBB.firstname);
|
||||
|
||||
assertThat(evaluate("findByFirstnameNotIn", list).against(new PredicateQueryCreatorUnitTests.Person(null, 10)))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test // GH-603
|
||||
void notInMatchesNullValuesCorrectly() {
|
||||
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.add(null);
|
||||
|
||||
assertThat(evaluate("findByFirstnameNotIn", list).against(new PredicateQueryCreatorUnitTests.Person(null, 10)))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test // DATAKV-185
|
||||
void noDerivedQueryArgumentsMatchesAlways() {
|
||||
|
||||
@@ -363,6 +432,9 @@ public abstract class AbstractQueryCreatorTestBase<QUERY_CREATOR extends Abstrac
|
||||
// Type.SIMPLE_PROPERTY
|
||||
Person findByFirstname(String firstname);
|
||||
|
||||
// Type.NEGATING_SIMPLE_PROPERTY
|
||||
Person findByFirstnameNot(String firstname);
|
||||
|
||||
// Type.TRUE
|
||||
Person findBySkinChangerIsTrue();
|
||||
|
||||
@@ -404,6 +476,9 @@ public abstract class AbstractQueryCreatorTestBase<QUERY_CREATOR extends Abstrac
|
||||
// Type.LIKE
|
||||
Person findByFirstnameLike(String firstname);
|
||||
|
||||
// Type.NOT_LIKE
|
||||
Person findByFirstnameNotLike(String firstname);
|
||||
|
||||
// Type.ENDING_WITH
|
||||
Person findByFirstnameEndingWith(String firstname);
|
||||
|
||||
@@ -417,6 +492,9 @@ public abstract class AbstractQueryCreatorTestBase<QUERY_CREATOR extends Abstrac
|
||||
// Type.IN
|
||||
Person findByFirstnameIn(ArrayList<String> in);
|
||||
|
||||
// Type.NOT_IN
|
||||
Person findByFirstnameNotIn(ArrayList<String> in);
|
||||
|
||||
}
|
||||
|
||||
public interface Evaluation {
|
||||
|
||||
Reference in New Issue
Block a user