/*
* Copyright 2016-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.query;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.data.cassandra.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.mapping.BasicCassandraMappingContext;
import org.springframework.data.cassandra.mapping.UserTypeResolver;
import org.springframework.data.cassandra.repository.Query;
import org.springframework.data.cassandra.support.UserTypeBuilder;
import org.springframework.data.cassandra.test.integration.repository.querymethods.declared.Address;
import org.springframework.data.cassandra.test.integration.repository.querymethods.declared.Person;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.AbstractRepositoryMetadata;
import org.springframework.data.repository.query.ExtensionAwareEvaluationContextProvider;
import org.springframework.data.repository.query.Param;
import org.springframework.data.repository.query.QueryCreationException;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.util.ReflectionUtils;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.SimpleStatement;
import com.datastax.driver.core.UDTValue;
import com.datastax.driver.core.UserType;
/**
* Unit tests for {@link StringBasedCassandraQuery}.
*
* @author Matthew T. Adams
* @author Oliver Gierke
* @author Mark Paluch
*/
@RunWith(MockitoJUnitRunner.class)
public class StringBasedCassandraQueryUnitTests {
SpelExpressionParser PARSER = new SpelExpressionParser();
@Mock CassandraOperations operations;
@Mock UserTypeResolver userTypeResolver;
@Mock UDTValue udtValue;
RepositoryMetadata metadata;
MappingCassandraConverter converter;
ProjectionFactory factory;
@Before
@SuppressWarnings("unchecked")
public void setUp() {
BasicCassandraMappingContext mappingContext = new BasicCassandraMappingContext();
mappingContext.setUserTypeResolver(userTypeResolver);
this.metadata = AbstractRepositoryMetadata.getMetadata(SampleRepository.class);
this.converter = new MappingCassandraConverter(mappingContext);
this.factory = new SpelAwareProxyProjectionFactory();
this.converter.afterPropertiesSet();
}
@Test // DATACASS-117
public void bindsIndexParameterCorrectly() {
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByLastname", String.class);
CassandraParametersParameterAccessor accessor = new CassandraParametersParameterAccessor(
cassandraQuery.getQueryMethod(), "Matthews");
SimpleStatement actual = cassandraQuery.createQuery(accessor);
assertThat(actual.toString()).isEqualTo("SELECT * FROM person WHERE lastname = ?;");
assertThat(actual.getObject(0)).isEqualTo("Matthews");
}
@Test // DATACASS-259
public void bindsIndexParameterForComposedQueryAnnotationCorrectly() {
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByComposedQueryAnnotation", String.class);
CassandraParametersParameterAccessor accessor = new CassandraParametersParameterAccessor(
cassandraQuery.getQueryMethod(), "Matthews");
SimpleStatement actual = cassandraQuery.createQuery(accessor);
assertThat(actual.toString()).isEqualTo("SELECT * FROM person WHERE lastname = ?;");
assertThat(actual.getObject(0)).isEqualTo("Matthews");
}
@Test // DATACASS-117
public void bindsAndEscapesIndexParameterCorrectly() {
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByLastname", String.class);
CassandraParametersParameterAccessor accessor = new CassandraParametersParameterAccessor(
cassandraQuery.getQueryMethod(), "Mat\th'ew\"s");
SimpleStatement actual = cassandraQuery.createQuery(accessor);
assertThat(actual.toString()).isEqualTo("SELECT * FROM person WHERE lastname = ?;");
assertThat(actual.getObject(0)).isEqualTo("Mat\th'ew\"s");
}
@Test // DATACASS-117
public void bindsAndEscapesBytesIndexParameterCorrectly() {
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByLastname", String.class);
CassandraParametersParameterAccessor accessor = new CassandraParametersParameterAccessor(
cassandraQuery.getQueryMethod(), ByteBuffer.wrap(new byte[] { 1, 2, 3, 4 }));
SimpleStatement actual = cassandraQuery.createQuery(accessor);
assertThat(actual.toString()).isEqualTo("SELECT * FROM person WHERE lastname = ?;");
assertThat(actual.getObject(0)).isEqualTo(ByteBuffer.wrap(new byte[] { 1, 2, 3, 4 }));
}
@Test // DATACASS-117
public void bindsIndexParameterInListCorrectly() {
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByLastNameIn", Collection.class);
CassandraParametersParameterAccessor accessor = new CassandraParametersParameterAccessor(
cassandraQuery.getQueryMethod(), Arrays.asList("White", "Heisenberg"));
SimpleStatement actual = cassandraQuery.createQuery(accessor);
assertThat(actual.toString()).isEqualTo("SELECT * FROM person WHERE lastname IN (?);");
assertThat(actual.getObject(0)).isEqualTo(Arrays.asList("White", "Heisenberg"));
}
@Test // DATACASS-117
public void bindsIndexParameterIsListCorrectly() {
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByLastNamesAndAge", Collection.class, int.class);
CassandraParametersParameterAccessor accessor = new CassandraParametersParameterAccessor(
cassandraQuery.getQueryMethod(), Arrays.asList("White", "Heisenberg"), 42);
SimpleStatement actual = cassandraQuery.createQuery(accessor);
assertThat(actual.toString()).isEqualTo("SELECT * FROM person WHERE lastnames = [?] AND age = ?;");
assertThat(actual.getObject(0)).isEqualTo(Arrays.asList("White", "Heisenberg"));
assertThat(actual.getObject(1)).isEqualTo(42);
}
@Test(expected = QueryCreationException.class) // DATACASS-117
public void referencingUnknownIndexedParameterShouldFail() {
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByOutOfBoundsLastNameShouldFail", String.class);
CassandraParametersParameterAccessor accessor = new CassandraParametersParameterAccessor(
cassandraQuery.getQueryMethod(), "Hello");
cassandraQuery.createQuery(accessor);
}
@Test(expected = QueryCreationException.class) // DATACASS-117
public void referencingUnknownNamedParameterShouldFail() {
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByUnknownParameterLastNameShouldFail", String.class);
CassandraParametersParameterAccessor accessor = new CassandraParametersParameterAccessor(
cassandraQuery.getQueryMethod(), "Hello");
cassandraQuery.createQuery(accessor);
}
@Test // DATACASS-117
public void bindsIndexParameterInSetCorrectly() {
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByLastNameIn", Collection.class);
CassandraParametersParameterAccessor accessor = new CassandraParametersParameterAccessor(
cassandraQuery.getQueryMethod(), new HashSet<>(Arrays.asList("White", "Heisenberg")));
SimpleStatement actual = cassandraQuery.createQuery(accessor);
assertThat(actual.toString()).isEqualTo("SELECT * FROM person WHERE lastname IN (?);");
assertThat(actual.getObject(0)).isEqualTo(new HashSet<String>(Arrays.asList("White", "Heisenberg")));
}
@Test // DATACASS-117
public void bindsNamedParameterCorrectly() {
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByNamedParameter", String.class, String.class);
CassandraParametersParameterAccessor accessor = new CassandraParametersParameterAccessor(
cassandraQuery.getQueryMethod(), "Walter", "Matthews");
SimpleStatement actual = cassandraQuery.createQuery(accessor);
assertThat(actual.toString()).isEqualTo("SELECT * FROM person WHERE lastname = ?;");
assertThat(actual.getObject(0)).isEqualTo("Matthews");
}
@Test // DATACASS-117
public void bindsIndexExpressionParameterCorrectly() {
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByIndexExpressionParameter", String.class);
CassandraParametersParameterAccessor accessor = new CassandraParametersParameterAccessor(
cassandraQuery.getQueryMethod(), "Matthews");
SimpleStatement actual = cassandraQuery.createQuery(accessor);
assertThat(actual.toString()).isEqualTo("SELECT * FROM person WHERE lastname = ?;");
assertThat(actual.getObject(0)).isEqualTo("Matthews");
}
@Test // DATACASS-117
public void bindsExpressionParameterCorrectly() {
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByExpressionParameter", String.class);
CassandraParametersParameterAccessor accessor = new CassandraParametersParameterAccessor(
cassandraQuery.getQueryMethod(), "Matthews");
SimpleStatement actual = cassandraQuery.createQuery(accessor);
assertThat(actual.toString()).isEqualTo("SELECT * FROM person WHERE lastname = ?;");
assertThat(actual.getObject(0)).isEqualTo("Matthews");
}
@Test // DATACASS-117
public void bindsConditionalExpressionParameterCorrectly() {
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByConditionalExpressionParameter", String.class);
CassandraParametersParameterAccessor accessor = new CassandraParametersParameterAccessor(
cassandraQuery.getQueryMethod(), "Matthews");
SimpleStatement actual = cassandraQuery.createQuery(accessor);
assertThat(actual.toString()).isEqualTo("SELECT * FROM person WHERE lastname = ?;");
assertThat(actual.getObject(0)).isEqualTo("Woohoo");
accessor = new CassandraParametersParameterAccessor(cassandraQuery.getQueryMethod(), "Walter");
actual = cassandraQuery.createQuery(accessor);
assertThat(actual.toString()).isEqualTo("SELECT * FROM person WHERE lastname = ?;");
assertThat(actual.getObject(0)).isEqualTo("Walter");
}
@Test // DATACASS-117
public void bindsReusedParametersCorrectly() {
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByLastnameUsedTwice", String.class);
CassandraParametersParameterAccessor accessor = new CassandraParametersParameterAccessor(
cassandraQuery.getQueryMethod(), "Matthews");
SimpleStatement actual = cassandraQuery.createQuery(accessor);
assertThat(actual.toString()).isEqualTo("SELECT * FROM person WHERE lastname = ? or firstname = ?;");
assertThat(actual.getObject(0)).isEqualTo("Matthews");
assertThat(actual.getObject(1)).isEqualTo("Matthews");
}
@Test // DATACASS-117
public void bindsMultipleParametersCorrectly() {
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByLastnameAndFirstname", String.class, String.class);
CassandraParametersParameterAccessor accessor = new CassandraParametersParameterAccessor(
cassandraQuery.getQueryMethod(), "Matthews", "John");
SimpleStatement actual = cassandraQuery.createQuery(accessor);
assertThat(actual.toString()).isEqualTo("SELECT * FROM person WHERE lastname=? AND firstname=?;");
assertThat(actual.getObject(0)).isEqualTo("Matthews");
assertThat(actual.getObject(1)).isEqualTo("John");
}
@Test // DATACASS-296
public void bindsConvertedParameterCorrectly() {
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByCreatedDate", LocalDate.class);
CassandraParameterAccessor accessor = new ConvertingParameterAccessor(converter,
new CassandraParametersParameterAccessor(cassandraQuery.getQueryMethod(), LocalDate.of(2010, 7, 4)));
SimpleStatement actual = cassandraQuery.createQuery(accessor);
assertThat(actual.toString()).isEqualTo("SELECT * FROM person WHERE createdDate=?;");
assertThat(actual.getObject(0)).isInstanceOf(com.datastax.driver.core.LocalDate.class);
assertThat(actual.getObject(0).toString()).isEqualTo("2010-07-04");
}
@Test // DATACASS-172
public void bindsMappedUdtPropertyCorrectly() throws Exception {
UserType addressType = UserTypeBuilder.forName("address").withField("city", DataType.varchar())
.withField("country", DataType.varchar()).build();
when(userTypeResolver.resolveType(CqlIdentifier.cqlId("address"))).thenReturn(addressType);
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByMainAddress", Address.class);
CassandraParameterAccessor accessor = new ConvertingParameterAccessor(converter,
new CassandraParametersParameterAccessor(cassandraQuery.getQueryMethod(), new Address()));
SimpleStatement stringQuery = cassandraQuery.createQuery(accessor);
assertThat(stringQuery.toString()).isEqualTo("SELECT * FROM person WHERE address=?;");
assertThat(stringQuery.getObject(0).toString()).isEqualTo("{city:NULL,country:NULL}");
}
@Test // DATACASS-172
public void bindsUdtValuePropertyCorrectly() throws Exception {
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByMainAddress", UDTValue.class);
CassandraParameterAccessor accessor = new ConvertingParameterAccessor(converter,
new CassandraParametersParameterAccessor(cassandraQuery.getQueryMethod(), udtValue));
SimpleStatement stringQuery = cassandraQuery.createQuery(accessor);
assertThat(stringQuery.toString()).isEqualTo("SELECT * FROM person WHERE address=?;");
assertThat(stringQuery.getObject(0).toString()).isEqualTo("udtValue");
}
private StringBasedCassandraQuery getQueryMethod(String name, Class<?>... args) {
Method method = ReflectionUtils.findMethod(SampleRepository.class, name, args);
CassandraQueryMethod queryMethod = new CassandraQueryMethod(method, metadata, factory,
converter.getMappingContext());
return new StringBasedCassandraQuery(queryMethod, operations, PARSER,
new ExtensionAwareEvaluationContextProvider());
}
private interface SampleRepository extends Repository<Person, String> {
@Query("SELECT * FROM person WHERE lastname = ?0;")
Person findByLastname(String lastname);
@Query("SELECT * FROM person WHERE lastname = ?0 or firstname = ?0;")
Person findByLastnameUsedTwice(String lastname);
@Query("SELECT * FROM person WHERE lastname = :lastname;")
Person findByNamedParameter(@Param("another") String another, @Param("lastname") String lastname);
@Query("SELECT * FROM person WHERE lastname = :#{[0]};")
Person findByIndexExpressionParameter(String lastname);
@Query("SELECT * FROM person WHERE lastnames = [?0] AND age = ?1;")
Person findByLastNamesAndAge(Collection<String> lastname, int age);
@Query("SELECT * FROM person WHERE lastname = ?0 AND age = ?2;")
Person findByOutOfBoundsLastNameShouldFail(String lastname);
@Query("SELECT * FROM person WHERE lastname = :unknown;")
Person findByUnknownParameterLastNameShouldFail(String lastname);
@Query("SELECT * FROM person WHERE lastname IN (?0);")
Person findByLastNameIn(Collection<String> lastNames);
@Query("SELECT * FROM person WHERE lastname = :#{#lastname};")
Person findByExpressionParameter(@Param("lastname") String lastname);
@Query("SELECT * FROM person WHERE lastname = :#{#lastname == 'Matthews' ? 'Woohoo' : #lastname};")
Person findByConditionalExpressionParameter(@Param("lastname") String lastname);
@Query("SELECT * FROM person WHERE lastname=?0 AND firstname=?1;")
Person findByLastnameAndFirstname(String lastname, String firstname);
@Query("SELECT * FROM person WHERE createdDate=?0;")
Person findByCreatedDate(LocalDate createdDate);
@Query("SELECT * FROM person WHERE address=?0;")
Person findByMainAddress(Address address);
@Query("SELECT * FROM person WHERE address=?0;")
Person findByMainAddress(UDTValue udtValue);
@ComposedQueryAnnotation
Person findByComposedQueryAnnotation(String lastname);
}
@Retention(RetentionPolicy.RUNTIME)
@Query("SELECT * FROM person WHERE lastname = ?0;")
@interface ComposedQueryAnnotation {
}
}