DATACASS-284 - Polish.

This commit is contained in:
John Blum
2018-02-02 12:53:57 -08:00
parent 511ae51208
commit 1205506147
10 changed files with 257 additions and 202 deletions

View File

@@ -15,11 +15,20 @@
*/
package org.springframework.data.cassandra.core.mapping;
import static org.springframework.data.cassandra.core.cql.CqlIdentifier.*;
import static org.springframework.data.cassandra.core.cql.keyspace.CreateTableSpecification.*;
import static org.springframework.data.cassandra.core.mapping.CassandraSimpleTypeHolder.*;
import static org.springframework.data.cassandra.core.cql.keyspace.CreateTableSpecification.createTable;
import static org.springframework.data.cassandra.core.mapping.CassandraSimpleTypeHolder.getDataTypeFor;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.StreamSupport;
import org.springframework.beans.BeansException;
@@ -42,6 +51,7 @@ import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.Optionals;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -76,7 +86,7 @@ public class CassandraMappingContext
private @Nullable UserTypeResolver userTypeResolver;
private @Nullable ApplicationContext context;
private @Nullable ApplicationContext applicationContext;
private @Nullable ClassLoader beanClassLoader;
@@ -90,8 +100,9 @@ public class CassandraMappingContext
*/
public CassandraMappingContext() {
setCustomConversions(
new CustomConversions(StoreConversions.of(CassandraSimpleTypeHolder.HOLDER), Collections.emptyList()));
setCustomConversions(new CustomConversions(
StoreConversions.of(CassandraSimpleTypeHolder.HOLDER), Collections.emptyList()));
setSimpleTypeHolder(CassandraSimpleTypeHolder.HOLDER);
}
@@ -104,34 +115,38 @@ public class CassandraMappingContext
processMappingOverrides();
}
@SuppressWarnings("all")
private void processMappingOverrides() {
this.mapping.getEntityMappings().stream().filter(Objects::nonNull).forEach(entityMapping -> {
Class<?> entityClass = getEntityClass(entityMapping.getEntityClassName());
CassandraPersistentEntity<?> entity = getRequiredPersistentEntity(entityClass);
String entityTableName = entityMapping.getTableName();
if (StringUtils.hasText(entityTableName)) {
entity.setTableName(of(entityTableName, Boolean.valueOf(entityMapping.getForceQuote())));
entity.setTableName(CqlIdentifier.of(entityTableName, Boolean.valueOf(entityMapping.getForceQuote())));
}
processMappingOverrides(entity, entityMapping);
});
}
private Class<?> getEntityClass(String entityClassName) {
try {
return ClassUtils.forName(entityClassName, beanClassLoader);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(String.format("Unknown persistent entity name [%s]", entityClassName), e);
return ClassUtils.forName(entityClassName, this.beanClassLoader);
}
catch (ClassNotFoundException cause) {
throw new IllegalStateException(
String.format("Unknown persistent entity name [%s]", entityClassName), cause);
}
}
private static void processMappingOverrides(CassandraPersistentEntity<?> entity, EntityMapping entityMapping) {
entityMapping.getPropertyMappings()
.forEach((key, propertyMapping) -> processMappingOverride(entity, propertyMapping));
}
@@ -145,7 +160,7 @@ public class CassandraMappingContext
property.setForceQuote(forceQuote);
if (StringUtils.hasText(mapping.getColumnName())) {
property.setColumnName(of(mapping.getColumnName(), forceQuote));
property.setColumnName(CqlIdentifier.of(mapping.getColumnName(), forceQuote));
}
}
@@ -154,7 +169,7 @@ public class CassandraMappingContext
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
this.applicationContext = applicationContext;
}
/* (non-Javadoc)
@@ -189,6 +204,24 @@ public class CassandraMappingContext
this.mapping = mapping;
}
/**
* Returns only {@link Table} entities.
*
* @since 1.5
*/
public Collection<BasicCassandraPersistentEntity<?>> getTableEntities() {
return Collections.unmodifiableCollection(this.tableEntities);
}
/**
* Returns only those entities representing a user defined type.
*
* @since 1.5
*/
public Collection<CassandraPersistentEntity<?>> getUserDefinedTypeEntities() {
return Collections.unmodifiableSet(this.userDefinedTypes);
}
/**
* Sets the {@link UserTypeResolver}.
*
@@ -213,25 +246,7 @@ public class CassandraMappingContext
* @return Returns the verifier.
*/
public CassandraPersistentEntityMetadataVerifier getVerifier() {
return verifier;
}
/**
* Returns only {@link Table} entities.
*
* @since 1.5
*/
public Collection<BasicCassandraPersistentEntity<?>> getTableEntities() {
return Collections.unmodifiableCollection(this.tableEntities);
}
/**
* Returns only those entities representing a user defined type.
*
* @since 1.5
*/
public Collection<CassandraPersistentEntity<?>> getUserDefinedTypeEntities() {
return Collections.unmodifiableSet(this.userDefinedTypes);
return this.verifier;
}
/* (non-Javadoc)
@@ -252,14 +267,15 @@ public class CassandraMappingContext
}
// now do some caching of the entity
Set<CassandraPersistentEntity<?>> entities = this.entitySetsByTableName.computeIfAbsent(entity.getTableName(),
cqlIdentifier -> new HashSet<>());
Set<CassandraPersistentEntity<?>> entities = this.entitySetsByTableName
.computeIfAbsent(entity.getTableName(), cqlIdentifier -> new HashSet<>());
entities.add(entity);
if (!entity.isUserDefinedType() && entity.isAnnotationPresent(Table.class)) {
this.tableEntities.add(entity);
}
});
return optional;
@@ -270,8 +286,8 @@ public class CassandraMappingContext
*/
@Override
protected boolean shouldCreatePersistentEntityFor(TypeInformation<?> typeInfo) {
return (!this.customConversions.hasCustomWriteTarget(typeInfo.getType())
&& super.shouldCreatePersistentEntityFor(typeInfo));
return !this.customConversions.hasCustomWriteTarget(typeInfo.getType())
&& super.shouldCreatePersistentEntityFor(typeInfo);
}
/* (non-Javadoc)
@@ -280,27 +296,32 @@ public class CassandraMappingContext
@Override
protected <T> BasicCassandraPersistentEntity<T> createPersistentEntity(TypeInformation<T> typeInformation) {
UserDefinedType userDefinedType = AnnotatedElementUtils.findMergedAnnotation(typeInformation.getType(),
UserDefinedType.class);
BasicCassandraPersistentEntity<T> entity = Optional.ofNullable(resolveUserDefinedType(typeInformation))
.<BasicCassandraPersistentEntity<T>>map(resolvedUserDefinedType ->
new CassandraUserTypePersistentEntity<>(typeInformation, getVerifier(), resolveUserTypeResolver()))
.orElseGet(() ->
new BasicCassandraPersistentEntity<>(typeInformation, getVerifier()));
BasicCassandraPersistentEntity<T> entity;
if (userDefinedType != null) {
Assert.state(this.userTypeResolver != null, "UserTypeResolver must not be null");
entity = new CassandraUserTypePersistentEntity<>(typeInformation, this.verifier, this.userTypeResolver);
} else {
entity = new BasicCassandraPersistentEntity<>(typeInformation, this.verifier);
}
if (this.context != null) {
entity.setApplicationContext(this.context);
}
Optional.ofNullable(this.applicationContext).ifPresent(entity::setApplicationContext);
return entity;
}
@Nullable
private UserDefinedType resolveUserDefinedType(TypeInformation typeInformation) {
return AnnotatedElementUtils.findMergedAnnotation(typeInformation.getType(), UserDefinedType.class);
}
@NonNull
private UserTypeResolver resolveUserTypeResolver() {
UserTypeResolver resolvedUserTypeResolver = this.userTypeResolver;
Assert.state(resolvedUserTypeResolver != null, "UserTypeResolver must not be null");
return resolvedUserTypeResolver;
}
/* (non-Javadoc)
* @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentProperty(org.springframework.data.mapping.model.Property, org.springframework.data.mapping.model.MutablePersistentEntity, org.springframework.data.mapping.model.SimpleTypeHolder)
*/
@@ -308,14 +329,12 @@ public class CassandraMappingContext
protected CassandraPersistentProperty createPersistentProperty(Property property,
BasicCassandraPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
BasicCassandraPersistentProperty cassandraProperty = new BasicCassandraPersistentProperty(property, owner,
simpleTypeHolder, this.userTypeResolver);
BasicCassandraPersistentProperty persistentProperty =
new BasicCassandraPersistentProperty(property, owner, simpleTypeHolder, this.userTypeResolver);
if (this.context != null) {
cassandraProperty.setApplicationContext(this.context);
}
Optional.ofNullable(this.applicationContext).ifPresent(persistentProperty::setApplicationContext);
return cassandraProperty;
return persistentProperty;
}
/**
@@ -326,7 +345,7 @@ public class CassandraMappingContext
*/
public boolean usesTable(CqlIdentifier name) {
Assert.notNull(name, "Table name must not be null!");
Assert.notNull(name, "Table name must not be null");
return this.entitySetsByTableName.containsKey(name);
}
@@ -340,23 +359,26 @@ public class CassandraMappingContext
*/
public boolean usesUserType(CqlIdentifier name) {
Assert.notNull(name, "User type name must not be null!");
Assert.notNull(name, "User type name must not be null");
return hasMappedUserType(name) || hasReferencedUserType(name);
}
private boolean hasReferencedUserType(CqlIdentifier identifier) {
return getPersistentEntities().stream().flatMap(entity -> StreamSupport.stream(entity.spliterator(), false))
.flatMap(it -> Optionals.toStream(Optional.ofNullable(it.findAnnotation(CassandraType.class))))
.map(CassandraType::userTypeName).filter(StringUtils::hasText).map(CqlIdentifier::of)
.anyMatch(identifier::equals);
}
private boolean hasMappedUserType(CqlIdentifier identifier) {
return this.userDefinedTypes.stream().map(CassandraPersistentEntity::getTableName).anyMatch(identifier::equals);
}
private boolean hasReferencedUserType(CqlIdentifier identifier) {
return getPersistentEntities().stream()
.flatMap(entity -> StreamSupport.stream(entity.spliterator(), false))
.flatMap(it -> Optionals.toStream(Optional.ofNullable(it.findAnnotation(CassandraType.class))))
.map(CassandraType::userTypeName)
.filter(StringUtils::hasText)
.map(CqlIdentifier::of)
.anyMatch(identifier::equals);
}
/**
* Returns a {@link CreateTableSpecification} for the given entity, including all mapping information.
*
@@ -370,36 +392,31 @@ public class CassandraMappingContext
for (CassandraPersistentProperty property : entity) {
if (!property.isCompositePrimaryKey()) {
continue;
}
if (property.isCompositePrimaryKey()) {
CassandraPersistentEntity<?> primaryKeyEntity = getRequiredPersistentEntity(property.getRawType());
CassandraPersistentEntity<?> primaryKeyEntity = getRequiredPersistentEntity(property.getRawType());
for (CassandraPersistentProperty primaryKeyProperty : primaryKeyEntity) {
if (primaryKeyProperty.isPartitionKeyColumn()) {
specification.partitionKeyColumn(primaryKeyProperty.getColumnName(), getDataType(primaryKeyProperty));
} else { // it's a cluster column
specification.clusteredKeyColumn(primaryKeyProperty.getColumnName(), getDataType(primaryKeyProperty),
primaryKeyProperty.getPrimaryKeyOrdering());
for (CassandraPersistentProperty primaryKeyProperty : primaryKeyEntity) {
if (primaryKeyProperty.isPartitionKeyColumn()) {
specification.partitionKeyColumn(primaryKeyProperty.getColumnName(),
getDataType(primaryKeyProperty));
} else { // cluster column
specification.clusteredKeyColumn(primaryKeyProperty.getColumnName(),
getDataType(primaryKeyProperty), primaryKeyProperty.getPrimaryKeyOrdering());
}
}
}
}
for (CassandraPersistentProperty property : entity) {
if (property.isCompositePrimaryKey()) {
continue;
}
if (property.isIdProperty() || property.isPartitionKeyColumn()) {
specification.partitionKeyColumn(property.getColumnName(),
UserTypeUtil.potentiallyFreeze(getDataType(property)));
} else if (property.isClusterKeyColumn()) {
specification.clusteredKeyColumn(property.getColumnName(),
UserTypeUtil.potentiallyFreeze(getDataType(property)), property.getPrimaryKeyOrdering());
} else {
specification.column(property.getColumnName(), UserTypeUtil.potentiallyFreeze(getDataType(property)));
else {
if (property.isIdProperty() || property.isPartitionKeyColumn()) {
specification.partitionKeyColumn(property.getColumnName(),
UserTypeUtil.potentiallyFreeze(getDataType(property)));
} else if (property.isClusterKeyColumn()) {
specification.clusteredKeyColumn(property.getColumnName(),
UserTypeUtil.potentiallyFreeze(getDataType(property)), property.getPrimaryKeyOrdering());
} else {
specification.column(property.getColumnName(),
UserTypeUtil.potentiallyFreeze(getDataType(property)));
}
}
}
@@ -419,17 +436,11 @@ public class CassandraMappingContext
Assert.notNull(entity, "CassandraPersistentEntity must not be null");
return getCreateIndexSpecifications(entity.getTableName(), entity);
}
private List<CreateIndexSpecification> getCreateIndexSpecifications(CqlIdentifier tableName,
CassandraPersistentEntity<?> entity) {
List<CreateIndexSpecification> indexes = new ArrayList<>();
for (CassandraPersistentProperty property : entity) {
if (property.isCompositePrimaryKey()) {
indexes.addAll(getCreateIndexSpecifications(tableName, getRequiredPersistentEntity(property)));
indexes.addAll(getCreateIndexSpecificationsFor(getRequiredPersistentEntity(property)));
} else {
indexes.addAll(IndexSpecificationFactory.createIndexSpecifications(property));
}
@@ -466,6 +477,31 @@ public class CassandraMappingContext
return specification;
}
/**
* Retrieve the data type based on the given {@code type}. Cassandra {@link DataType types} are determined using
* simple types and configured {@link org.springframework.data.convert.CustomConversions}.
*
* @param type must not be {@literal null}.
* @return the Cassandra {@link DataType type}.
* @see org.springframework.data.convert.CustomConversions
* @see CassandraSimpleTypeHolder
* @since 1.5
*/
public DataType getDataType(Class<?> type) {
return this.customConversions.getCustomWriteTarget(type)
.map(CassandraSimpleTypeHolder::getDataTypeFor)
.orElseGet(() -> getDataTypeFor(type));
}
@Nullable
private DataType getDataType(@NonNull Class<?> type, DataTypeProvider dataTypeProvider) {
BasicCassandraPersistentEntity<?> entity = getPersistentEntity(type);
return entity != null && entity.isUserDefinedType() ? dataTypeProvider.getDataType(entity) : getDataType(type);
}
/**
* Retrieve the data type of the property. Cassandra {@link DataType types} are determined using simple types and
* configured {@link org.springframework.data.convert.CustomConversions}.
@@ -487,10 +523,20 @@ public class CassandraMappingContext
CassandraType annotation = property.getRequiredAnnotation(CassandraType.class);
if (annotation.type() == Name.TUPLE) {
DataType[] dataTypes = Arrays.stream(annotation.typeArguments())
.map(CassandraSimpleTypeHolder::getDataTypeFor)
.toArray(DataType[]::new);
return TupleType.of(ProtocolVersion.NEWEST_SUPPORTED, CodecRegistry.DEFAULT_INSTANCE, dataTypes);
}
if (annotation.type() == Name.UDT) {
CqlIdentifier userTypeName = CqlIdentifier.of(annotation.userTypeName());
DataType userType = dataTypeProvider.getUserType(userTypeName, userTypeResolver);
DataType userType = dataTypeProvider.getUserType(userTypeName, resolveUserTypeResolver());
if (userType == null) {
throw new MappingException(String.format("User type [%s] not found", userTypeName));
@@ -503,14 +549,6 @@ public class CassandraMappingContext
}
}
if (annotation.type() == Name.TUPLE) {
DataType[] dataTypes = Arrays.stream(annotation.typeArguments()) //
.map(CassandraSimpleTypeHolder::getDataTypeFor) //
.toArray(DataType[]::new);
return TupleType.of(ProtocolVersion.NEWEST_SUPPORTED, CodecRegistry.DEFAULT_INSTANCE, dataTypes);
}
return property.getDataType();
}
@@ -526,11 +564,13 @@ public class CassandraMappingContext
}
return this.customConversions.getCustomWriteTarget(property.getType())
.map(CassandraSimpleTypeHolder::getDataTypeFor).orElseGet(() -> this.customConversions
.getCustomWriteTarget(property.getActualType()).filter(it -> !property.isMapLike()).map(it -> {
.map(CassandraSimpleTypeHolder::getDataTypeFor)
.orElseGet(() -> this.customConversions
.getCustomWriteTarget(property.getActualType())
.filter(it -> !property.isMapLike())
.map(it -> {
if (property.isCollectionLike()) {
if (List.class.isAssignableFrom(property.getType())) {
return DataType.list(getDataTypeFor(it));
}
@@ -541,6 +581,7 @@ public class CassandraMappingContext
}
return getDataTypeFor(it);
}).orElseGet(() -> {
if (property.isMapLike()) {
@@ -548,19 +589,13 @@ public class CassandraMappingContext
Class<?> keyType = property.getComponentType();
Class<?> valueType = property.getMapValueType();
return DataType.map(getDataType(keyType, dataTypeProvider), getDataType(valueType, dataTypeProvider));
return DataType.map(getDataType(keyType, dataTypeProvider),
getDataType(valueType, dataTypeProvider));
}
return property.getDataType();
}));
}
private DataType getDataType(Class<?> type, DataTypeProvider dataTypeProvider) {
BasicCassandraPersistentEntity<?> entity = getPersistentEntity(type);
return entity != null && entity.isUserDefinedType() ? dataTypeProvider.getDataType(entity) : getDataType(type);
}
@Nullable
@@ -568,36 +603,16 @@ public class CassandraMappingContext
if (property.isCollectionLike()) {
if (Set.class.isAssignableFrom(property.getType())) {
return DataType.set(elementType);
}
if (List.class.isAssignableFrom(property.getType())) {
return DataType.list(elementType);
}
if (Set.class.isAssignableFrom(property.getType())) {
return DataType.set(elementType);
}
}
if (!property.isCollectionLike() && !property.isMapLike()) {
return elementType;
}
return null;
}
/**
* Retrieve the data type based on the given {@code type}. Cassandra {@link DataType types} are determined using
* simple types and configured {@link org.springframework.data.convert.CustomConversions}.
*
* @param type must not be {@literal null}.
* @return the Cassandra {@link DataType type}.
* @see org.springframework.data.convert.CustomConversions
* @see CassandraSimpleTypeHolder
* @since 1.5
*/
public DataType getDataType(Class<?> type) {
return this.customConversions.getCustomWriteTarget(type).map(CassandraSimpleTypeHolder::getDataTypeFor)
.orElseGet(() -> getDataTypeFor(type));
return !(property.isCollectionLike() || property.isMapLike()) ? elementType : null;
}
/**
@@ -652,5 +667,6 @@ public class CassandraMappingContext
*/
@Nullable
abstract DataType getUserType(CqlIdentifier userTypeName, UserTypeResolver userTypeResolver);
}
}

