DATACASS-376 - Support ALLOW FILTERING using derived query methods.
We now support allow filtering using derived query methods by annotating query methods with AllowFiltering.
interface PersonRepository extends Repository<Person, String> {
@AllowFiltering
List<Person> findAllByFirstname(String firstname);
}
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.cassandra.repository;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation to declare filtering for a derived query.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
|
||||
@Documented
|
||||
@Query(allowFiltering = true)
|
||||
public @interface AllowFiltering {
|
||||
}
|
||||
@@ -39,8 +39,13 @@ public @interface Query {
|
||||
/**
|
||||
* A Cassandra CQL3 string to define the actual query to be executed. Placeholders {@code ?0}, {@code ?1}, etc are
|
||||
* supported.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* Specifies whether to allow filtering using query derivation without a {@link #value() string query}.
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
boolean allowFiltering() default false;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import java.lang.reflect.Method;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
|
||||
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
|
||||
@@ -50,6 +49,8 @@ public class CassandraQueryMethod extends QueryMethod {
|
||||
|
||||
private final MappingContext<? extends CassandraPersistentEntity<?>, ? extends CassandraPersistentProperty> mappingContext;
|
||||
|
||||
private final Optional<Query> query;
|
||||
|
||||
private CassandraEntityMetadata<?> entityMetadata;
|
||||
|
||||
/**
|
||||
@@ -71,6 +72,7 @@ public class CassandraQueryMethod extends QueryMethod {
|
||||
|
||||
this.method = method;
|
||||
this.mappingContext = mappingContext;
|
||||
this.query = Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(method, Query.class));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,7 +144,7 @@ public class CassandraQueryMethod extends QueryMethod {
|
||||
* Returns whether the method has an annotated query.
|
||||
*/
|
||||
public boolean hasAnnotatedQuery() {
|
||||
return findAnnotatedQuery().isPresent();
|
||||
return query.map(Query::value).filter(StringUtils::hasText).isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,16 +154,7 @@ public class CassandraQueryMethod extends QueryMethod {
|
||||
* @return
|
||||
*/
|
||||
public String getAnnotatedQuery() {
|
||||
return findAnnotatedQuery().orElse(null);
|
||||
}
|
||||
|
||||
private Optional<String> findAnnotatedQuery() {
|
||||
|
||||
Optional<Query> queryAnnotation = Optional.ofNullable(getQueryAnnotation());
|
||||
|
||||
return queryAnnotation.map(AnnotationUtils::getValue) //
|
||||
.map(it -> (String) it) //
|
||||
.filter(StringUtils::hasText);
|
||||
return query.map(Query::value).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,8 +162,8 @@ public class CassandraQueryMethod extends QueryMethod {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Query getQueryAnnotation() {
|
||||
return AnnotatedElementUtils.findMergedAnnotation(method, Query.class);
|
||||
Optional<Query> getQueryAnnotation() {
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -102,8 +102,13 @@ public class PartTreeCassandraQuery extends AbstractCassandraQuery {
|
||||
Query query = queryCreator.createQuery();
|
||||
|
||||
try {
|
||||
|
||||
if (getTree().isLimiting()) {
|
||||
query.limit(getTree().getMaxResults());
|
||||
query = query.limit(getTree().getMaxResults());
|
||||
}
|
||||
|
||||
if (getQueryMethod().getQueryAnnotation().map(q -> q.allowFiltering()).orElse(false)) {
|
||||
query = query.withAllowFiltering();
|
||||
}
|
||||
|
||||
CassandraPersistentEntity<?> persistentEntity = getMappingContext()
|
||||
|
||||
@@ -102,8 +102,13 @@ public class ReactivePartTreeCassandraQuery extends AbstractReactiveCassandraQue
|
||||
Query query = queryCreator.createQuery();
|
||||
|
||||
try {
|
||||
|
||||
if (getTree().isLimiting()) {
|
||||
query.limit(getTree().getMaxResults());
|
||||
query = query.limit(getTree().getMaxResults());
|
||||
}
|
||||
|
||||
if (getQueryMethod().getQueryAnnotation().map(q -> q.allowFiltering()).orElse(false)) {
|
||||
query = query.withAllowFiltering();
|
||||
}
|
||||
|
||||
CassandraPersistentEntity<?> persistentEntity = getMappingContext()
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.springframework.data.cassandra.core.mapping.UserTypeResolver;
|
||||
import org.springframework.data.cassandra.domain.AddressType;
|
||||
import org.springframework.data.cassandra.domain.Group;
|
||||
import org.springframework.data.cassandra.domain.Person;
|
||||
import org.springframework.data.cassandra.repository.AllowFiltering;
|
||||
import org.springframework.data.cassandra.repository.MapIdCassandraRepository;
|
||||
import org.springframework.data.cassandra.repository.Query;
|
||||
import org.springframework.data.cql.core.CqlIdentifier;
|
||||
@@ -155,6 +156,14 @@ public class PartTreeCassandraQueryUnitTests {
|
||||
assertThat(query.toString()).isEqualTo("SELECT * FROM group WHERE hash_prefix='foo';");
|
||||
}
|
||||
|
||||
@Test // DATACASS-376
|
||||
public void shouldAllowFiltering() {
|
||||
|
||||
Statement query = deriveQueryFromMethod(Repo.class, "findByFirstname", new Class[] { String.class }, "foo");
|
||||
|
||||
assertThat(query.toString()).isEqualTo("SELECT * FROM person WHERE firstname='foo' ALLOW FILTERING;");
|
||||
}
|
||||
|
||||
private String deriveQueryFromMethod(String method, Object... args) {
|
||||
|
||||
Class<?>[] types = new Class<?>[args.length];
|
||||
@@ -224,6 +233,9 @@ public class PartTreeCassandraQueryUnitTests {
|
||||
|
||||
Person findByFirstnameIn(Collection<String> firstname);
|
||||
|
||||
@AllowFiltering
|
||||
Person findByFirstname(String firstname);
|
||||
|
||||
PersonProjection findPersonProjectedBy();
|
||||
|
||||
<T> T findDynamicallyProjectedBy(Class<T> type);
|
||||
|
||||
@@ -87,6 +87,14 @@ public class ReactivePartTreeCassandraQueryUnitTests {
|
||||
assertThat(query).isEqualTo("SELECT * FROM person WHERE firstname='foo' AND lastname='bar';");
|
||||
}
|
||||
|
||||
@Test // DATACASS-376
|
||||
public void shouldAllowFiltering() {
|
||||
|
||||
String query = deriveQueryFromMethod("findPersonByFirstname", "foo");
|
||||
|
||||
assertThat(query).isEqualTo("SELECT * FROM person WHERE firstname='foo' ALLOW FILTERING;");
|
||||
}
|
||||
|
||||
@Test // DATACASS-335
|
||||
public void usesDynamicProjection() {
|
||||
String query = deriveQueryFromMethod("findDynamicallyProjectedBy", PersonProjection.class);
|
||||
@@ -140,6 +148,9 @@ public class ReactivePartTreeCassandraQueryUnitTests {
|
||||
|
||||
Flux<Person> findPersonBy();
|
||||
|
||||
@Query(allowFiltering = true)
|
||||
Flux<Person> findPersonByFirstname(String name);
|
||||
|
||||
Mono<PersonProjection> findPersonProjectedBy();
|
||||
|
||||
<T> Single<T> findDynamicallyProjectedBy(Class<T> type);
|
||||
|
||||
Reference in New Issue
Block a user