This commit is contained in:
Matthew Adams
2013-12-11 11:25:43 -06:00
parent c6ed00f4b0
commit adeb4df5e5
19 changed files with 168 additions and 279 deletions

View File

@@ -263,7 +263,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter implem
final CreateTableSpecification spec = new CreateTableSpecification();
spec.name(entity.getTable());
spec.name(entity.getTableName());
entity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {
public void doWithPersistentProperty(CassandraPersistentProperty prop) {

View File

@@ -238,7 +238,7 @@ public class CassandraAdminTemplate implements CassandraAdminOperations {
throw new InvalidDataAccessApiUsageException("No Persitent Entity information found for the class "
+ entityClass.getName());
}
return entity.getTable();
return entity.getTableName();
}
}

View File

@@ -390,7 +390,7 @@ public class CassandraDataTemplate extends CassandraTemplate implements Cassandr
throw new InvalidDataAccessApiUsageException("No Persitent Entity information found for the class "
+ entityClass.getName());
}
return entity.getTable();
return entity.getTableName();
}
/* (non-Javadoc)

View File

@@ -205,7 +205,7 @@ public class CassandraKeyspaceFactoryBean implements FactoryBean<SpringDataKeysp
String entityClassName = tableAttributes.getEntity();
Class<?> entityClass = ClassUtils.forName(entityClassName, this.beanClassLoader);
CassandraPersistentEntity<?> entity = determineEntity(entityClass);
String useTableName = tableAttributes.getName() != null ? tableAttributes.getName() : entity.getTable();
String useTableName = tableAttributes.getName() != null ? tableAttributes.getName() : entity.getTableName();
if (keyspaceCreated) {
createNewTable(session, useTableName, entity);

View File

@@ -15,8 +15,6 @@
*/
package org.springframework.data.cassandra.mapping;
import java.util.Comparator;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@@ -32,7 +30,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.StringUtils;
/**
* Cassandra specific {@link BasicPersistentEntity} implementation that adds Cassandra specific meta-data such as the
* Cassandra specific {@link BasicPersistentEntity} implementation that adds Cassandra specific metadata such as the
* table name.
*
* @author Alex Shvid
@@ -52,7 +50,7 @@ public class BasicCassandraPersistentEntity<T> extends BasicPersistentEntity<T,
*/
public BasicCassandraPersistentEntity(TypeInformation<T> typeInformation) {
super(typeInformation, CassandraPersistentPropertyComparator.INSTANCE);
super(typeInformation, CassandraPersistentPropertyColumnNameComparator.INSTANCE);
this.parser = new SpelExpressionParser();
this.context = new StandardEvaluationContext();
@@ -68,10 +66,6 @@ public class BasicCassandraPersistentEntity<T> extends BasicPersistentEntity<T,
}
}
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context.addPropertyAccessor(new BeanFactoryAccessor());
@@ -79,34 +73,8 @@ public class BasicCassandraPersistentEntity<T> extends BasicPersistentEntity<T,
context.setRootObject(applicationContext);
}
/**
* Returns the table the entity shall be persisted to.
*
* @return
*/
public String getTable() {
public String getTableName() {
Expression expression = parser.parseExpression(table, ParserContext.TEMPLATE_EXPRESSION);
return expression.getValue(context, String.class);
}
/**
* {@link Comparator} implementation inspecting the {@link CassandraPersistentProperty}'s order.
*
* @author Alex Shvid
*/
static enum CassandraPersistentPropertyComparator implements Comparator<CassandraPersistentProperty> {
INSTANCE;
/*
* (non-Javadoc)
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(CassandraPersistentProperty o1, CassandraPersistentProperty o2) {
return o1.getColumnName().compareTo(o2.getColumnName());
}
}
}

View File

@@ -91,7 +91,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
}
public DataType getDataType() {
Qualify annotation = getField().getAnnotation(Qualify.class);
CassandraType annotation = getField().getAnnotation(CassandraType.class);
if (annotation != null) {
return qualifyAnnotatedType(annotation);
}
@@ -110,7 +110,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
return DataType.list(autodetectPrimitiveType(args.get(0).getType()));
}
}
DataType dataType = CassandraSimpleTypes.autodetectPrimitive(this.getType());
DataType dataType = CassandraSimpleTypeHolder.getDataTypeFor(this.getType());
if (dataType == null) {
throw new InvalidDataAccessApiUsageException(
"only primitive types and Set,List,Map collections are allowed, unknown type for property '" + this.getName()
@@ -119,7 +119,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
return dataType;
}
private DataType qualifyAnnotatedType(Qualify annotation) {
private DataType qualifyAnnotatedType(CassandraType annotation) {
DataType.Name type = annotation.type();
if (type.isCollection()) {
switch (type) {
@@ -138,7 +138,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
+ "' type is '" + this.getType() + "' in the entity " + this.getOwner().getName());
}
} else {
return CassandraSimpleTypes.resolvePrimitive(type);
return CassandraSimpleTypeHolder.getDataTypeFor(type);
}
}
@@ -172,7 +172,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
}
DataType resolvePrimitiveType(DataType.Name typeName) {
DataType dataType = CassandraSimpleTypes.resolvePrimitive(typeName);
DataType dataType = CassandraSimpleTypeHolder.getDataTypeFor(typeName);
if (dataType == null) {
throw new InvalidDataAccessApiUsageException(
"only primitive types are allowed inside collections for the property '" + this.getName() + "' type is '"
@@ -182,7 +182,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
}
DataType autodetectPrimitiveType(Class<?> javaType) {
DataType dataType = CassandraSimpleTypes.autodetectPrimitive(javaType);
DataType dataType = CassandraSimpleTypeHolder.getDataTypeFor(javaType);
if (dataType == null) {
throw new InvalidDataAccessApiUsageException(
"only primitive types are allowed inside collections for the property '" + this.getName() + "' type is '"

View File

@@ -42,7 +42,7 @@ public class CassandraMappingContext extends
* Creates a new {@link CassandraMappingContext}.
*/
public CassandraMappingContext() {
setSimpleTypeHolder(CassandraSimpleTypes.HOLDER);
setSimpleTypeHolder(new CassandraSimpleTypeHolder());
}
/*

View File

@@ -21,14 +21,12 @@ import org.springframework.data.mapping.PersistentEntity;
* Cassandra specific {@link PersistentEntity} abstraction.
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
public interface CassandraPersistentEntity<T> extends PersistentEntity<T, CassandraPersistentProperty> {
/**
* Returns the table the entity shall be persisted to.
*
* @return
* Returns the table name to which the entity shall be persisted.
*/
String getTable();
String getTableName();
}

View File

@@ -0,0 +1,18 @@
package org.springframework.data.cassandra.mapping;
import java.util.Comparator;
/**
* {@link Comparator} implementation that uses the {@link CassandraPersistentProperty}'s column name for ordering.
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
public enum CassandraPersistentPropertyColumnNameComparator implements Comparator<CassandraPersistentProperty> {
INSTANCE;
public int compare(CassandraPersistentProperty o1, CassandraPersistentProperty o2) {
return o1.getColumnName().compareTo(o2.getColumnName());
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2010-2013 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.mapping;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.TypeInformation;
import com.datastax.driver.core.DataType;
/**
* Simple constant holder for a {@link SimpleTypeHolder} enriched with Cassandra specific simple types.
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
public class CassandraSimpleTypeHolder extends SimpleTypeHolder {
public static final Set<Class<?>> CASSANDRA_SIMPLE_TYPES;
private static final Map<Class<?>, Class<?>> primitiveTypesByWrapperType = new HashMap<Class<?>, Class<?>>(8);
private static final Map<Class<?>, DataType> dataTypesByJavaClass = new HashMap<Class<?>, DataType>();
private static final Map<DataType.Name, DataType> dataTypesByDataTypeName = new HashMap<DataType.Name, DataType>();
static {
primitiveTypesByWrapperType.put(Boolean.class, boolean.class);
primitiveTypesByWrapperType.put(Byte.class, byte.class);
primitiveTypesByWrapperType.put(Character.class, char.class);
primitiveTypesByWrapperType.put(Double.class, double.class);
primitiveTypesByWrapperType.put(Float.class, float.class);
primitiveTypesByWrapperType.put(Integer.class, int.class);
primitiveTypesByWrapperType.put(Long.class, long.class);
primitiveTypesByWrapperType.put(Short.class, short.class);
Set<Class<?>> simpleTypes = new HashSet<Class<?>>();
for (DataType dataType : DataType.allPrimitiveTypes()) {
Class<?> javaClass = dataType.asJavaClass();
simpleTypes.add(javaClass);
dataTypesByJavaClass.put(javaClass, dataType);
Class<?> primitiveJavaClass = primitiveTypesByWrapperType.get(javaClass);
if (primitiveJavaClass != null) {
dataTypesByJavaClass.put(primitiveJavaClass, dataType);
}
dataTypesByDataTypeName.put(dataType.getName(), dataType);
}
dataTypesByJavaClass.put(String.class, DataType.text());
CASSANDRA_SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes);
}
public static DataType getDataTypeFor(DataType.Name name) {
return dataTypesByDataTypeName.get(name);
}
public static DataType getDataTypeFor(Class<?> javaClass) {
return dataTypesByJavaClass.get(javaClass);
}
public static DataType.Name[] getDataTypeNamesFrom(List<TypeInformation<?>> arguments) {
DataType.Name[] array = new DataType.Name[arguments.size()];
for (int i = 0; i != array.length; i++) {
TypeInformation<?> typeInfo = arguments.get(i);
DataType dataType = getDataTypeFor(typeInfo.getType());
if (dataType == null) {
throw new InvalidDataAccessApiUsageException("not found appropriate primitive DataType for type = '"
+ typeInfo.getType());
}
array[i] = dataType.getName();
}
return array;
}
public CassandraSimpleTypeHolder() {
super(CASSANDRA_SIMPLE_TYPES, true);
}
}

View File

@@ -1,104 +0,0 @@
/*
* Copyright 2010-2013 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.mapping;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.TypeInformation;
import com.datastax.driver.core.DataType;
/**
* Simple constant holder for a {@link SimpleTypeHolder} enriched with Cassandra specific simple types.
*
* @author Alex Shvid
*/
public class CassandraSimpleTypes {
private static final Map<Class<?>, Class<?>> primitiveWrapperTypeMap = new HashMap<Class<?>, Class<?>>(8);
private static final Map<Class<?>, DataType> javaClassToDataType = new HashMap<Class<?>, DataType>();
private static final Map<DataType.Name, DataType> nameToDataType = new HashMap<DataType.Name, DataType>();
static {
primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
primitiveWrapperTypeMap.put(Byte.class, byte.class);
primitiveWrapperTypeMap.put(Character.class, char.class);
primitiveWrapperTypeMap.put(Double.class, double.class);
primitiveWrapperTypeMap.put(Float.class, float.class);
primitiveWrapperTypeMap.put(Integer.class, int.class);
primitiveWrapperTypeMap.put(Long.class, long.class);
primitiveWrapperTypeMap.put(Short.class, short.class);
Set<Class<?>> simpleTypes = new HashSet<Class<?>>();
for (DataType dataType : DataType.allPrimitiveTypes()) {
Class<?> javaClass = dataType.asJavaClass();
simpleTypes.add(javaClass);
javaClassToDataType.put(javaClass, dataType);
Class<?> primitiveJavaClass = primitiveWrapperTypeMap.get(javaClass);
if (primitiveJavaClass != null) {
javaClassToDataType.put(primitiveJavaClass, dataType);
}
nameToDataType.put(dataType.getName(), dataType);
}
javaClassToDataType.put(String.class, DataType.text());
CASSANDRA_SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes);
}
private static final Set<Class<?>> CASSANDRA_SIMPLE_TYPES;
public static final SimpleTypeHolder HOLDER = new SimpleTypeHolder(CASSANDRA_SIMPLE_TYPES, true);
private CassandraSimpleTypes() {
}
public static DataType resolvePrimitive(DataType.Name name) {
return nameToDataType.get(name);
}
public static DataType autodetectPrimitive(Class<?> javaClass) {
return javaClassToDataType.get(javaClass);
}
public static DataType.Name[] convertPrimitiveTypeArguments(List<TypeInformation<?>> arguments) {
DataType.Name[] result = new DataType.Name[arguments.size()];
for (int i = 0; i != result.length; ++i) {
TypeInformation<?> type = arguments.get(i);
DataType dataType = autodetectPrimitive(type.getType());
if (dataType == null) {
throw new InvalidDataAccessApiUsageException("not found appropriate primitive DataType for type = '"
+ type.getType());
}
result[i] = dataType.getName();
}
return result;
}
}

