This commit is contained in:
Matthew Adams
2013-12-11 12:14:30 -06:00
parent adeb4df5e5
commit a50eb2f7ab
9 changed files with 82 additions and 70 deletions

View File

@@ -34,6 +34,7 @@ import org.springframework.util.StringUtils;
* table name.
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
public class BasicCassandraPersistentEntity<T> extends BasicPersistentEntity<T, CassandraPersistentProperty> implements
CassandraPersistentEntity<T>, ApplicationContextAware {
@@ -44,7 +45,7 @@ public class BasicCassandraPersistentEntity<T> extends BasicPersistentEntity<T,
/**
* Creates a new {@link BasicCassandraPersistentEntity} with the given {@link TypeInformation}. Will default the table
* name to the entities simple type name.
* name to the entity's simple type name.
*
* @param typeInformation
*/
@@ -56,14 +57,10 @@ public class BasicCassandraPersistentEntity<T> extends BasicPersistentEntity<T,
this.context = new StandardEvaluationContext();
Class<?> rawType = typeInformation.getType();
String fallback = CassandraNamingUtils.getPreferredTableName(rawType);
Table anno = rawType.getAnnotation(Table.class);
if (rawType.isAnnotationPresent(Table.class)) {
Table d = rawType.getAnnotation(Table.class);
this.table = StringUtils.hasText(d.name()) ? d.name() : fallback;
} else {
this.table = fallback;
}
this.table = anno != null && StringUtils.hasText(anno.name()) ? anno.name() : CassandraNamingUtils
.getPreferredTableName(rawType);
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

View File

@@ -59,7 +59,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
return true;
}
return getField().isAnnotationPresent(PrimaryKey.class);
return isAnnotationPresent(PrimaryKey.class);
}
@Override
@@ -70,85 +70,105 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
public String getColumnName() {
// first check @Column annotation
Column annotation = getField().getAnnotation(Column.class);
if (annotation != null && StringUtils.hasText(annotation.value())) {
return annotation.value();
Column column = findAnnotation(Column.class);
if (column != null && StringUtils.hasText(column.value())) {
return column.value();
}
// else check @KeyColumn annotation
PrimaryKeyColumn anno = getField().getAnnotation(PrimaryKeyColumn.class);
if (anno == null || !StringUtils.hasText(anno.value())) {
return field.getName();
PrimaryKeyColumn pk = findAnnotation(PrimaryKeyColumn.class);
if (pk != null && StringUtils.hasText(pk.value())) {
return pk.value();
}
return anno.value();
// else default
return field.getName().toLowerCase();
}
public Ordering getOrdering() {
PrimaryKeyColumn anno = getField().getAnnotation(PrimaryKeyColumn.class);
PrimaryKeyColumn anno = findAnnotation(PrimaryKeyColumn.class);
return anno == null ? null : anno.ordering();
}
public DataType getDataType() {
CassandraType annotation = getField().getAnnotation(CassandraType.class);
CassandraType annotation = findAnnotation(CassandraType.class);
if (annotation != null) {
return qualifyAnnotatedType(annotation);
return getDataTypeFor(annotation);
}
if (isMap()) {
List<TypeInformation<?>> args = getTypeInformation().getTypeArguments();
ensureTypeArguments(args.size(), 2);
return DataType.map(autodetectPrimitiveType(args.get(0).getType()),
autodetectPrimitiveType(args.get(1).getType()));
return DataType.map(getDataTypeFor(args.get(0).getType()), getDataTypeFor(args.get(1).getType()));
}
if (isCollectionLike()) {
List<TypeInformation<?>> args = getTypeInformation().getTypeArguments();
ensureTypeArguments(args.size(), 1);
if (Set.class.isAssignableFrom(getType())) {
return DataType.set(autodetectPrimitiveType(args.get(0).getType()));
} else if (List.class.isAssignableFrom(getType())) {
return DataType.list(autodetectPrimitiveType(args.get(0).getType()));
return DataType.set(getDataTypeFor(args.get(0).getType()));
}
if (List.class.isAssignableFrom(getType())) {
return DataType.list(getDataTypeFor(args.get(0).getType()));
}
}
DataType dataType = CassandraSimpleTypeHolder.getDataTypeFor(this.getType());
DataType dataType = CassandraSimpleTypeHolder.getDataTypeFor(getType());
if (dataType == null) {
throw new InvalidDataAccessApiUsageException(
"only primitive types and Set,List,Map collections are allowed, unknown type for property '" + this.getName()
+ "' type is '" + this.getType() + "' in the entity " + this.getOwner().getName());
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()));
}
return dataType;
}
private DataType qualifyAnnotatedType(CassandraType annotation) {
private DataType getDataTypeFor(CassandraType annotation) {
DataType.Name type = annotation.type();
if (type.isCollection()) {
switch (type) {
case MAP:
ensureTypeArguments(annotation.typeArguments().length, 2);
return DataType.map(resolvePrimitiveType(annotation.typeArguments()[0]),
resolvePrimitiveType(annotation.typeArguments()[1]));
return DataType.map(getDataTypeFor(annotation.typeArguments()[0]),
getDataTypeFor(annotation.typeArguments()[1]));
case LIST:
ensureTypeArguments(annotation.typeArguments().length, 1);
return DataType.list(resolvePrimitiveType(annotation.typeArguments()[0]));
return DataType.list(getDataTypeFor(annotation.typeArguments()[0]));
case SET:
ensureTypeArguments(annotation.typeArguments().length, 1);
return DataType.set(resolvePrimitiveType(annotation.typeArguments()[0]));
return DataType.set(getDataTypeFor(annotation.typeArguments()[0]));
default:
throw new InvalidDataAccessApiUsageException("unknown collection DataType for property '" + this.getName()
+ "' type is '" + this.getType() + "' in the entity " + this.getOwner().getName());
throw new InvalidDataAccessApiUsageException(
String.format("unknown multivalued DataType [%s] for property [%s] in entity [%s]", type, getType(),
getOwner().getName()));
}
} else {
return CassandraSimpleTypeHolder.getDataTypeFor(type);
}
}
public boolean isIndexed() {
return getField().isAnnotationPresent(Indexed.class);
return isAnnotationPresent(Indexed.class);
}
public boolean isPartitionKeyColumn() {
PrimaryKeyColumn anno = getField().getAnnotation(PrimaryKeyColumn.class);
PrimaryKeyColumn anno = findAnnotation(PrimaryKeyColumn.class);
return anno != null && anno.type() == PrimaryKeyType.PARTITIONED;
}
@@ -156,14 +176,14 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
@Override
public boolean isClusterKeyColumn() {
PrimaryKeyColumn anno = getField().getAnnotation(PrimaryKeyColumn.class);
PrimaryKeyColumn anno = findAnnotation(PrimaryKeyColumn.class);
return anno != null && anno.type() == PrimaryKeyType.CLUSTERED;
}
@Override
public boolean isPrimaryKeyColumn() {
return getField().isAnnotationPresent(PrimaryKeyColumn.class);
return isAnnotationPresent(PrimaryKeyColumn.class);
}
@Override
@@ -171,7 +191,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
return new Association<CassandraPersistentProperty>(this, null);
}
DataType resolvePrimitiveType(DataType.Name typeName) {
protected DataType getDataTypeFor(DataType.Name typeName) {
DataType dataType = CassandraSimpleTypeHolder.getDataTypeFor(typeName);
if (dataType == null) {
throw new InvalidDataAccessApiUsageException(
@@ -181,7 +201,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
return dataType;
}
DataType autodetectPrimitiveType(Class<?> javaType) {
protected DataType getDataTypeFor(Class<?> javaType) {
DataType dataType = CassandraSimpleTypeHolder.getDataTypeFor(javaType);
if (dataType == null) {
throw new InvalidDataAccessApiUsageException(
@@ -191,7 +211,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
return dataType;
}
void ensureTypeArguments(int args, int expected) {
protected void ensureTypeArguments(int args, int expected) {
if (args != expected) {
throw new InvalidDataAccessApiUsageException("expected " + expected + " of typed arguments for the property '"
+ this.getName() + "' type is '" + this.getType() + "' in the entity " + this.getOwner().getName());

View File

@@ -45,20 +45,12 @@ public class CassandraMappingContext extends
setSimpleTypeHolder(new CassandraSimpleTypeHolder());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.AbstractMappingContext#createPersistentProperty(java.lang.reflect.Field, java.beans.PropertyDescriptor, org.springframework.data.mapping.MutablePersistentEntity, org.springframework.data.mapping.SimpleTypeHolder)
*/
@Override
public CassandraPersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor,
BasicCassandraPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
return new CachingCassandraPersistentProperty(field, descriptor, owner, simpleTypeHolder);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.BasicMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation, org.springframework.data.mapping.model.MappingContext)
*/
@Override
protected <T> BasicCassandraPersistentEntity<T> createPersistentEntity(TypeInformation<T> typeInformation) {
@@ -71,10 +63,6 @@ public class CassandraMappingContext extends
return entity;
}
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;

View File

@@ -31,8 +31,19 @@ import com.datastax.driver.core.DataType;
@Retention(RetentionPolicy.RUNTIME)
public @interface CassandraType {
/**
* The {@link DataType}.{@link Name} of the property.
*/
DataType.Name type();
/**
* If the property is collection-like, then this attribute holds a single {@link DataType}.{@link Name}, representing
* the element type of the collection.
* <p/>
* If the property is map, then this attribute holds exactly two {@link DataType}.{@link Name}s: the first is the key
* type, and the second is the value type.
* <p/>
* If the property is neither collection-like or a map, then this attribute is ignored.
*/
DataType.Name[] typeArguments() default {};
}

View File

@@ -31,8 +31,10 @@
package org.springframework.data.cassandra.mapping;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to define custom metadata for document fields.
@@ -42,6 +44,7 @@ import java.lang.annotation.RetentionPolicy;
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
public @interface Column {
/**

View File

@@ -22,21 +22,13 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Defines composite primary key class in the Cassandra table that contains several fields. Example:
*
* @CompositePrimaryKey class AccountPK { String account; String region; }
*
* @Table class Account {
* @Id AccountPK pk; }
*
* Defines composite primary key class in the Cassandra table that contains several fields.
*
* @author Alex Shvid
* @deprecated This class is actually unnecessary and can be inferred by the existence of field(s) with
* {@link PrimaryKeyColumn} annotations on them.
* @author Matthew T. Adams
*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
@Deprecated
public @interface CompositePrimaryKey {
}

View File

@@ -21,15 +21,14 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Identifies a secondary index in the table. Usually it is a field with common dublicate values for the hole table.
* such as city, place, educationType, state flags ant etc.
* Identifies a secondary index in the table. Usually it is a field with common duplicate values within the table.
*
* Using unique fields is not common and has overhead, such as email, username and etc.
* Using unique fields is not recommended.
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = { ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
public @interface Indexed {
}

View File

@@ -20,6 +20,8 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.data.annotation.Id;
/**
* Identifies the primary key field of the entity, which may be of a basic type or of a type that represents a composite
* primary key class. This field corresponds to the <code>PRIMARY KEY</code> of the corresponding Cassandra table.
@@ -29,6 +31,6 @@ import java.lang.annotation.Target;
*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = { ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@org.springframework.data.annotation.Id
@Id
public @interface PrimaryKey {
}

View File

@@ -28,7 +28,7 @@ import org.springframework.cassandra.core.PrimaryKeyType;
* cluster key field.
*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = { ElementType.FIELD, ElementType.METHOD })
@Target(value = { ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
public @interface PrimaryKeyColumn {
/**