DATACASS-7 - Polish.

Origin pull request: #74.
This commit is contained in:
John Blum
2016-07-24 19:17:10 -07:00
parent 43fc7518b3
commit f96e9383f3
22 changed files with 578 additions and 573 deletions

View File

@@ -83,7 +83,6 @@ public class BasicCassandraMappingContext
* Creates a new {@link BasicCassandraMappingContext}.
*/
public BasicCassandraMappingContext() {
setCustomConversions(new CustomConversions(Collections.EMPTY_LIST));
setSimpleTypeHolder(CassandraSimpleTypeHolder.HOLDER);
}
@@ -95,7 +94,6 @@ public class BasicCassandraMappingContext
* @since 1.5
*/
public void setCustomConversions(CustomConversions customConversions) {
Assert.notNull(customConversions, "CustomConversions must not be null");
this.customConversions = customConversions;
@@ -127,6 +125,7 @@ public class BasicCassandraMappingContext
if (includePrimaryKeyTypes) {
return super.getPersistentEntities();
}
return Collections.unmodifiableSet(nonPrimaryKeyEntities);
}
@@ -296,55 +295,50 @@ public class BasicCassandraMappingContext
public DataType getDataType(Class<?> type) {
return (customConversions.hasCustomWriteTarget(type)
? getDataTypeFor(customConversions.getCustomWriteTarget(type))
: getDataTypeFor(type));
? getDataTypeFor(customConversions.getCustomWriteTarget(type)) : getDataTypeFor(type));
}
public void setMapping(Mapping mapping) {
Assert.notNull(mapping, "Mapping must not be null");
this.mapping = mapping;
}
protected void processMappingOverrides() {
if (mapping != null) {
for (EntityMapping entityMapping : mapping.getEntityMappings()) {
if (entityMapping == null) {
continue;
}
if (entityMapping != null) {
String entityClassName = entityMapping.getEntityClassName();
String entityClassName = entityMapping.getEntityClassName();
try {
Class<?> entityClass = ClassUtils.forName(entityClassName, beanClassLoader);
try {
Class<?> entityClass = ClassUtils.forName(entityClassName, beanClassLoader);
CassandraPersistentEntity<?> entity = getPersistentEntity(entityClass);
CassandraPersistentEntity<?> entity = getPersistentEntity(entityClass);
if (entity == null) {
throw new IllegalStateException(String.format(
"Unknown persistent entity class name [%s]", entityClassName));
}
if (entity == null) {
throw new IllegalStateException(String.format("unknown persistent entity class name [%s]", entityClassName));
String tableName = entityMapping.getTableName();
if (StringUtils.hasText(tableName)) {
entity.setTableName(cqlId(tableName, Boolean.valueOf(entityMapping.getForceQuote())));
}
processMappingOverrides(entity, entityMapping);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(String.format(
"unknown persistent entity name [%s]", entityClassName), e);
}
String tableName = entityMapping.getTableName();
if (StringUtils.hasText(tableName)) {
entity.setTableName(cqlId(tableName, Boolean.valueOf(entityMapping.getForceQuote())));
}
processMappingOverrides(entity, entityMapping);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(String.format(
"unknown persistent entity name [%s]", entityClassName), e);
}
}
}
}
protected void processMappingOverrides(CassandraPersistentEntity<?> entity, EntityMapping entityMapping) {
for (PropertyMapping mapping : entityMapping.getPropertyMappings().values()) {
processMappingOverride(entity, mapping);
}
@@ -356,7 +350,7 @@ public class BasicCassandraMappingContext
if (property == null) {
throw new IllegalArgumentException(String.format("Entity class [%s] has no persistent property named [%s]",
entity.getType().getName(), mapping.getPropertyName()));
entity.getType().getName(), mapping.getPropertyName()));
}
boolean forceQuote = false;

View File

@@ -32,6 +32,7 @@ import org.springframework.data.mapping.model.MappingException;
*
* @author Matthew T Adams
* @author David Webb
* @author John Blum
*/
public class BasicCassandraPersistentEntityMetadataVerifier implements CassandraPersistentEntityMetadataVerifier {
@@ -40,14 +41,15 @@ public class BasicCassandraPersistentEntityMetadataVerifier implements Cassandra
protected boolean strict = false;
@Override
@SuppressWarnings("all")
public void verify(CassandraPersistentEntity<?> entity) throws MappingException {
if(entity.getType().isInterface()){
if (entity.getType().isInterface()){
return;
}
VerifierMappingExceptions exceptions = new VerifierMappingExceptions(entity,
String.format("Mapping Exceptions from BasicCassandraPersistentEntityMetadataVerifier for %s", entity.getName()));
VerifierMappingExceptions exceptions = new VerifierMappingExceptions(entity, String.format(
"Mapping Exceptions from BasicCassandraPersistentEntityMetadataVerifier for %s", entity.getName()));
final List<CassandraPersistentProperty> idProperties = new ArrayList<CassandraPersistentProperty>();
final List<CassandraPersistentProperty> compositePrimaryKeys = new ArrayList<CassandraPersistentProperty>();
@@ -55,49 +57,41 @@ public class BasicCassandraPersistentEntityMetadataVerifier implements Cassandra
final List<CassandraPersistentProperty> clusterKeyColumns = new ArrayList<CassandraPersistentProperty>();
final List<CassandraPersistentProperty> primaryKeyColumns = new ArrayList<CassandraPersistentProperty>();
/*
* Determine how this type is annotated
*/
Class<?> thisType = entity.getType();
Class<?> entityType = entity.getType();
boolean isTable = (thisType.isAnnotationPresent(Table.class) || thisType.isAnnotationPresent(Persistent.class));
boolean isPrimaryKeyClass = thisType.isAnnotationPresent(PrimaryKeyClass.class);
boolean isTable = (entityType.isAnnotationPresent(Table.class)
|| entityType.isAnnotationPresent(Persistent.class));
/*
* Ensure that this is not both a @Table(@Persistent) and a @PrimaryKey
*/
boolean isPrimaryKeyClass = entityType.isAnnotationPresent(PrimaryKeyClass.class);
// Ensure entity is not both a @Table(@Persistent) and a @PrimaryKey
if (isTable && isPrimaryKeyClass) {
exceptions.add(new MappingException("Entity cannot be of type Table and PrimaryKey"));
throw exceptions;
}
/*
* Ensure that this is either a @Table(@Persistent) or a @PrimaryKey
*/
// Ensure entity is either a @Table/@Persistent or a @PrimaryKey
if (!isTable && !isPrimaryKeyClass) {
exceptions.add(new MappingException(
"Cassandra entities must have the @Table, @Persistent or @PrimaryKeyClass Annotation"));
"Cassandra entities must have the @Table, @Persistent or @PrimaryKeyClass Annotation"));
throw exceptions;
}
/*
* Parse the properties
*/
// Parse entity properties
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty p) {
if (p.isIdProperty()) {
idProperties.add(p);
} else if (p.isCompositePrimaryKey()) {
compositePrimaryKeys.add(p);
} else if (p.isPartitionKeyColumn()) {
partitionKeyColumns.add(p);
primaryKeyColumns.add(p);
} else if (p.isClusterKeyColumn()) {
clusterKeyColumns.add(p);
primaryKeyColumns.add(p);
public void doWithPersistentProperty(CassandraPersistentProperty property) {
if (property.isIdProperty()) {
idProperties.add(property);
} else if (property.isClusterKeyColumn()) {
clusterKeyColumns.add(property);
primaryKeyColumns.add(property);
} else if (property.isCompositePrimaryKey()) {
compositePrimaryKeys.add(property);
} else if (property.isPartitionKeyColumn()) {
partitionKeyColumns.add(property);
primaryKeyColumns.add(property);
}
}
});
@@ -106,92 +100,51 @@ public class BasicCassandraPersistentEntityMetadataVerifier implements Cassandra
final int partitionKeyColumnCount = partitionKeyColumns.size();
final int primaryKeyColumnCount = primaryKeyColumns.size();
/*
* Perform rules verification on PrimaryKeyClass
*/
// Perform rules verification on PrimaryKeyClass
if (isPrimaryKeyClass) {
/*
* Must have at least 1 attribute annotated with @PrimaryKeyColumn
*/
// Must have at least 1 attribute annotated with @PrimaryKeyColumn
if (primaryKeyColumnCount == 0) {
exceptions.add(new MappingException(String.format(
"composite primary key type [%s] has no fields annotated with @%s", entity.getType().getName(),
"Composite primary key type [%s] has no fields annotated with @%s", entity.getType().getName(),
PrimaryKeyColumn.class.getSimpleName())));
}
/*
* At least one of the PrimaryKeyColumns must have a type PARTIONED
*/
// At least one of the PrimaryKeyColumns must have a type PARTIONED
if (partitionKeyColumnCount == 0) {
exceptions.add(new MappingException(
"At least one of the @PrimaryKeyColumn annotation must have a type of PARTITIONED"));
"At least one of the @PrimaryKeyColumn annotations must have a type of PARTITIONED"));
}
/*
* Cannot have any Id or PrimaryKey Annotations
*/
// Cannot have any Id or PrimaryKey Annotations
if (idPropertyCount > 0) {
exceptions.add(new MappingException(
"Annotations @Id and @PrimaryKey are invalid for type annotated with @PrimaryKeyClass"));
"Annotations @Id and @PrimaryKey are invalid for type annotated with @PrimaryKeyClass"));
}
/*
* Ensure that PrimaryKeyColumn is a supported Type.
*/
for (CassandraPersistentProperty p : primaryKeyColumns) {
if (CassandraSimpleTypeHolder.getDataTypeFor(p.getType()) == null) {
exceptions.add(new MappingException("Fields annotated with @PrimaryKeyColumn must be simple CassandraTypes"));
// Ensure that PrimaryKeyColumn is a supported Type.
for (CassandraPersistentProperty property : primaryKeyColumns) {
if (CassandraSimpleTypeHolder.getDataTypeFor(property.getType()) == null) {
exceptions.add(new MappingException(
"Fields annotated with @PrimaryKeyColumn must be simple CassandraTypes"));
}
}
/*
* Ensure PrimaryKeyClass is Serializable
*/
if (!Serializable.class.isAssignableFrom(thisType)) {
// Ensure PrimaryKeyClass is Serializable
if (!Serializable.class.isAssignableFrom(entityType)) {
exceptions.add(new MappingException("@PrimaryKeyClass must be Serializable"));
}
/*
* Ensure PrimaryKeyClass only extends Object
*/
if (!thisType.getSuperclass().equals(Object.class)) {
// Ensure PrimaryKeyClass only extends Object
if (!entityType.getSuperclass().equals(Object.class)) {
exceptions.add(new MappingException("@PrimaryKeyClass must only extend Object"));
}
/*
* Check that PrimaryKeyClass overrides "boolean equals(Object)"
*/
try {
Method equalsMethod = thisType.getDeclaredMethod("equals", Object.class);
if (equalsMethod == null || !equalsMethod.getDeclaringClass().equals(thisType)) {
throw new NoSuchMethodException();
}
} catch (NoSuchMethodException e) {
String message = "@PrimaryKeyClass should override 'boolean equals(Object)' method and use all @PrimaryKeyColumn fields";
if (strict) {
exceptions.add(new MappingException(message, e));
} else {
log.warn(message);
}
}
// Check that PrimaryKeyClass overrides "boolean equals(Object)"
verifyMethodPresent(entityType, "equals", "boolean equals(Object)", exceptions);
/*
* Ensure PrimaryKeyClass overrides "int hashCode()"
*/
try {
Method hashCodeMethod = thisType.getDeclaredMethod("hashCode", (Class<?>[]) null);
if (hashCodeMethod == null || !hashCodeMethod.getDeclaringClass().equals(thisType)) {
throw new NoSuchMethodException();
}
} catch (NoSuchMethodException e) {
String message = "@PrimaryKeyClass should override 'int hashCode()' method and use all @PrimaryKeyColumn fields";
if (strict) {
exceptions.add(new MappingException(message, e));
} else {
log.warn(message);
}
}
// Ensure PrimaryKeyClass overrides "int hashCode()"
verifyMethodPresent(entityType, "hashCode", "int hashCode()", exceptions);
}
/*
@@ -199,75 +152,88 @@ public class BasicCassandraPersistentEntityMetadataVerifier implements Cassandra
*/
if (isTable) {
/*
* TODO Verify annotation values with CqlIndentifier
*/
// TODO Verify annotation values with CqlIndentifier
/*
* Ensure only one PK or at least one partitioned PKC and not both PK(s) & PKC(s)
*/
// Ensure only one PK or at least one partitioned PK Column and not both PK(s) & PK Column(s) exist
if (primaryKeyColumnCount == 0) {
/*
* Can only have one PK.
*/
// Can only have one PK
if (idPropertyCount != 1) {
exceptions
.add(new MappingException(String.format(
"@Table/@Persistent types must have only one @PrimaryKey attribute, if any. Found %s.",
idPropertyCount)));
exceptions.add(new MappingException(String.format(
"@Table/@Persistent types must have only one @PrimaryKey attribute, if any; Found %s",
idPropertyCount)));
throw exceptions;
}
/*
* Ensure that Id is a supported Type. At the point there is only 1.
*/
Class<?> typeClass = idProperties.get(0).getType();
if (!typeClass.isAnnotationPresent(PrimaryKeyClass.class)
&& CassandraSimpleTypeHolder.getDataTypeFor(typeClass) == null) {
// Ensure that Id is a supported Type. At this point there is only 1.
Class<?> idType = idProperties.get(0).getType();
if (!idType.isAnnotationPresent(PrimaryKeyClass.class)
&& CassandraSimpleTypeHolder.getDataTypeFor(idType) == null) {
exceptions.add(new MappingException(
"Fields annotated with @PrimaryKey must be simple CassandraTypes or @PrimaryKeyClass type"));
"Fields annotated with @PrimaryKey must be simple CassandraTypes or @PrimaryKeyClass type"));
}
} else if (idPropertyCount > 0) {
/*
* Then we have both PK(s) & PKC(s)
*/
exceptions
.add(new MappingException(
String
.format(
"@Table/@Persistent types must not define both @PrimaryKeyColumn field%s (found %s) and @PrimaryKey field%s (found %s)",
primaryKeyColumnCount == 1 ? "" : "s", primaryKeyColumnCount, idPropertyCount == 1 ? "" : "s",
idPropertyCount)));
// Then we have both PK(s) & PK Column(s)
exceptions.add(new MappingException(String.format(
"@Table/@Persistent types must not define both @PrimaryKeyColumn field(s) (found %s) and @PrimaryKey field(s) (found %s)",
primaryKeyColumnCount, idPropertyCount)));
throw exceptions;
} else {
/*
* We have no PKs & only PKC(s) -- ensure at least one is of type PARTITIONED
*/
// We have no PKs & only PK Column(s); ensure at least one is of type PARTITIONED
if (partitionKeyColumnCount == 0) {
exceptions.add(new MappingException(String
.format("@Table/@Persistent types must define at least one @PrimaryKeyColumn of type PARTITIONED")));
exceptions.add(new MappingException(String.format(
"@Table/@Persistent types must define at least one @PrimaryKeyColumn of type PARTITIONED")));
}
}
}
/*
* Determine whether or not to throw Exception based on errors found
*/
// Determine whether or not to throw Exception based on errors found
if (exceptions.getCount() > 0) {
log.error("Exceptions while verifying PersistentEntity", exceptions);
throw exceptions;
}
}
boolean verifyMethodPresent(Class<?> type, String methodName, String methodDescription,
VerifierMappingExceptions exceptions) {
try {
Method method = type.getDeclaredMethod(methodName, Object.class);
if (method == null || !method.getDeclaringClass().equals(type)) {
throw new NoSuchMethodException();
}
return true;
} catch (NoSuchMethodException e) {
String message = String.format(
"@PrimaryKeyClass should override '%s' method and use all @PrimaryKeyColumn fields",
methodDescription);
if (strict) {
exceptions.add(new MappingException(message, e));
} else {
log.warn(message);
}
return false;
}
}
/**
* @return Returns the strict.
* @return the setting for strict.
*/
@SuppressWarnings("unused")
public boolean isStrict() {
return strict;
}
/**
* @param strict The strict to set.
* @param strict boolean setting for strict.
*/
@SuppressWarnings("unused")
public void setStrict(boolean strict) {
this.strict = strict;
}

View File

@@ -22,6 +22,7 @@ import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.cassandra.core.Ordering;
@@ -41,6 +42,7 @@ import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.DataType;
@@ -52,6 +54,7 @@ import com.datastax.driver.core.DataType;
* @author Matthew T. Adams
* @author Antoine Toulme
* @author Mark Paluch
* @author John Blum
*/
public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentProperty<CassandraPersistentProperty>
implements CassandraPersistentProperty, ApplicationContextAware {
@@ -154,8 +157,8 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
if (dataType == null) {
throw new InvalidDataAccessApiUsageException(String.format(
"unknown type for property [%s], type [%s] in entity [%s]; only primitive types and collections or maps of primitive types are allowed",
getName(), getType(), getOwner().getName()));
"Unknown type [%s] for property [%s] in entity [%s]; only primitive types and Collections or Maps of primitive types are allowed",
getType(), getName(), getOwner().getName()));
}
return dataType;
@@ -178,7 +181,6 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
}
if (isCollectionLike()) {
List<TypeInformation<?>> args = getTypeInformation().getTypeArguments();
ensureTypeArguments(args.size(), 1);
@@ -220,20 +222,20 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
return isAnnotationPresent(Indexed.class);
}
@Override
public boolean isPartitionKeyColumn() {
PrimaryKeyColumn primaryKeyColumn = findAnnotation(PrimaryKeyColumn.class);
return (primaryKeyColumn != null && primaryKeyColumn.type() == PrimaryKeyType.PARTITIONED);
}
@Override
public boolean isClusterKeyColumn() {
PrimaryKeyColumn primaryKeyColumn = findAnnotation(PrimaryKeyColumn.class);
return (primaryKeyColumn != null && primaryKeyColumn.type() == PrimaryKeyType.CLUSTERED);
return (primaryKeyColumn != null && PrimaryKeyType.CLUSTERED.equals(primaryKeyColumn.type()));
}
@Override
public boolean isPartitionKeyColumn() {
PrimaryKeyColumn primaryKeyColumn = findAnnotation(PrimaryKeyColumn.class);
return (primaryKeyColumn != null && PrimaryKeyType.PARTITIONED.equals(primaryKeyColumn.type()));
}
@Override
@@ -241,12 +243,13 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
return isAnnotationPresent(PrimaryKeyColumn.class);
}
protected DataType getDataTypeFor(DataType.Name typeName) {
DataType dataType = CassandraSimpleTypeHolder.getDataTypeFor(typeName);
protected DataType getDataTypeFor(DataType.Name dataTypeName) {
DataType dataType = CassandraSimpleTypeHolder.getDataTypeFor(dataTypeName);
if (dataType == null) {
throw new InvalidDataAccessApiUsageException(String.format(
"only primitive types are allowed inside collections for the property '%1$s' type is '%2$s' in the entity %3$s",
"Only primitive types are allowed inside Collections for property [%1$s] of type [%2$s] in entity [%3$s]",
getName(), getType(), getOwner().getName()));
}
@@ -254,11 +257,12 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
}
protected DataType getDataTypeFor(Class<?> javaType) {
DataType dataType = CassandraSimpleTypeHolder.getDataTypeFor(javaType);
if (dataType == null) {
throw new InvalidDataAccessApiUsageException(String.format(
"only primitive types are allowed inside collections for the property '%1$s' type is '%2$s' in the entity %3$s",
"Only primitive types are allowed inside Collections for property [%1$s] of type ['%2$s'] in entity [%3$s]",
getName(), getType(), getOwner().getName()));
}
@@ -268,8 +272,8 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
protected void ensureTypeArguments(int args, int expected) {
if (args != expected) {
throw new InvalidDataAccessApiUsageException(
String.format("expected %1$s of typed arguments for the property '%2$s' type is '%3$s' in the entity %4$s",
expected, getName(), getType(), getOwner().getName()));
String.format("Expected [%1$s] typed arguments for property ['%2$s'] of type ['%3$s'] in entity [%4$s]",
expected, getName(), getType(), getOwner().getName()));
}
}
@@ -320,7 +324,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
String name = defaultName;
if (StringUtils.hasText(overriddenName)) {
name = (spelContext == null ? overriddenName : SpelUtils.evaluate(overriddenName, spelContext));
name = (spelContext != null ? SpelUtils.evaluate(overriddenName, spelContext) : overriddenName);
}
return cqlId(name, forceQuote);
@@ -331,11 +335,11 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
compositePrimaryKeyEntity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
@Override
public void doWithPersistentProperty(CassandraPersistentProperty p) {
if (p.isCompositePrimaryKey()) {
addCompositePrimaryKeyColumnNames(p.getCompositePrimaryKeyEntity(), columnNames);
public void doWithPersistentProperty(CassandraPersistentProperty property) {
if (property.isCompositePrimaryKey()) {
addCompositePrimaryKeyColumnNames(property.getCompositePrimaryKeyEntity(), columnNames);
} else {
columnNames.add(p.getColumnName());
columnNames.add(property.getColumnName());
}
}
});
@@ -353,14 +357,14 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
Assert.notNull(columnNames);
// force calculation of columnNames if not yet known
// force calculation of columnNames if not known yet
getColumnNames();
if (this.columnNames.size() != columnNames.size()) {
throw new IllegalStateException(String.format(
"property [%s] on entity [%s] is mapped to [%s] column%s, but given column name list has size [%s]",
getName(), getOwner().getType().getName(), this.columnNames.size(), this.columnNames.size() == 1 ? "" : "s",
columnNames.size()));
"Property [%s] of entity [%s] is mapped to [%s] column%s, but given column name list has size [%s]",
getName(), getOwner().getType().getName(), this.columnNames.size(),
this.columnNames.size() == 1 ? "" : "s", columnNames.size()));
}
this.columnNames = this.explicitColumnNames =
@@ -390,8 +394,8 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
public List<CassandraPersistentProperty> getCompositePrimaryKeyProperties() {
if (!isCompositePrimaryKey()) {
throw new IllegalStateException(
String.format("[%s] does not represent a composite primary key property", getName()));
throw new IllegalStateException(String.format(
"[%s] does not represent a composite primary key property", getName()));
}
return getCompositePrimaryKeyEntity().getCompositePrimaryKeyProperties();
@@ -399,6 +403,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
@Override
public CassandraPersistentEntity<?> getCompositePrimaryKeyEntity() {
CassandraMappingContext mappingContext = getOwner().getMappingContext();
if (mappingContext == null) {
@@ -417,4 +422,9 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
protected Association<CassandraPersistentProperty> createAssociation() {
return new Association<CassandraPersistentProperty>(this, null);
}
@Override
public boolean isMapLike() {
return ClassUtils.isAssignable(Map.class, getType());
}
}

View File

@@ -1,12 +1,12 @@
/*
* Copyright 2013-2016 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.
@@ -29,11 +29,12 @@ import com.datastax.driver.core.DataType;
/**
* Cassandra specific {@link org.springframework.data.mapping.PersistentProperty} extension.
*
*
* @author Alex Shvid
* @author Matthew T. Adams
* @author David T. Webb
* @author Mark Paluch
* @author John Blum
*/
public interface CassandraPersistentProperty
extends PersistentProperty<CassandraPersistentProperty>, ApplicationContextAware {
@@ -81,7 +82,7 @@ public interface CassandraPersistentProperty
/**
* The column's data type. Not valid for a composite primary key.
*
*
* @return the Cassandra {@link DataType}
* @throws InvalidDataAccessApiUsageException if the {@link DataType} cannot be resolved
* @see CassandraType
@@ -105,7 +106,7 @@ public interface CassandraPersistentProperty
/**
* Whether the property is a partition key column or a cluster key column
*
*
* @see #isPartitionKeyColumn()
* @see #isClusterKeyColumn()
*/
@@ -116,7 +117,7 @@ public interface CassandraPersistentProperty
/**
* Whether to force-quote the column names of this property.
*
*
* @param forceQuote
* @see CassandraPersistentProperty#getColumnNames()
*/
@@ -126,7 +127,7 @@ public interface CassandraPersistentProperty
* If this property is mapped with a single column, set the column name to the given {@link CqlIdentifier}. If this
* property is not mapped by a single column, throws {@link IllegalStateException}. If the given column name is null,
* {@link IllegalArgumentException} is thrown.
*
*
* @param columnName
*/
void setColumnName(CqlIdentifier columnName);
@@ -134,18 +135,25 @@ public interface CassandraPersistentProperty
/**
* Sets this property's column names to the collection given. The given collection must have the same size as this
* property's current list of column names, and must contain no <code>null</code> elements.
*
*
* @param columnName
*/
void setColumnNames(List<CqlIdentifier> columnNames);
public enum PropertyToFieldNameConverter implements Converter<CassandraPersistentProperty, String> {
/**
* Returns whether the property is a {@link java.util.Map}.
*
* @return a boolean indicating whether this property type is a {@link java.util.Map}.
*/
boolean isMapLike();
enum PropertyToFieldNameConverter implements Converter<CassandraPersistentProperty, String> {
INSTANCE;
@Override
public String convert(CassandraPersistentProperty source) {
return source.getColumnName().toCql();
public String convert(CassandraPersistentProperty property) {
return property.getColumnName().toCql();
}
}
}

View File

@@ -51,27 +51,28 @@ import com.datastax.driver.core.Row;
* Base class for {@link RepositoryQuery} implementations for Cassandra.
*
* @author Mark Paluch
* @author John Blum
*/
public abstract class AbstractCassandraQuery implements RepositoryQuery {
protected static Logger log = LoggerFactory.getLogger(AbstractCassandraQuery.class);
private final CassandraQueryMethod method;
private final CassandraQueryMethod queryMethod;
private final CassandraOperations template;
/**
* Creates a new {@link AbstractCassandraQuery} from the given {@link CassandraQueryMethod} and
* {@link CassandraOperations}.
*
* @param method must not be {@literal null}.
* @param queryMethod must not be {@literal null}.
* @param operations must not be {@literal null}.
*/
public AbstractCassandraQuery(CassandraQueryMethod method, CassandraOperations operations) {
public AbstractCassandraQuery(CassandraQueryMethod queryMethod, CassandraOperations operations) {
Assert.notNull(method, "CassandraQueryMethod must not be null");
Assert.notNull(queryMethod, "CassandraQueryMethod must not be null");
Assert.notNull(operations, "CassandraOperations must not be null");
this.method = method;
this.queryMethod = queryMethod;
this.template = operations;
}
@@ -80,7 +81,7 @@ public abstract class AbstractCassandraQuery implements RepositoryQuery {
*/
@Override
public CassandraQueryMethod getQueryMethod() {
return method;
return queryMethod;
}
/* (non-Javadoc)
@@ -89,22 +90,23 @@ public abstract class AbstractCassandraQuery implements RepositoryQuery {
@Override
public Object execute(Object[] parameters) {
CassandraParameterAccessor accessor = new ConvertingParameterAccessor(template.getConverter(),
new CassandraParametersParameterAccessor(method, parameters));
String query = createQuery(accessor);
CassandraParameterAccessor parameterAccessor = new ConvertingParameterAccessor(template.getConverter(),
new CassandraParametersParameterAccessor(queryMethod, parameters));
ResultProcessor processor = method.getResultProcessor().withDynamicProjection(accessor);
String query = createQuery(parameterAccessor);
CassandraQueryExecution cassandraQueryExecution = getExecution(query, accessor,
new ResultProcessingConverter(processor));
ResultProcessor resultProcessor = queryMethod.getResultProcessor().withDynamicProjection(parameterAccessor);
CassandraReturnedType returnedType = new CassandraReturnedType(processor.getReturnedType(), template.getConverter().getCustomConversions());
CassandraQueryExecution queryExecution = getExecution(query, parameterAccessor,
new ResultProcessingConverter(resultProcessor));
if (returnedType.isProjecting()) {
return cassandraQueryExecution.execute(query, returnedType.getDomainType());
}
CassandraReturnedType returnedType = new CassandraReturnedType(resultProcessor.getReturnedType(),
template.getConverter().getCustomConversions());
return cassandraQueryExecution.execute(query, returnedType.getReturnedType());
Class<?> resultType = (returnedType.isProjecting() ? returnedType.getDomainType()
: returnedType.getReturnedType());
return queryExecution.execute(query, resultType);
}
/**
@@ -123,11 +125,11 @@ public abstract class AbstractCassandraQuery implements RepositoryQuery {
private CassandraQueryExecution getExecutionToWrap(CassandraParameterAccessor accessor,
Converter<Object, Object> resultProcessing) {
if (method.isCollectionQuery()) {
if (queryMethod.isCollectionQuery()) {
return new CollectionExecution(template);
} else if (method.isResultSetQuery()) {
} else if (queryMethod.isResultSetQuery()) {
return new ResultSetQuery(template);
} else if (method.isStreamQuery()) {
} else if (queryMethod.isStreamQuery()) {
return new StreamExecution(template, resultProcessing);
} else {
return new SingleEntityExecution(template);
@@ -145,7 +147,7 @@ public abstract class AbstractCassandraQuery implements RepositoryQuery {
public Object getCollectionOfEntity(ResultSet resultSet, Class<?> declaredReturnType,
Class<?> returnedUnwrappedObjectType) {
Collection<Object> results = null;
Collection<Object> results;
if (ClassUtils.isAssignable(SortedSet.class, declaredReturnType)) {
results = new TreeSet<Object>();
@@ -156,6 +158,7 @@ public abstract class AbstractCassandraQuery implements RepositoryQuery {
}
CassandraConverter converter = template.getConverter();
for (Row row : resultSet) {
results.add(converter.read(returnedUnwrappedObjectType, row));
}
@@ -171,45 +174,51 @@ public abstract class AbstractCassandraQuery implements RepositoryQuery {
*/
@Deprecated
public Object getSingleEntity(ResultSet resultSet, Class<?> type) {
if (resultSet.isExhausted()) {
return null;
Object result = (resultSet.isExhausted() ? null : template.getConverter().read(type, resultSet.one()));
warnIfMoreResults(resultSet);
return result;
}
private void warnIfMoreResults(ResultSet resultSet) {
if (log.isWarnEnabled() && !resultSet.isExhausted()) {
int count = 0;
while (resultSet.one() != null) {
count++;
}
log.warn("ignoring extra {} row{}", count, count == 1 ? "" : "s");
}
Iterator<Row> iterator = resultSet.iterator();
Object object = template.getConverter().read(type, iterator.next());
warnIfMoreResults(iterator);
return object;
}
@Deprecated
protected void warnIfMoreResults(Iterator<Row> iterator) {
if (log.isWarnEnabled() && iterator.hasNext()) {
int count = 0;
int i = 0;
while (iterator.hasNext()) {
iterator.next();
i++;
for ( ; iterator.hasNext(); iterator.next()) {
count++;
}
log.warn("ignoring extra {} row{}", i, i == 1 ? "" : "s");
log.warn("ignoring extra {} row{}", count, count == 1 ? "" : "s");
}
}
@Deprecated
public ConversionService getConversionService() {
return template.getConverter().getConversionService();
}
/**
* @param conversionService
* @deprecated {@link org.springframework.data.cassandra.mapping.CassandraMappingContext} handles type conversion.
*/
@Deprecated
public void setConversionService(ConversionService conversionService) {
throw new UnsupportedOperationException("setConversionService(ConversionService) is not supported anymore. "
+ "Please use CassandraMappingContext instead");
+ "Please use CassandraMappingContext instead");
}
@Deprecated
public ConversionService getConversionService() {
return template.getConverter().getConversionService();
}
/**
@@ -229,37 +238,33 @@ public abstract class AbstractCassandraQuery implements RepositoryQuery {
this.customConversions = customConversions;
}
boolean isProjecting(){
boolean isProjecting() {
if(!returnedType.isProjecting()){
if (!returnedType.isProjecting()) {
return false;
}
// Spring Data Cassandra allows List<Map<String, Object> and Map<String, Object> declarations on query methods
// so we don't want to let projection kick in
if(ClassUtils.isAssignable(Map.class, returnedType.getReturnedType())){
// Spring Data Cassandra allows List<Map<String, Object> and Map<String, Object> declarations
// on query methods so we don't want to let projection kick in
if (ClassUtils.isAssignable(Map.class, returnedType.getReturnedType())) {
return false;
}
// Type conversion using registered conversions is handled on template level
if(customConversions.hasCustomWriteTarget(returnedType.getReturnedType())){
if (customConversions.hasCustomWriteTarget(returnedType.getReturnedType())) {
return false;
}
// Don't apply projection on Cassandra simple types
if(customConversions.isSimpleType(returnedType.getReturnedType())){
return false;
}
return true;
}
Class<?> getReturnedType() {
return returnedType.getReturnedType();
return !customConversions.isSimpleType(returnedType.getReturnedType());
}
Class<?> getDomainType() {
return returnedType.getDomainType();
}
Class<?> getReturnedType() {
return returnedType.getReturnedType();
}
}
}

View File

@@ -19,13 +19,11 @@ import java.lang.reflect.Method;
import java.util.List;
import org.springframework.core.MethodParameter;
import org.springframework.data.cassandra.mapping.CassandraSimpleTypeHolder;
import org.springframework.data.cassandra.mapping.CassandraType;
import org.springframework.data.cassandra.repository.query.CassandraParameters.CassandraParameter;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.Parameters;
import com.datastax.driver.core.DataType;
import org.springframework.util.Assert;
/**
* Custom extension of {@link Parameters} discovering additional properties of query method parameters.
@@ -78,14 +76,11 @@ public class CassandraParameters extends Parameters<CassandraParameters, Cassand
super(parameter);
if (parameter.hasParameterAnnotation(CassandraType.class)) {
CassandraType cassandraType = parameter.getParameterAnnotation(CassandraType.class);
if (cassandraType.type() == null) {
throw new IllegalArgumentException(
String.format("You must specify the type() when annotating method parameters with @%s",
CassandraType.class.getSimpleName()));
}
Assert.notNull(cassandraType.type(), String.format(
"You must specify the type() when annotating method parameters with @%s",
CassandraType.class.getSimpleName()));
this.cassandraType = cassandraType;
} else {
@@ -95,7 +90,7 @@ public class CassandraParameters extends Parameters<CassandraParameters, Cassand
/**
* Returns the {@link CassandraType} for the declared parameter if specified using {@link org.springframework.data.cassandra.mapping.CassandraType}.
*
*
* @return the {@link CassandraType} or {@literal null}.
*/
public CassandraType getCassandraType() {

View File

@@ -33,7 +33,7 @@ public class CassandraParametersParameterAccessor extends ParametersParameterAcc
/**
* Creates a new {@link CassandraParametersParameterAccessor}.
*
*
* @param method must not be {@literal null}.
* @param values must not be {@literal null}.
*/
@@ -41,14 +41,16 @@ public class CassandraParametersParameterAccessor extends ParametersParameterAcc
super(method.getParameters(), values);
}
/* (non-Javadoc)
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.repository.query.CassandraParameterAccessor#findCassandraType(int)
*/
public CassandraType findCassandraType(int index) {
return getParameters().getParameter(index).getCassandraType();
}
/* (non-Javadoc)
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.repository.query.CassandraParameterAccessor#getDataType(int)
*/
@Override
@@ -56,22 +58,12 @@ public class CassandraParametersParameterAccessor extends ParametersParameterAcc
CassandraType cassandraType = findCassandraType(index);
if (cassandraType != null) {
return CassandraSimpleTypeHolder.getDataTypeFor(cassandraType.type());
}
return CassandraSimpleTypeHolder.getDataTypeFor(getParameterType(index));
return (cassandraType != null ? CassandraSimpleTypeHolder.getDataTypeFor(cassandraType.type())
: CassandraSimpleTypeHolder.getDataTypeFor(getParameterType(index)));
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.repository.query.CassandraParameterAccessor#getParameterType(int)
*/
@Override
public Class<?> getParameterType(int index) {
return getParameters().getParameter(index).getType();
}
/* (non-Javadoc)
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.ParametersParameterAccessor#getParameters()
*/
@Override
@@ -79,4 +71,12 @@ public class CassandraParametersParameterAccessor extends ParametersParameterAcc
return (CassandraParameters) super.getParameters();
}
/*
* (non-Javadoc)
* @see org.springframework.data.cassandra.repository.query.CassandraParameterAccessor#getParameterType(int)
*/
@Override
public Class<?> getParameterType(int index) {
return getParameters().getParameter(index).getType();
}
}

View File

@@ -19,7 +19,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.slf4j.Logger;
@@ -39,7 +38,6 @@ import org.springframework.data.repository.query.parser.Part;
import org.springframework.data.repository.query.parser.Part.Type;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import com.datastax.driver.core.querybuilder.Clause;
import com.datastax.driver.core.querybuilder.QueryBuilder;
@@ -50,35 +48,39 @@ import com.datastax.driver.core.querybuilder.Select;
*
* @author Matthew Adams
* @author Mark Paluch
* @author John Blum
*/
class CassandraQueryCreator extends AbstractQueryCreator<Select, Clause> {
private static final Pattern PUNCTATION_PATTERN = Pattern.compile("\\p{Punct}");
private static final Pattern PUNCTUATION_PATTERN = Pattern.compile("\\p{Punct}");
private static final Logger LOG = LoggerFactory.getLogger(CassandraQueryCreator.class);
private final CassandraMappingContext context;
private final WhereBuilder whereBuilder = new WhereBuilder();
private final CassandraMappingContext mappingContext;
private final CassandraPersistentEntity<?> entity;
private final WhereBuilder whereBuilder = new WhereBuilder();
/**
* Creates a new {@link CassandraQueryCreator} from the given {@link PartTree}, {@link ConvertingParameterAccessor}
* and {@link MappingContext}.
*
*
* @param tree must not be {@literal null}.
* @param accessor must not be {@literal null}.
* @param context must not be {@literal null}.
* @param mappingContext must not be {@literal null}.
* @param entityMetadata must not be {@literal null}.
*/
public CassandraQueryCreator(PartTree tree, CassandraParameterAccessor accessor, CassandraMappingContext context,
EntityMetadata<?> entityMetadata) {
public CassandraQueryCreator(PartTree tree, CassandraParameterAccessor accessor,
CassandraMappingContext mappingContext, EntityMetadata<?> entityMetadata) {
super(tree, accessor);
Assert.notNull(context, "CassandraMappingContext must not be null");
Assert.notNull(entityMetadata, "EntityInformation must not be null");
Assert.notNull(mappingContext, "CassandraMappingContext must not be null");
Assert.notNull(entityMetadata, "EntityMetaData must not be null");
this.context = context;
this.entity = context.getPersistentEntity(entityMetadata.getJavaType());
this.mappingContext = mappingContext;
this.entity = mappingContext.getPersistentEntity(entityMetadata.getJavaType());
}
/* (non-Javadoc)
@@ -87,8 +89,9 @@ class CassandraQueryCreator extends AbstractQueryCreator<Select, Clause> {
@Override
protected Clause create(Part part, Iterator<Object> iterator) {
PersistentPropertyPath<CassandraPersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
PersistentPropertyPath<CassandraPersistentProperty> path = mappingContext.getPersistentPropertyPath(part.getProperty());
CassandraPersistentProperty property = path.getLeafProperty();
return from(part, property, (PotentiallyConvertingIterator) iterator);
}
@@ -103,18 +106,19 @@ class CassandraQueryCreator extends AbstractQueryCreator<Select, Clause> {
}
whereBuilder.and(base);
return create(part, iterator);
}
/*
* Cassandra does not support OR queries.
*
*
* (non-Javadoc)
* @see org.springframework.data.repository.query.parser.AbstractQueryCreator#or(java.lang.Object, java.lang.Object)
*/
@Override
protected Clause or(Clause base, Clause criteria) {
throw new InvalidDataAccessApiUsageException(String.format("Cassandra does not support an OR operator!"));
throw new InvalidDataAccessApiUsageException("Cassandra does not support an OR operator");
}
/* (non-Javadoc)
@@ -166,14 +170,18 @@ class CassandraQueryCreator extends AbstractQueryCreator<Select, Clause> {
case SIMPLE_PROPERTY:
return QueryBuilder.eq(columnName(property), parameters.nextConverted(property));
default:
throw new InvalidDataAccessApiUsageException(
String.format("Unsupported Keyword: [%s] in part [%s]", type, part));
throw new InvalidDataAccessApiUsageException(String.format(
"Unsupported keyword [%s] in part [%s]", type, part));
}
}
private static String columnName(CassandraPersistentProperty property) {
return property.getColumnName().toCql();
}
private Clause containing(CassandraPersistentProperty property, Object bindableValue) {
if (property.isCollectionLike() || ClassUtils.isAssignable(Map.class, property.getType())) {
if (property.isCollectionLike() || property.isMapLike()) {
return QueryBuilder.contains(columnName(property), bindableValue);
}
@@ -182,32 +190,22 @@ class CassandraQueryCreator extends AbstractQueryCreator<Select, Clause> {
private Object like(Type type, Object value) {
if (value == null) {
return null;
if (value != null) {
switch (type) {
case LIKE:
return value;
case CONTAINING:
return "%" + value + "%";
case STARTING_WITH:
return value + "%";
case ENDING_WITH:
return "%" + value;
}
throw new IllegalArgumentException(String.format("Part Type [%s] not supported with like queries", type));
}
if (type == Type.LIKE) {
return value;
}
if (type == Type.CONTAINING) {
return "%" + value + "%";
}
if (type == Type.STARTING_WITH) {
return value + "%";
}
if (type == Type.ENDING_WITH) {
return "%" + value;
}
throw new IllegalArgumentException(String.format("Part Type [%s] not supported with like queries", type));
}
private static String columnName(CassandraPersistentProperty property) {
return property.getColumnName().toCql();
return null;
}
private Object[] nextAsArray(CassandraPersistentProperty property, PotentiallyConvertingIterator iterator) {
@@ -225,7 +223,7 @@ class CassandraQueryCreator extends AbstractQueryCreator<Select, Clause> {
/**
* Where clause builder. Collects {@link Clause clauses} and builds the where-clause depending on the WHERE type.
*
*
* @author Mark Paluch
*/
static class WhereBuilder {
@@ -233,16 +231,15 @@ class CassandraQueryCreator extends AbstractQueryCreator<Select, Clause> {
private List<Clause> clauses = new ArrayList<Clause>();
Clause and(Clause clause) {
clauses.add(clause);
return clause;
}
Select.Where build(Select.Where where) {
for (Clause clause : clauses) {
where = where.and(clause);
}
return where;
}
}
@@ -255,19 +252,14 @@ class CassandraQueryCreator extends AbstractQueryCreator<Select, Clause> {
/**
* Build a {@link Select} statement from the given {@link WhereBuilder} and {@link Sort}. Resolves property names
* for {@link Sort} using the {@link CassandraPersistentEntity}.
*
* @param whereBuilder
* @param entity
* @param sort
* @return
*/
static Select select(CassandraPersistentEntity<?> entity, WhereBuilder whereBuilder, Sort sort) {
Select select = QueryBuilder.select().from(entity.getTableName().toCql());
whereBuilder.build(select.where());
if (sort != null) {
for (Order order : sort) {
String dotPath = order.getProperty();
@@ -287,7 +279,7 @@ class CassandraQueryCreator extends AbstractQueryCreator<Select, Clause> {
private static CassandraPersistentProperty getPersistentProperty(CassandraPersistentEntity<?> entity,
String dotPath) {
String[] segments = PUNCTATION_PATTERN.split(dotPath);
String[] segments = PUNCTUATION_PATTERN.split(dotPath);
CassandraPersistentProperty property = null;
CassandraPersistentEntity<?> currentEntity = entity;
@@ -301,12 +293,11 @@ class CassandraQueryCreator extends AbstractQueryCreator<Select, Clause> {
}
if (property != null) {
return property;
}
throw new IllegalArgumentException(
String.format("Cannot resolve path [%s] to a property of [%s]", dotPath, entity.getName()));
throw new IllegalArgumentException(String.format(
"Cannot resolve path [%s] to a property of [%s]", dotPath, entity.getName()));
}
}
}

View File

@@ -40,29 +40,32 @@ import com.datastax.driver.core.ResultSet;
* @author Matthew Adams
* @author Oliver Gierke
* @author Mark Paluch
* @author John Blum
*/
public class CassandraQueryMethod extends QueryMethod {
private final Method method;
private CassandraEntityMetadata<?> entityMetadata;
private final CassandraMappingContext mappingContext;
private CassandraEntityMetadata<?> metadata;
private final Method method;
/**
* Creates a new {@link CassandraQueryMethod} from the given {@link Method}.
*
* @param method must not be {@literal null}.
* @param metadata must not be {@literal null}.
* @param repositoryMetadata must not be {@literal null}.
* @param projectionFactory must not be {@literal null}.
* @param mappingContext must not be {@literal null}.
*/
public CassandraQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
CassandraMappingContext mappingContext) {
public CassandraQueryMethod(Method method, RepositoryMetadata repositoryMetadata,
ProjectionFactory projectionFactory, CassandraMappingContext mappingContext) {
super(method, metadata, factory);
super(method, repositoryMetadata, projectionFactory);
Assert.notNull(mappingContext, "MappingContext must not be null");
verify(method, metadata);
verify(method, repositoryMetadata);
this.method = method;
this.mappingContext = mappingContext;
@@ -81,33 +84,34 @@ public class CassandraQueryMethod extends QueryMethod {
}
@Override
@SuppressWarnings("unchecked")
public CassandraEntityMetadata<?> getEntityInformation() {
if (metadata == null) {
if (entityMetadata == null) {
Class<?> returnedObjectType = getReturnedObjectType();
Class<?> domainClass = getDomainClass();
if (ClassUtils.isPrimitiveOrWrapper(returnedObjectType)) {
this.metadata = new SimpleCassandraEntityMetadata<Object>((Class<Object>) domainClass,
mappingContext.getPersistentEntity(domainClass));
this.entityMetadata = new SimpleCassandraEntityMetadata<Object>((Class<Object>) domainClass,
mappingContext.getPersistentEntity(domainClass));
} else {
CassandraPersistentEntity<?> returnedEntity = mappingContext.getPersistentEntity(returnedObjectType);
CassandraPersistentEntity<?> managedEntity = mappingContext.getPersistentEntity(domainClass);
returnedEntity = returnedEntity == null || returnedEntity.getType().isInterface() ? managedEntity
: returnedEntity;
CassandraPersistentEntity<?> collectionEntity = domainClass.isAssignableFrom(returnedObjectType)
? returnedEntity : managedEntity;
this.metadata = new SimpleCassandraEntityMetadata<Object>((Class<Object>) returnedEntity.getType(),
collectionEntity);
returnedEntity = (returnedEntity == null || returnedEntity.getType().isInterface()
? managedEntity : returnedEntity);
// TODO collectionEntity?
CassandraPersistentEntity<?> collectionEntity = domainClass.isAssignableFrom(returnedObjectType)
? returnedEntity : managedEntity;
this.entityMetadata = new SimpleCassandraEntityMetadata<Object>(
(Class<Object>) returnedEntity.getType(), collectionEntity);
}
}
return this.metadata;
return this.entityMetadata;
}
/* (non-Javadoc)
@@ -132,9 +136,8 @@ public class CassandraQueryMethod extends QueryMethod {
* @return
*/
public String getAnnotatedQuery() {
String query = (String) AnnotationUtils.getValue(getQueryAnnotation());
return StringUtils.hasText(query) ? query : null;
return (StringUtils.hasText(query) ? query : null);
}
/**

View File

@@ -52,7 +52,6 @@ class ConvertingParameterAccessor implements CassandraParameterAccessor {
private final CassandraParameterAccessor delegate;
ConvertingParameterAccessor(CassandraConverter cassandraConverter, CassandraParameterAccessor delegate) {
this.cassandraConverter = cassandraConverter;
this.delegate = delegate;
}
@@ -102,11 +101,8 @@ class ConvertingParameterAccessor implements CassandraParameterAccessor {
DataType dataType = delegate.getDataType(index);
if (dataType != null) {
return dataType;
}
return cassandraConverter.getMappingContext().getDataType(getParameterType(index));
return (dataType != null ? dataType
: cassandraConverter.getMappingContext().getDataType(getParameterType(index)));
}
/* (non-Javadoc)
@@ -150,13 +146,14 @@ class ConvertingParameterAccessor implements CassandraParameterAccessor {
&& property.isCollectionLike()) {
Class<?> customWriteTarget = getCustomConversions().getCustomWriteTarget(property.getActualType());
if (Collection.class.isAssignableFrom(property.getType()) && bindableValue instanceof Collection) {
Collection<Object> original = (Collection<Object>) bindableValue;
Collection<Object> converted = CollectionFactory.createCollection(property.getType(), original.size());
for (Object o : original) {
converted.add(getConversionService().convert(o, customWriteTarget));
for (Object element : original) {
converted.add(getConversionService().convert(element, customWriteTarget));
}
return converted;
@@ -181,12 +178,12 @@ class ConvertingParameterAccessor implements CassandraParameterAccessor {
/**
* Return the {@link DataType} based on annotated parameters with {@link CassandraType}, the
* {@link CassandraPersistentProperty} type or the declared parameter type.
*
* @param index
* @param cassandraPersistentProperty
*
* @param index index of parameter.
* @param property {@link CassandraPersistentProperty}.
* @return the {@link DataType}
*/
DataType getDataType(int index, CassandraPersistentProperty cassandraPersistentProperty) {
DataType getDataType(int index, CassandraPersistentProperty property) {
CassandraType cassandraType = delegate.findCassandraType(index);
@@ -197,24 +194,24 @@ class ConvertingParameterAccessor implements CassandraParameterAccessor {
CassandraMappingContext mappingContext = cassandraConverter.getMappingContext();
TypeInformation<?> typeInformation = ClassTypeInformation.from(getParameterType(index));
if (cassandraPersistentProperty == null) {
if (property == null) {
return mappingContext.getDataType(typeInformation.getType());
}
DataType dataType = mappingContext.getDataType(cassandraPersistentProperty);
DataType dataType = mappingContext.getDataType(property);
if (cassandraPersistentProperty.isCollectionLike() && !typeInformation.isCollectionLike()) {
if (property.isCollectionLike() && !typeInformation.isCollectionLike()) {
if (dataType instanceof CollectionType) {
CollectionType collectionType = (CollectionType) dataType;
if (collectionType.getTypeArguments().size() == 1) {
return collectionType.getTypeArguments().get(0);
}
}
}
if (!cassandraPersistentProperty.isCollectionLike() && typeInformation.isCollectionLike()) {
if (!property.isCollectionLike() && typeInformation.isCollectionLike()) {
if (typeInformation.isAssignableFrom(SET)) {
return DataType.set(dataType);
@@ -223,18 +220,18 @@ class ConvertingParameterAccessor implements CassandraParameterAccessor {
return DataType.list(dataType);
}
if (cassandraPersistentProperty.isMap()) {
if (property.isMap()) {
if (dataType instanceof CollectionType) {
CollectionType collectionType = (CollectionType) dataType;
if (collectionType.getTypeArguments().size() == 2) {
return collectionType.getTypeArguments().get(0);
}
}
}
return mappingContext.getDataType(cassandraPersistentProperty);
return mappingContext.getDataType(property);
}
/**
@@ -299,5 +296,6 @@ class ConvertingParameterAccessor implements CassandraParameterAccessor {
* @return the converted object, may be {@literal null}.
*/
Object nextConverted(CassandraPersistentProperty property);
}
}

View File

@@ -24,32 +24,33 @@ import org.springframework.data.repository.query.parser.PartTree;
/**
* {@link RepositoryQuery} implementation for Cassandra.
*
*
* @author Matthew Adams
* @author Mark Paluch
*/
public class PartTreeCassandraQuery extends AbstractCassandraQuery {
private final CassandraMappingContext mappingContext;
private final PartTree tree;
private final CassandraMappingContext context;
/**
* Creates a new {@link PartTreeCassandraQuery} from the given {@link QueryMethod} and {@link CassandraTemplate}.
*
* @param method must not be {@literal null}.
*
* @param queryMethod must not be {@literal null}.
* @param operations must not be {@literal null}.
*/
public PartTreeCassandraQuery(CassandraQueryMethod method, CassandraOperations operations) {
public PartTreeCassandraQuery(CassandraQueryMethod queryMethod, CassandraOperations operations) {
super(method, operations);
super(queryMethod, operations);
this.tree = new PartTree(method.getName(), method.getEntityInformation().getJavaType());
this.context = operations.getConverter().getMappingContext();
this.tree = new PartTree(queryMethod.getName(), queryMethod.getEntityInformation().getJavaType());
this.mappingContext = operations.getConverter().getMappingContext();
}
/**
* Return the {@link PartTree} backing the query.
*
*
* @return the tree
*/
public PartTree getTree() {
@@ -61,10 +62,11 @@ public class PartTreeCassandraQuery extends AbstractCassandraQuery {
* @see org.springframework.data.cassandra.repository.query.AbstractCassandraQuery#createQuery(org.springframework.data.cassandra.repository.query.CassandraParameterAccessor, boolean)
*/
@Override
protected String createQuery(CassandraParameterAccessor accessor) {
protected String createQuery(CassandraParameterAccessor parameterAccessor) {
CassandraQueryCreator creator = new CassandraQueryCreator(tree, accessor, context,
CassandraQueryCreator queryCreator = new CassandraQueryCreator(tree, parameterAccessor, mappingContext,
getQueryMethod().getEntityInformation());
return creator.createQuery().toString();
return queryCreator.createQuery().toString();
}
}

View File

@@ -21,29 +21,30 @@ import org.springframework.util.Assert;
/**
* Implementation of {@link CassandraEntityMetadata} based on the type and {@link CassandraPersistentEntity}.
*
*
* @author Mark Paluch
* @since 1.5
*/
class SimpleCassandraEntityMetadata<T> implements CassandraEntityMetadata<T> {
private final CassandraPersistentEntity<?> entity;
private final Class<T> type;
private final CassandraPersistentEntity<?> tableEntity;
/**
* Creates a new {@link SimpleCassandraEntityMetadata} using the given type and {@link CassandraPersistentEntity} to
* use for table lookups.
*
* @param type must not be {@literal null}.
* @param tableEntity must not be {@literal null} or empty.
* @param entity must not be {@literal null} or empty.
*/
public SimpleCassandraEntityMetadata(Class<T> type, CassandraPersistentEntity<?> tableEntity) {
public SimpleCassandraEntityMetadata(Class<T> type, CassandraPersistentEntity<?> entity) {
Assert.notNull(type, "Type must not be null!");
Assert.notNull(tableEntity, "Collection entity must not be null or empty!");
Assert.notNull(type, "Type must not be null");
Assert.notNull(entity, "Collection entity must not be null or empty");
this.type = type;
this.tableEntity = tableEntity;
this.entity = entity;
}
/* (non-Javadoc)
@@ -51,7 +52,7 @@ class SimpleCassandraEntityMetadata<T> implements CassandraEntityMetadata<T> {
*/
@Override
public CqlIdentifier getTableName() {
return tableEntity.getTableName();
return entity.getTableName();
}
/* (non-Javadoc)

View File

@@ -1,12 +1,12 @@
/*
* Copyright 2013-2016 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.
@@ -32,6 +32,7 @@ import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.data.repository.query.EvaluationContextProvider;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.RepositoryQuery;
@@ -39,11 +40,12 @@ import org.springframework.util.Assert;
/**
* Factory to create {@link TypedIdCassandraRepository} instances.
*
*
* @author Alex Shvid
* @author Matthew T. Adams
* @author Thomas Darimont
* @author Mark Paluch
* @author John Blum
*/
public class CassandraRepositoryFactory extends RepositoryFactorySupport {
@@ -52,7 +54,7 @@ public class CassandraRepositoryFactory extends RepositoryFactorySupport {
/**
* Creates a new {@link CassandraRepositoryFactory} with the given {@link CassandraOperations}.
*
*
* @param cassandraOperations must not be {@literal null}
*/
public CassandraRepositoryFactory(CassandraOperations cassandraOperations) {
@@ -63,7 +65,8 @@ public class CassandraRepositoryFactory extends RepositoryFactorySupport {
this.mappingContext = cassandraOperations.getConverter().getMappingContext();
}
/* (non-Javadoc)
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getRepositoryBaseClass(org.springframework.data.repository.core.RepositoryMetadata)
*/
@Override
@@ -71,7 +74,8 @@ public class CassandraRepositoryFactory extends RepositoryFactorySupport {
return SimpleCassandraRepository.class;
}
/* (non-Javadoc)
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getTargetRepository(org.springframework.data.repository.core.RepositoryInformation)
*/
@Override
@@ -81,7 +85,8 @@ public class CassandraRepositoryFactory extends RepositoryFactorySupport {
return getTargetRepositoryViaReflection(information, entityInformation, cassandraOperations);
}
/* (non-Javadoc)
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getEntityInformation(java.lang.Class)
*/
@Override
@@ -99,17 +104,27 @@ public class CassandraRepositoryFactory extends RepositoryFactorySupport {
cassandraOperations.getConverter());
}
/* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getQueryLookupStrategy(org.springframework.data.repository.query.QueryLookupStrategy.Key)
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getQueryLookupStrategy(Key)
*/
@Override
protected QueryLookupStrategy getQueryLookupStrategy(Key key) {
return getQueryLookupStrategy(key, null);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getQueryLookupStrategy(Key, EvaluationContextProvider)
*/
@Override
protected QueryLookupStrategy getQueryLookupStrategy(Key key, EvaluationContextProvider evaluationContextProvider) {
return new CassandraQueryLookupStrategy();
}
private class CassandraQueryLookupStrategy implements QueryLookupStrategy {
/*
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.projection.ProjectionFactory, org.springframework.data.repository.core.NamedQueries)
*/

View File

@@ -1,12 +1,12 @@
/*
* Copyright 2013-2014 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.
@@ -26,11 +26,15 @@ import org.springframework.util.Assert;
/**
* {@link org.springframework.beans.factory.FactoryBean} to create {@link TypedIdCassandraRepository} instances.
*
*
* @author Alex Shvid
* @author John Blum
* @see java.io.Serializable
* @see org.springframework.data.repository.Repository
* @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport
*/
public class CassandraRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends
RepositoryFactoryBeanSupport<T, S, ID> {
public class CassandraRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable>
extends RepositoryFactoryBeanSupport<T, S, ID> {
private CassandraTemplate cassandraTemplate;
@@ -40,9 +44,10 @@ public class CassandraRepositoryFactoryBean<T extends Repository<S, ID>, S, ID e
}
/**
* Configures the {@link CassandraTemplate} to be used.
*
* @param operations the operations to set
* Configures the {@link CassandraTemplate} used for Cassandra data access operations.
*
* @param cassandraTemplate {@link CassandraTemplate} used to perform CRUD, Query and general data access operations
* on Apache Cassandra.
*/
public void setCassandraTemplate(CassandraTemplate cassandraTemplate) {
this.cassandraTemplate = cassandraTemplate;
@@ -51,7 +56,7 @@ public class CassandraRepositoryFactoryBean<T extends Repository<S, ID>, S, ID e
/*
* (non-Javadoc)
*
*
* @see
* org.springframework.data.repository.support.RepositoryFactoryBeanSupport
* #afterPropertiesSet()
@@ -59,7 +64,6 @@ public class CassandraRepositoryFactoryBean<T extends Repository<S, ID>, S, ID e
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
Assert.notNull(cassandraTemplate, "cassandraTemplate must not be null!");
Assert.notNull(cassandraTemplate, "CassandraTemplate must not be null!");
}
}

View File

@@ -27,7 +27,6 @@ import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.cassandra.domain.Person;
import org.springframework.data.cassandra.mapping.CassandraType;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.DataType.Name;
/**
@@ -44,7 +43,7 @@ public class CassandraParametersUnitTests {
* @see DATACASS-296
*/
@Test
public void shouldUnknownDataTypeForSimpleType() throws Exception {
public void shouldReturnUnknownDataTypeForSimpleType() throws Exception {
Method method = PersonRepository.class.getMethod("findByFirstname", String.class);
CassandraParameters cassandraParameters = new CassandraParameters(method);

View File

@@ -47,26 +47,25 @@ import org.springframework.data.repository.query.parser.PartTree;
/**
* Unit tests for {@link CassandraQueryCreator}.
*
*
* @author Mark Paluch
* @soundtrack Odyssey - Everybody Move 9Club Mix
*/
public class CassandraQueryCreatorUnitTests {
CassandraMappingContext context;
CassandraConverter converter;
@Rule public ExpectedException expection = ExpectedException.none();
@Rule
public ExpectedException exception = ExpectedException.none();
@Before
public void setUp() throws SecurityException, NoSuchMethodException {
context = new BasicCassandraMappingContext();
converter = new MappingCassandraConverter(context);
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsQueryCorrectly() {
@@ -77,7 +76,7 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsQueryWithSortCorrectly() {
@@ -88,7 +87,7 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsAndQueryCorrectly() {
@@ -99,15 +98,15 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test(expected = InvalidDataAccessApiUsageException.class)
public void rejectsNegatingQueryQuery() {
public void rejectsNegatingQuery() {
createQuery("findByFirstnameNot", Person.class, "Walter");
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test(expected = InvalidDataAccessApiUsageException.class)
public void rejectsOrQuery() {
@@ -115,7 +114,7 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsGreaterThanQueryCorrectly() {
@@ -126,7 +125,7 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsGreaterThanEqualQueryCorrectly() {
@@ -137,7 +136,7 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsLessThanQueryCorrectly() {
@@ -148,7 +147,7 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsLessThanEqualQueryCorrectly() {
@@ -159,7 +158,7 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsInQueryCorrectly() {
@@ -170,7 +169,7 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsInQueryWithListCorrectly() {
@@ -181,30 +180,32 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsInQueryWithArrayCorrectly() {
String query = createQuery("findByFirstnameInAndLastname", Person.class, new String[] { "Walter", "Gus" }, "Fring");
String query = createQuery("findByFirstnameInAndLastname", Person.class,
new String[] { "Walter", "Gus" }, "Fring");
assertThat(query, is(equalTo("SELECT * FROM person WHERE firstname IN ('Walter','Gus') AND lastname='Fring';")));
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsLikeQueryCorrectly() {
assertThat(createQuery("findByFirstnameLike", Person.class, "Wal%ter"),
is(equalTo("SELECT * FROM person WHERE firstname LIKE 'Wal%ter';")));
assertThat(createQuery("findByFirstnameLike", Person.class, "Walter"),
is(equalTo("SELECT * FROM person WHERE firstname LIKE 'Walter';")));
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsStartsWithQueryCorrectly() {
@@ -215,7 +216,7 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsEndsWithQueryCorrectly() {
@@ -226,7 +227,7 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsContainsQueryOnSimplePropertyCorrectly() {
@@ -237,7 +238,7 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsContainsQueryOnSetPropertyCorrectly() {
@@ -248,7 +249,7 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsContainsQueryOnListPropertyCorrectly() {
@@ -259,7 +260,7 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsContainsQueryOnMapPropertyCorrectly() {
@@ -270,7 +271,7 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsIsTrueQueryCorrectly() {
@@ -281,7 +282,7 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsIsFalseQueryCorrectly() {
@@ -292,7 +293,7 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsQueryUsingQuotingCorrectly() {
@@ -303,7 +304,7 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsFindByPrimaryKeyPartCorrectly() {
@@ -314,7 +315,7 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsFindByPrimaryKeyPartWithSortCorrectly() {
@@ -325,7 +326,7 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void createsFindByPrimaryKeyPartOfPrimaryKeyClassCorrectly() {
@@ -337,7 +338,7 @@ public class CassandraQueryCreatorUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test(expected = IllegalStateException.class)
public void createsFindByPrimaryKey2PartCorrectly() {

View File

@@ -33,7 +33,7 @@ import org.springframework.data.repository.core.support.DefaultRepositoryMetadat
/**
* Unit tests for {@link CassandraQueryMethod}.
*
*
* @author Mark Paluch
*/
public class CassandraQueryMethodUnitTests {
@@ -46,7 +46,7 @@ public class CassandraQueryMethodUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void detectsCollectionFromRepoTypeIfReturnTypeNotAssignable() throws Exception {
@@ -59,7 +59,7 @@ public class CassandraQueryMethodUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test(expected = IllegalArgumentException.class)
public void rejectsNullMappingContext() throws Exception {
@@ -71,7 +71,7 @@ public class CassandraQueryMethodUnitTests {
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void considersMethodAsCollectionQuery() throws Exception {
@@ -88,8 +88,10 @@ public class CassandraQueryMethodUnitTests {
return new CassandraQueryMethod(method, new DefaultRepositoryMetadata(repository), factory, context);
}
@SuppressWarnings("unused")
interface SampleRepository extends Repository<Person, Long> {
List<Person> method();
}
}

View File

@@ -20,7 +20,6 @@ import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@@ -40,127 +39,127 @@ import com.datastax.driver.core.DataType;
/**
* Unit tests for {@link ConvertingParameterAccessor}.
*
*
* @author Mark Paluch
*/
@SuppressWarnings("Since15")
@RunWith(MockitoJUnitRunner.class)
public class ConvertingParameterAccessorUnitTests {
@Mock CassandraParameterAccessor delegateMock;
@Mock CassandraPersistentProperty propertyMock;
@Mock
private CassandraParameterAccessor mockParameterAccessor;
@Mock
private CassandraPersistentProperty mockProperty;
ConvertingParameterAccessor convertingParameterAccessor;
MappingCassandraConverter converter;
ConvertingParameterAccessor accessor;
@Before
public void setUp() {
this.converter = new MappingCassandraConverter(new BasicCassandraMappingContext());
this.converter.afterPropertiesSet();
this.accessor = new ConvertingParameterAccessor(converter, delegateMock);
this.convertingParameterAccessor = new ConvertingParameterAccessor(converter, mockParameterAccessor);
}
/**
* @see DATACASS-296
* @see <a href="https://jira.spring.io/browse/DATACASS-296">DATACASS-296</a>
*/
@Test
public void shouldReturnNullBindableValue() {
assertThat(accessor.getBindableValue(0), is(nullValue()));
assertThat(convertingParameterAccessor.getBindableValue(0), is(nullValue()));
}
/**
* @see DATACASS-296
* @see <a href="https://jira.spring.io/browse/DATACASS-296">DATACASS-296</a>
*/
@Test
@SuppressWarnings({"rawtypes", "unchecked"})
public void shouldReturnNativeBindableValue() {
when(mockParameterAccessor.getBindableValue(0)).thenReturn("hello");
when(mockParameterAccessor.getDataType(0)).thenReturn(DataType.varchar());
when(mockParameterAccessor.getParameterType(0)).thenReturn((Class) String.class);
when(delegateMock.getBindableValue(0)).thenReturn("hello");
when(delegateMock.getDataType(0)).thenReturn(DataType.varchar());
when(delegateMock.getParameterType(0)).thenReturn((Class) String.class);
assertThat(accessor.getBindableValue(0), is(equalTo((Object) "hello")));
assertThat(convertingParameterAccessor.getBindableValue(0), is(equalTo((Object) "hello")));
}
/**
* @see DATACASS-296
* @see <a href="https://jira.spring.io/browse/DATACASS-296">DATACASS-296</a>
*/
@Test
@SuppressWarnings({"rawtypes", "unchecked"})
public void shouldReturnConvertedBindableValue() {
LocalDate localDate = LocalDate.of(2010, 7, 4);
when(delegateMock.getBindableValue(0)).thenReturn(localDate);
when(delegateMock.getParameterType(0)).thenReturn((Class) LocalDate.class);
when(mockParameterAccessor.getBindableValue(0)).thenReturn(localDate);
when(mockParameterAccessor.getParameterType(0)).thenReturn((Class) LocalDate.class);
assertThat(accessor.getBindableValue(0),
is(equalTo((Object) com.datastax.driver.core.LocalDate.fromYearMonthDay(2010, 7, 4))));
assertThat(convertingParameterAccessor.getBindableValue(0),
is(equalTo((Object) com.datastax.driver.core.LocalDate.fromYearMonthDay(2010, 7, 4))));
}
/**
* @see DATACASS-296
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-296">DATACASS-296</a>
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void shouldReturnDataTypeProvidedByDelegate() {
when(mockParameterAccessor.getDataType(0)).thenReturn(DataType.varchar());
when(delegateMock.getDataType(0)).thenReturn(DataType.varchar());
assertThat(accessor.getDataType(0), is(equalTo(DataType.varchar())));
assertThat(convertingParameterAccessor.getDataType(0), is(equalTo(DataType.varchar())));
}
/**
* @see DATACASS-296
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-296">DATACASS-296</a>
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
@SuppressWarnings({"rawtypes", "unchecked"})
public void shouldConvertCollections() {
LocalDate localDate = LocalDate.of(2010, 7, 4);
when(delegateMock.iterator()).thenReturn((Iterator) Arrays.asList(Collections.singletonList(localDate)).iterator());
when(delegateMock.getDataType(0)).thenReturn(DataType.list(DataType.date()));
when(delegateMock.getParameterType(0)).thenReturn((Class) List.class);
when(propertyMock.getType()).thenReturn((Class) List.class);
when(propertyMock.getActualType()).thenReturn((Class) LocalDate.class);
when(propertyMock.isCollectionLike()).thenReturn(true);
when(mockParameterAccessor.iterator()).thenReturn((Iterator)
Collections.singletonList(Collections.singletonList(localDate)).iterator());
when(mockParameterAccessor.getDataType(0)).thenReturn(DataType.list(DataType.date()));
when(mockParameterAccessor.getParameterType(0)).thenReturn((Class) List.class);
when(mockProperty.getType()).thenReturn((Class) List.class);
when(mockProperty.getActualType()).thenReturn((Class) LocalDate.class);
when(mockProperty.isCollectionLike()).thenReturn(true);
PotentiallyConvertingIterator iterator = (PotentiallyConvertingIterator) accessor.iterator();
Object converted = iterator.nextConverted(propertyMock);
PotentiallyConvertingIterator iterator = (PotentiallyConvertingIterator) convertingParameterAccessor.iterator();
Object converted = iterator.nextConverted(mockProperty);
assertThat(converted, is(instanceOf(List.class)));
List<?> list = (List<?>) converted;
assertThat(list.get(0), is(instanceOf(com.datastax.driver.core.LocalDate.class)));
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
@SuppressWarnings({"rawtypes", "unchecked"})
public void shouldProvideTypeBasedOnValue() {
when(mockParameterAccessor.getDataType(0)).thenReturn(null);
when(mockParameterAccessor.getParameterType(0)).thenReturn((Class) LocalDate.class);
when(delegateMock.getDataType(0)).thenReturn(null);
when(delegateMock.getParameterType(0)).thenReturn((Class) LocalDate.class);
assertThat(accessor.getDataType(0), is(equalTo(DataType.date())));
assertThat(convertingParameterAccessor.getDataType(0), is(equalTo(DataType.date())));
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
@SuppressWarnings("rawtypes")
@SuppressWarnings({ "rawtypes", "unchecked" })
public void shouldProvideTypeBasedOnPropertyType() {
when(mockProperty.getDataType()).thenReturn(DataType.varchar());
when(mockProperty.findAnnotation(CassandraType.class)).thenReturn(mock(CassandraType.class));
when(mockParameterAccessor.getParameterType(0)).thenReturn((Class) String.class);
when(mockParameterAccessor.getDataType(0)).thenReturn(null);
when(propertyMock.getDataType()).thenReturn(DataType.varchar());
when(propertyMock.findAnnotation(CassandraType.class)).thenReturn(mock(CassandraType.class));
when(delegateMock.getParameterType(0)).thenReturn((Class) String.class);
when(delegateMock.getDataType(0)).thenReturn(null);
assertThat(accessor.getDataType(0, propertyMock), is(equalTo(DataType.varchar())));
assertThat(convertingParameterAccessor.getDataType(0, mockProperty), is(equalTo(DataType.varchar())));
}
}

View File

@@ -16,8 +16,8 @@
package org.springframework.data.cassandra.repository.query;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.when;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.lang.reflect.Method;
@@ -42,66 +42,64 @@ import org.springframework.data.repository.core.support.DefaultRepositoryMetadat
/**
* Unit tests for {@link PartTreeCassandraQuery}.
*
*
* @author Mark Paluch
*/
@RunWith(MockitoJUnitRunner.class)
public class PartTreeCassandraQueryUnitTests {
public @Rule ExpectedException exception = ExpectedException.none();
@Rule
public ExpectedException exception = ExpectedException.none();
@Mock CassandraOperations cassandraOperationsMock;
@Mock
private CassandraOperations mockCassandraOperations;
CassandraMappingContext mappingContext;
CassandraConverter converter;
private CassandraMappingContext mappingContext;
private CassandraConverter converter;
@Before
public void setUp() {
mappingContext = new BasicCassandraMappingContext();
converter = new MappingCassandraConverter(mappingContext);
when(cassandraOperationsMock.getConverter()).thenReturn(converter);
when(mockCassandraOperations.getConverter()).thenReturn(converter);
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void shouldDeriveSimpleQuery() {
String query = deriveQueryFromMethod("findByLastname", "foo");
assertThat(query, is(equalTo("SELECT * FROM person WHERE lastname='foo';")));
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void shouldDeriveSimpleQueryWithoutNames() {
String query = deriveQueryFromMethod("findPersonBy");
assertThat(query, is(equalTo("SELECT * FROM person;")));
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void shouldDeriveAndQuery() {
String query = deriveQueryFromMethod("findByFirstnameAndLastname", "foo", "bar" );
assertThat(query, is(equalTo("SELECT * FROM person WHERE firstname='foo' AND lastname='bar';")));
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void usesDynamicProjection() {
String query = deriveQueryFromMethod("findDynamicallyProjectedBy", PersonProjection.class);
assertThat(query, is(equalTo("SELECT * FROM person;")));
@@ -109,7 +107,6 @@ public class PartTreeCassandraQueryUnitTests {
private String deriveQueryFromMethod(String method, Object... args) {
Class<?>[] types = new Class<?>[args.length];
for (int i = 0; i < args.length; i++) {
@@ -119,19 +116,18 @@ public class PartTreeCassandraQueryUnitTests {
PartTreeCassandraQuery partTreeQuery = createQueryForMethod(method, types);
CassandraParameterAccessor accessor = new CassandraParametersParameterAccessor(partTreeQuery.getQueryMethod(), args);
return partTreeQuery.createQuery(new ConvertingParameterAccessor(cassandraOperationsMock.getConverter(), accessor));
return partTreeQuery.createQuery(new ConvertingParameterAccessor(mockCassandraOperations.getConverter(), accessor));
}
private PartTreeCassandraQuery createQueryForMethod(String methodName, Class<?>... paramTypes) {
try {
Method method = Repo.class.getMethod(methodName, paramTypes);
ProjectionFactory factory = new SpelAwareProxyProjectionFactory();
CassandraQueryMethod queryMethod = new CassandraQueryMethod(method, new DefaultRepositoryMetadata(Repo.class), factory,
mappingContext);
CassandraQueryMethod queryMethod = new CassandraQueryMethod(method,
new DefaultRepositoryMetadata(Repo.class), factory, mappingContext);
return new PartTreeCassandraQuery(queryMethod, cassandraOperationsMock);
return new PartTreeCassandraQuery(queryMethod, mockCassandraOperations);
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException(e.getMessage(), e);
} catch (SecurityException e) {

View File

@@ -36,21 +36,27 @@ import org.springframework.data.repository.Repository;
/**
* Unit tests for {@link CassandraRepositoryFactory}.
*
*
* @author Mark Paluch
*/
@RunWith(MockitoJUnitRunner.class)
@SuppressWarnings({ "rawtypes", "unchecked" })
public class CassandraRepositoryFactoryUnitTests {
@Mock CassandraTemplate template;
@Mock CassandraConverter converter;
@Mock CassandraMappingContext mappingContext;
@Mock CassandraPersistentEntity entity;
@Mock
private CassandraConverter converter;
@Mock
private CassandraMappingContext mappingContext;
@Mock
private CassandraPersistentEntity entity;
@Mock
private CassandraTemplate template;
@Before
public void setUp() {
when(template.getConverter()).thenReturn(converter);
when(converter.getMappingContext()).thenReturn(mappingContext);
}
@@ -60,12 +66,14 @@ public class CassandraRepositoryFactoryUnitTests {
*/
@Test
public void usesMappingCassandraEntityInformationIfMappingContextSet() {
when(mappingContext.getPersistentEntity(Person.class)).thenReturn(entity);
when(entity.getType()).thenReturn(Person.class);
CassandraRepositoryFactory factory = new CassandraRepositoryFactory(template);
CassandraEntityInformation<Person, Serializable> entityInformation = factory.getEntityInformation(Person.class);
CassandraRepositoryFactory repositoryFactory = new CassandraRepositoryFactory(template);
CassandraEntityInformation<Person, Serializable> entityInformation =
repositoryFactory.getEntityInformation(Person.class);
assertTrue(entityInformation instanceof MappingCassandraEntityInformation);
}
@@ -74,16 +82,15 @@ public class CassandraRepositoryFactoryUnitTests {
*/
@Test
public void createsRepositoryWithIdTypeLong() {
when(mappingContext.getPersistentEntity(Person.class)).thenReturn(entity);
when(entity.getType()).thenReturn(Person.class);
CassandraRepositoryFactory factory = new CassandraRepositoryFactory(template);
MyPersonRepository repository = factory.getRepository(MyPersonRepository.class);
CassandraRepositoryFactory repositoryFactory = new CassandraRepositoryFactory(template);
MyPersonRepository repository = repositoryFactory.getRepository(MyPersonRepository.class);
assertThat(repository, is(notNullValue()));
}
interface MyPersonRepository extends Repository<Person, Long> {
}
}

View File

@@ -45,11 +45,12 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Integration tests for query derivation through {@link PersonRepository}.
*
*
* @author Mark Paluch
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@SuppressWarnings("all")
public class QueryDerivationIntegrationTests extends AbstractSpringDataEmbeddedCassandraIntegrationTest {
@Configuration
@@ -68,14 +69,18 @@ public class QueryDerivationIntegrationTests extends AbstractSpringDataEmbeddedC
}
@Autowired CassandraOperations template;
@Autowired PersonRepository personRepository;
@Autowired
private CassandraOperations template;
Person walter, skyler, flynn;
@Autowired
private PersonRepository personRepository;
private Person walter;
private Person skyler;
private Person flynn;
@Before
public void before() {
deleteAllEntities();
Person person = new Person("Walter", "White");
@@ -87,7 +92,7 @@ public class QueryDerivationIntegrationTests extends AbstractSpringDataEmbeddedC
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void shouldFindByLastname() {
@@ -98,7 +103,7 @@ public class QueryDerivationIntegrationTests extends AbstractSpringDataEmbeddedC
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void shouldFindByLastnameAndDynamicSort() {
@@ -109,7 +114,7 @@ public class QueryDerivationIntegrationTests extends AbstractSpringDataEmbeddedC
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void shouldFindByLastnameWithOrdering() {
@@ -120,7 +125,7 @@ public class QueryDerivationIntegrationTests extends AbstractSpringDataEmbeddedC
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void shouldFindByFirstnameAndLastname() {
@@ -131,7 +136,7 @@ public class QueryDerivationIntegrationTests extends AbstractSpringDataEmbeddedC
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void executesCollectionQueryWithProjectionCorrectly() {
@@ -146,7 +151,7 @@ public class QueryDerivationIntegrationTests extends AbstractSpringDataEmbeddedC
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void shouldFindByNumberOfChildren() throws Exception {
@@ -154,6 +159,7 @@ public class QueryDerivationIntegrationTests extends AbstractSpringDataEmbeddedC
assumeThat(SpringVersion.getVersion(), startsWith("4.3"));
template.execute("CREATE INDEX IF NOT EXISTS person_number_of_children ON person (numberofchildren);");
// Give Cassandra some time to build the index
Thread.sleep(500);
@@ -163,12 +169,13 @@ public class QueryDerivationIntegrationTests extends AbstractSpringDataEmbeddedC
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void shouldFindByLocalDate() throws InterruptedException {
template.execute("CREATE INDEX IF NOT EXISTS person_created_date ON person (createddate);");
// Give Cassandra some time to build the index
Thread.sleep(500);
@@ -181,12 +188,13 @@ public class QueryDerivationIntegrationTests extends AbstractSpringDataEmbeddedC
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void shouldUseQueryOverride() {
Person otherWalter = new Person("Walter", "Black");
personRepository.save(otherWalter);
List<Person> result = personRepository.findByFirstname("Walter");
@@ -195,16 +203,16 @@ public class QueryDerivationIntegrationTests extends AbstractSpringDataEmbeddedC
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void shouldUseStartsWithQuery() throws InterruptedException {
Version version = CassandraVersion.get(template.getSession());
assumeTrue(version.isGreaterThanOrEqualTo(Version.parse("3.4")));
assumeTrue(CassandraVersion.get(template.getSession()).isGreaterThanOrEqualTo(Version.parse("3.4")));
template.execute(
"CREATE CUSTOM INDEX IF NOT EXISTS fn_starts_with ON person (nickname) USING 'org.apache.cassandra.index.sasi.SASIIndex';");
// Give Cassandra some time to build the index
Thread.sleep(500);
@@ -215,17 +223,17 @@ public class QueryDerivationIntegrationTests extends AbstractSpringDataEmbeddedC
}
/**
* @see DATACASS-7
* @see <a href="https://jira.spring.io/browse/DATACASS-7">DATACASS-7</a>
*/
@Test
public void shouldUseContainsQuery() throws InterruptedException {
Version version = CassandraVersion.get(template.getSession());
assumeTrue(version.isGreaterThanOrEqualTo(Version.parse("3.4")));
assumeTrue(CassandraVersion.get(template.getSession()).isGreaterThanOrEqualTo(Version.parse("3.4")));
template.execute(
"CREATE CUSTOM INDEX IF NOT EXISTS fn_contains ON person (nickname) USING 'org.apache.cassandra.index.sasi.SASIIndex'\n"
+ "WITH OPTIONS = { 'mode': 'CONTAINS' };");
// Give Cassandra some time to build the index
Thread.sleep(500);

View File

@@ -26,7 +26,7 @@ import com.datastax.driver.core.Session;
/**
* Utility to retrieve the Cassandra release version.
*
*
* @author Mark Paluch
*/
@UtilityClass
@@ -34,7 +34,7 @@ public class CassandraVersion {
/**
* Retrieve the Cassandra release version.
*
*
* @param session must not be {@literal null}.
* @return the release {@link Version}.
*/
@@ -44,6 +44,7 @@ public class CassandraVersion {
ResultSet resultSet = session.execute("SELECT release_version FROM system.local;");
Row row = resultSet.one();
return Version.parse(row.getString(0));
}
}