View File

@@ -22,39 +22,46 @@ import java.lang.annotation.RetentionPolicy;
import com.datastax.driver.core.DataType;
/**
* Specifies the Cassandra type of the annotated property or parameter if used in query methods.
* Specifies the Cassandra type of the annotated property or parameter when used in query methods.
*
* @author Alex Shvid
* @author Matthew T. Adams
* @author Mark Paluch
* @see com.datastax.driver.core.DataType
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface CassandraType {
/**
* The {@link DataType}.{@link Name} of the property.
* The {@link DataType.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.
* If the property is {@link java.util.Collection Collection-like}, then this attribute holds
* a single {@link DataType.Name DataType Name} representing the element type of the {@link java.util.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.
* If the property is a {@link java.util.Map}, then this attribute holds exactly
* two {@link DataType.Name DataType Names}; 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.
* If the property is neither {@link java.util.Collection Collection-like} nor a {@link java.util.Map},
* then this attribute is ignored.
*
* @return an array of {@link DataType.Name} objects.
* @see com.datastax.driver.core.DataType.Name
*/
DataType.Name[] typeArguments() default {};
/**
* If the property maps to a user-defined type then this attribute holds the user type name. For collection-like
* properties the user type name applies to the component type. The user type name is only required if the UDT does
* not map to a class annotated with {@link UserDefinedType}.
* If the property maps to a User-Defined Type (UDT) then this attribute holds the user type name.
*
* @return name of the user type
* For {@link java.util.Collection Collection-like} properties the user type name applies to the component type.
* The user type name is only required if the UDT does not map to a class annotated with {@link UserDefinedType}.
*
* @return {@link String name} of the user type
* @since 1.5
*/
String userTypeName() default "";
}