View File

@@ -22,13 +22,14 @@ import java.lang.annotation.RetentionPolicy;
import com.datastax.driver.core.DataType;
/**
* Qualifies data type as Cassandra type.
* Specifies the Cassandra type of the annotated property.
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Qualify {
public @interface CassandraType {
DataType.Name type();

View File

@@ -1,74 +0,0 @@
/*
* Copyright 2010-2013 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.mapping;
import com.datastax.driver.core.DataType;
/**
* Uses to transfer DataType and attributes for the property.
*
* @author Alex Shvid
*/
public class DataTypeInformation {
public static DataType.Name[] EMPTY_ATTRIBUTES = {};
private DataType.Name typeName;
private DataType.Name[] typeAttributes;
public DataTypeInformation(DataType.Name typeName) {
this(typeName, EMPTY_ATTRIBUTES);
}
public DataTypeInformation(DataType.Name typeName, DataType.Name[] typeAttributes) {
this.typeName = typeName;
this.typeAttributes = typeAttributes;
}
public DataType.Name getTypeName() {
return typeName;
}
public void setTypeName(DataType.Name typeName) {
this.typeName = typeName;
}
public DataType.Name[] getTypeAttributes() {
return typeAttributes;
}
public void setTypeAttributes(DataType.Name[] typeAttributes) {
this.typeAttributes = typeAttributes;
}
public String toCQL() {
if (typeAttributes.length == 0) {
return typeName.name();
} else {
StringBuilder str = new StringBuilder();
str.append(typeName.name());
str.append('<');
for (int i = 0; i != typeAttributes.length; ++i) {
if (i != 0) {
str.append(',');
}
str.append(typeAttributes[i].name());
}
str.append('>');
return str.toString();
}
}
}