View File

@@ -63,40 +63,43 @@ public class EntityMapping {
@Nullable
public String getEntityClassName() {
return entityClassName;
return this.entityClassName;
}
public void setEntityClassName(String entityClassName) {
Assert.hasText(entityClassName, "Entity class name must not be null or empty");
this.entityClassName = entityClassName;
}
public String getForceQuote() {
return forceQuote;
return this.forceQuote;
}
public void setForceQuote(String forceQuote) {
Assert.notNull(forceQuote, "Force quote must not be null or empty");
this.forceQuote = forceQuote;
}
public Map<String, PropertyMapping> getPropertyMappings() {
return Collections.unmodifiableMap(propertyMappings);
return Collections.unmodifiableMap(this.propertyMappings);
}
public void setPropertyMappings(@Nullable Map<String, PropertyMapping> propertyMappings) {
this.propertyMappings = (propertyMappings != null ? new HashMap<>(propertyMappings) : Collections.emptyMap());
this.propertyMappings = propertyMappings != null ? new HashMap<>(propertyMappings) : Collections.emptyMap();
}
public String getTableName() {
return tableName;
return this.tableName;
}
public void setTableName(String tableName) {
Assert.notNull(tableName, "Table name must not be null or empty");
this.tableName = tableName;
}
@@ -105,6 +108,7 @@ public class EntityMapping {
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
@@ -127,9 +131,11 @@ public class EntityMapping {
public int hashCode() {
int hashValue = 17;
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(this.getEntityClassName());
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(this.getForceQuote());
hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(this.getTableName());
return hashValue;
}
@@ -139,12 +145,14 @@ public class EntityMapping {
@Override
public String toString() {
return String.format(
"{ @type = %1$s, entityClassName = %2$s, tableName = %3$s, forceQuote = %4$s, propertyMappings = %5$s }",
"{ @type = %1$s, entityClassName = %2$s, tableName = %3$s, forceQuote = %4$s, propertyMappings = %5$s }",
getClass().getName(), getEntityClassName(), getTableName(), getForceQuote(), toString(getPropertyMappings()));
}
private String toString(Map<?, ?> map) {
StringBuilder builder = new StringBuilder("[");
int count = 0;
for (Map.Entry<?, ?> entry : map.entrySet()) {

View File

@@ -17,6 +17,7 @@ package org.springframework.data.cassandra.core.mapping;
import lombok.EqualsAndHashCode;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -46,9 +47,10 @@ public class PropertyMapping {
this(propertyName, columnName, "false");
}
public PropertyMapping(String propertyName, String columnName, String forceQuote) {
public PropertyMapping(@NonNull String propertyName, @NonNull String columnName, String forceQuote) {
Assert.notNull(propertyName, "Property name must not be null");
this.propertyName = propertyName;
setColumnName(columnName);
@@ -57,18 +59,19 @@ public class PropertyMapping {
@Nullable
public String getColumnName() {
return columnName;
return this.columnName;
}
public void setColumnName(String columnName) {
public void setColumnName(@NonNull String columnName) {
Assert.notNull(columnName, "Column name must not be null");
this.columnName = columnName;
}
@Nullable
public String getForceQuote() {
return forceQuote;
return this.forceQuote;
}
public void setForceQuote(String forceQuote) {
@@ -76,7 +79,7 @@ public class PropertyMapping {
}
public String getPropertyName() {
return propertyName;
return this.propertyName;
}
/* (non-Javadoc)

View File

@@ -21,20 +21,25 @@ import org.springframework.lang.Nullable;
import com.datastax.driver.core.UserType;
/**
* Strategy interface to resolve {@link UserType} by its name.
* Strategy interface to resolve {@link UserType} by {@link String name}.
*
* @author Mark Paluch
* @see com.datastax.driver.core.DataType
* @see org.springframework.data.cassandra.core.cql.CqlIdentifier
* @since 1.5
*/
@FunctionalInterface
public interface UserTypeResolver {
/**
* Resolve a {@link UserType} by its name.
* Resolve a {@link UserType} by {@link String name}.
*
* @param typeName must not be {@literal null}.
* @return the type or {@literal null}, if not found.
* @param typeName {@link String name} of the {@link UserType} to resolve; must not be {@literal null}.
* @return the resolved {@link UserType} or {@literal null} if not found.
* @see org.springframework.data.cassandra.core.cql.CqlIdentifier
* @see com.datastax.driver.core.DataType
*/
@Nullable
UserType resolveType(CqlIdentifier typeName);
}

View File

@@ -15,12 +15,8 @@
*/
package org.springframework.data.cassandra.core.convert;
import static org.assertj.core.api.Assertions.*;
import static org.junit.Assume.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assume.assumeTrue;
import java.math.BigDecimal;
import java.math.BigInteger;
@@ -35,10 +31,14 @@ import java.util.HashSet;
import java.util.List;
import java.util.UUID;
import org.assertj.core.api.Assertions;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.core.CassandraTemplate;
@@ -48,6 +48,8 @@ import org.springframework.data.cassandra.support.CassandraVersion;
import org.springframework.data.cassandra.test.util.AbstractKeyspaceCreatingIntegrationTest;
import org.springframework.data.util.Version;
import org.assertj.core.api.Assertions;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.Duration;
import com.datastax.driver.core.LocalDate;
@@ -62,7 +64,7 @@ import com.datastax.driver.core.TupleValue;
* @soundtrack DJ THT meets Scarlet - Live 2 Dance (Extended Mix) (Zgin Remix)
*/
@SuppressWarnings("Since15")
public class CassandraTypeMappingIntegrationTest extends AbstractKeyspaceCreatingIntegrationTest {
public class CassandraTypeMappingIntegrationTests extends AbstractKeyspaceCreatingIntegrationTest {
static final Version VERSION_3_10 = Version.parse("3.10");
@@ -460,11 +462,13 @@ public class CassandraTypeMappingIntegrationTest extends AbstractKeyspaceCreatin
public void shouldReadAndWriteTupleType() {
TupleType tupleType = cluster.getMetadata().newTupleType(DataType.varchar(), DataType.bigint());
AllPossibleTypes entity = new AllPossibleTypes("1");
entity.setTupleValue(tupleType.newValue("foo", 23L));
operations.insert(entity);
AllPossibleTypes loaded = operations.selectOneById(entity.getId(), AllPossibleTypes.class);
assertThat(loaded.getTupleValue().getObject(0)).isEqualTo("foo");
@@ -477,11 +481,12 @@ public class CassandraTypeMappingIntegrationTest extends AbstractKeyspaceCreatin
TupleType tupleType = cluster.getMetadata().newTupleType(DataType.varchar(), DataType.bigint());
ListOfTuples entity = new ListOfTuples();
entity.setId("foo");
entity.setId("foo");
entity.setTuples(Arrays.asList(tupleType.newValue("foo", 23L), tupleType.newValue("bar", 42L)));
operations.insert(entity);
ListOfTuples loaded = operations.selectOneById(entity.getId(), ListOfTuples.class);
assertThat(loaded.getTuples().get(0).getObject(0)).isEqualTo("foo");

View File

@@ -15,8 +15,10 @@
*/
package org.springframework.data.cassandra.core.mapping;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.Serializable;
import java.util.Collection;
@@ -27,6 +29,7 @@ import java.util.NoSuchElementException;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.convert.converter.Converter;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.annotation.Id;
@@ -64,12 +67,12 @@ public class CassandraMappingContextUnitTests {
@Before
public void before() {
mappingContext.setUserTypeResolver(typeName -> null);
this.mappingContext.setUserTypeResolver(typeName -> null);
}
@Test
public void testgetRequiredPersistentEntityOfTransientType() {
mappingContext.getRequiredPersistentEntity(Transient.class);
this.mappingContext.getRequiredPersistentEntity(Transient.class);
}
private static class Transient {}
@@ -342,15 +345,16 @@ public class CassandraMappingContextUnitTests {
@Test(expected = InvalidDataAccessApiUsageException.class) // DATACASS-284
public void shouldRejectUntypedTuples() {
mappingContext.getCreateTableSpecificationFor(mappingContext.getRequiredPersistentEntity(UntypedTupleEntity.class));
this.mappingContext.getCreateTableSpecificationFor(
this.mappingContext.getRequiredPersistentEntity(UntypedTupleEntity.class));
}
@Test // DATACASS-284
public void shouldCreateTableForTypedTupleType() {
CreateTableSpecification tableSpecification = mappingContext
.getCreateTableSpecificationFor(mappingContext.getRequiredPersistentEntity(TypedTupleEntity.class));
CreateTableSpecification tableSpecification =
this.mappingContext.getCreateTableSpecificationFor(
this.mappingContext.getRequiredPersistentEntity(TypedTupleEntity.class));
assertThat(tableSpecification.getColumns()).hasSize(2);
@@ -662,17 +666,15 @@ public class CassandraMappingContextUnitTests {
String str;
}
@Table
static class UntypedTupleEntity {
@Id String id;
TupleType untyped;
}
@Table
static class TypedTupleEntity {
@Id String id;
@CassandraType(type = Name.TUPLE, typeArguments = { Name.VARCHAR, Name.BIGINT }) TupleValue typed;
}
@Table
static class UntypedTupleEntity {
@Id String id;
TupleType untyped;
}
}

View File

@@ -30,7 +30,7 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.springframework.data.cassandra.core.convert.CassandraTypeMappingIntegrationTest.Condition;
import org.springframework.data.cassandra.core.convert.CassandraTypeMappingIntegrationTests.Condition;
import org.springframework.data.cassandra.core.mapping.CassandraType;
import org.springframework.data.cassandra.core.mapping.PrimaryKey;
import org.springframework.data.cassandra.core.mapping.Table;

View File

@@ -15,7 +15,8 @@
*/
package org.springframework.data.cassandra.repository.isolated;
import static org.assertj.core.api.Assertions.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.offset;
import java.math.BigDecimal;
import java.math.BigInteger;
@@ -31,6 +32,7 @@ import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.config.SchemaAction;
@@ -261,11 +263,13 @@ public class RepositoryReturnTypesIntegrationTests extends AbstractSpringDataEmb
public void shouldReturnEntityAsMap() {
AllPossibleTypes entity = new AllPossibleTypes("123");
entity.setPrimitiveInteger(123);
entity.setBigInteger(BigInteger.ONE);
allPossibleTypesRepository.save(entity);
Map<String, Object> result = allPossibleTypesRepository.findEntityAsMapById(entity.getId());
assertThat(result.size()).isGreaterThan(30);
assertThat(result.get("primitiveinteger")).isEqualTo((Object) Integer.valueOf(123));
assertThat(result.get("biginteger")).isEqualTo((Object) BigInteger.ONE);

View File

@@ -613,7 +613,12 @@ NOTE: `SchemaAction.RECREATE`/`SchemaAction.RECREATE_DROP_UNUSED` will drop your
==== Enabling Tables and User-Defined Types for Schema Management
<<mapping.usage>> explains object mapping using conventions and annotations. Schema management is only active for entities annotated with `@Table` and user-defined types annotated with `@UserDefinedType` to prevent unwanted classes from being created as table/type. Entities are discovered by scanning the class path. Entity scanning requires one or more base packages. Tuple typed columns using `TupleValue` do not provide any typing details hences you must annotate such column properties with `@CassandraType(type = TUPLE, typeArguments = …)` to specify the desired column type.
<<mapping.usage>> explains object mapping using conventions and annotations. Schema management is only active
for entities annotated with `@Table` and user-defined types annotated with `@UserDefinedType` to prevent
unwanted classes from being created as table/type. Entities are discovered by scanning the classpath.
Entity scanning requires one or more base packages. Tuple typed columns using `TupleValue` do not provide
any typing details hences you must annotate such column properties with `@CassandraType(type = TUPLE, typeArguments = …)`
to specify the desired column type.
.Specifying Entity Base Packages via XML
====