View File

@@ -94,7 +94,7 @@ public class MappingCassandraEntityInformation<T, ID extends Serializable> exten
*/
@Override
public String getTableName() {
return customTableName == null ? entityMetadata.getTable() : customTableName;
return customTableName == null ? entityMetadata.getTableName() : customTableName;
}
/* (non-Javadoc)

View File

@@ -58,7 +58,7 @@ public class BasicCassandraPersistentEntityIntegrationTests {
BasicCassandraPersistentEntity<Notification> entity = new BasicCassandraPersistentEntity<Notification>(
ClassTypeInformation.from(Notification.class));
assertThat(entity.getTable(), is("messages"));
assertThat(entity.getTableName(), is("messages"));
}
@Test
@@ -66,7 +66,7 @@ public class BasicCassandraPersistentEntityIntegrationTests {
BasicCassandraPersistentEntity<Area> entity = new BasicCassandraPersistentEntity<Area>(
ClassTypeInformation.from(Area.class));
assertThat(entity.getTable(), is("123"));
assertThat(entity.getTableName(), is("123"));
}
@Test
@@ -82,7 +82,7 @@ public class BasicCassandraPersistentEntityIntegrationTests {
ClassTypeInformation.from(UserLine.class));
entity.setApplicationContext(context);
assertThat(entity.getTable(), is("user_line"));
assertThat(entity.getTableName(), is("user_line"));
}
@After

View File

@@ -17,25 +17,19 @@ package org.springframework.data.cassandra.test.integration.mapping;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Date;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.thrift.transport.TTransportException;
import org.cassandraunit.utils.EmbeddedCassandraServerHelper;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.cassandra.mapping.BasicCassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.BasicCassandraPersistentProperty;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
import org.springframework.data.cassandra.mapping.Column;
import org.springframework.data.cassandra.mapping.PrimaryKey;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.util.ReflectionUtils;
@@ -47,14 +41,20 @@ import org.springframework.util.ReflectionUtils;
*/
public class BasicCassandraPersistentPropertyIntegrationTests {
CassandraPersistentEntity<Timeline> entity;
static class Timeline {
@PrimaryKey
String id;
Date time;
@Column("message")
String text;
@BeforeClass
public static void startCassandra() throws IOException, TTransportException, ConfigurationException,
InterruptedException {
EmbeddedCassandraServerHelper.startEmbeddedCassandra("cassandra.yaml");
}
CassandraPersistentEntity<Timeline> entity;
@Before
public void setup() {
entity = new BasicCassandraPersistentEntity<Timeline>(ClassTypeInformation.from(Timeline.class));
@@ -71,39 +71,16 @@ public class BasicCassandraPersistentPropertyIntegrationTests {
public void checksIdProperty() {
Field field = ReflectionUtils.findField(Timeline.class, "id");
CassandraPersistentProperty property = getPropertyFor(field);
assertThat(property.isIdProperty(), is(true));
assertTrue(property.isIdProperty());
}
@Test
public void returnsPropertyNameForUnannotatedProperties() {
public void returnsPropertyNameForUnannotatedProperty() {
Field field = ReflectionUtils.findField(Timeline.class, "time");
assertThat(getPropertyFor(field).getColumnName(), is("time"));
}
@After
public void clearCassandra() {
EmbeddedCassandraServerHelper.cleanEmbeddedCassandra();
}
@AfterClass
public static void stopCassandra() {
EmbeddedCassandraServerHelper.stopEmbeddedCassandra();
}
private CassandraPersistentProperty getPropertyFor(Field field) {
return new BasicCassandraPersistentProperty(field, null, entity, new SimpleTypeHolder());
}
class Timeline {
@Id
String id;
Date time;
@Column("message")
String text;
}
}

View File

@@ -19,7 +19,7 @@ import java.util.Date;
import java.util.Set;
import org.springframework.data.cassandra.mapping.PrimaryKey;
import org.springframework.data.cassandra.mapping.Qualify;
import org.springframework.data.cassandra.mapping.CassandraType;
import org.springframework.data.cassandra.mapping.Table;
import com.datastax.driver.core.DataType;
@@ -41,7 +41,7 @@ public class Comment {
private String text;
@Qualify(type = DataType.Name.SET, typeArguments = { DataType.Name.TEXT })
@CassandraType(type = DataType.Name.SET, typeArguments = { DataType.Name.TEXT })
private Set<String> likes;
/*

View File

@@ -20,7 +20,7 @@ import java.util.Date;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.data.cassandra.mapping.CompositePrimaryKey;
import org.springframework.data.cassandra.mapping.PrimaryKeyColumn;
import org.springframework.data.cassandra.mapping.Qualify;
import org.springframework.data.cassandra.mapping.CassandraType;
import com.datastax.driver.core.DataType;
@@ -43,7 +43,7 @@ public class CommentPK {
* Clustered Column
*/
@PrimaryKeyColumn(ordinal = 1)
@Qualify(type = DataType.Name.TIMESTAMP)
@CassandraType(type = DataType.Name.TIMESTAMP)
private Date time;
public String getAuthor() {

View File

@@ -20,7 +20,7 @@ import java.util.Date;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.data.cassandra.mapping.CompositePrimaryKey;
import org.springframework.data.cassandra.mapping.PrimaryKeyColumn;
import org.springframework.data.cassandra.mapping.Qualify;
import org.springframework.data.cassandra.mapping.CassandraType;
import com.datastax.driver.core.DataType;
@@ -45,7 +45,7 @@ public class NotificationPK {
* Clustered Column
*/
@PrimaryKeyColumn(ordinal = 1)
@Qualify(type = DataType.Name.TIMESTAMP)
@CassandraType(type = DataType.Name.TIMESTAMP)
private Date time;
public String getUsername() {