DATACASS-470 - Move Cassandra type resolution into MappingCassandraConverter.

All DataType-resolution functionality is now encapsulated in ColumnTypeResolver and all DataType-related methods (getDataType(), getTupleType(), getUserType()) were migrated into ColumnTypeResolver. DataType resolution requires always a converter context to apply registered converters so this concern cannot be handled entirely on the mapping level.

Related to type resolution also schema object derivation (create table/index/user type) functionality was moved from CassandraMappingContext into SchemaFactory as schema object derivation requires the converter registrations.
This commit is contained in:
Mark Paluch
2020-03-23 15:20:33 +01:00
parent 5b0e3f0c56
commit 64d46da6a4
51 changed files with 1248 additions and 1573 deletions

View File

@@ -68,12 +68,17 @@ public abstract class AbstractCassandraConfiguration extends AbstractSessionConf
@Bean
public CassandraConverter cassandraConverter() {
MappingCassandraConverter mappingCassandraConverter =
new MappingCassandraConverter(requireBeanOfType(CassandraMappingContext.class));
UserTypeResolver userTypeResolver = new SimpleUserTypeResolver(getRequiredSession(),
CqlIdentifier.fromCql(getKeyspaceName()));
mappingCassandraConverter.setCustomConversions(requireBeanOfType(CassandraCustomConversions.class));
MappingCassandraConverter converter = new MappingCassandraConverter(
requireBeanOfType(CassandraMappingContext.class));
return mappingCassandraConverter;
converter.setCodecRegistry(getRequiredSession().getContext().getCodecRegistry());
converter.setUserTypeResolver(userTypeResolver);
converter.setCustomConversions(requireBeanOfType(CassandraCustomConversions.class));
return converter;
}
/**
@@ -86,11 +91,11 @@ public abstract class AbstractCassandraConfiguration extends AbstractSessionConf
@Bean
public CassandraMappingContext cassandraMapping() throws ClassNotFoundException {
UserTypeResolver userTypeResolver =
new SimpleUserTypeResolver(getRequiredSession(), CqlIdentifier.fromCql(getKeyspaceName()));
UserTypeResolver userTypeResolver = new SimpleUserTypeResolver(getRequiredSession(),
CqlIdentifier.fromCql(getKeyspaceName()));
CassandraMappingContext mappingContext =
new CassandraMappingContext(userTypeResolver, SimpleTupleTypeFactory.DEFAULT);
CassandraMappingContext mappingContext = new CassandraMappingContext(userTypeResolver,
SimpleTupleTypeFactory.DEFAULT);
CustomConversions customConversions = requireBeanOfType(CustomConversions.class);
@@ -156,8 +161,8 @@ public abstract class AbstractCassandraConfiguration extends AbstractSessionConf
/**
* Configures the Java {@link ClassLoader} used to resolve Cassandra application entity {@link Class types}.
*
* @param classLoader Java {@link ClassLoader} used to resolve Cassandra application entity {@link Class types};
* may be {@literal null}.
* @param classLoader Java {@link ClassLoader} used to resolve Cassandra application entity {@link Class types}; may
* be {@literal null}.
* @see java.lang.ClassLoader
*/
@Override

View File

@@ -18,6 +18,7 @@ package org.springframework.data.cassandra.core;
import java.util.Map;
import java.util.Optional;
import org.springframework.data.cassandra.core.convert.SchemaFactory;
import org.springframework.data.cassandra.core.mapping.Table;
import com.datastax.oss.driver.api.core.CqlIdentifier;
@@ -34,6 +35,12 @@ import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata;
*/
public interface CassandraAdminOperations extends CassandraOperations {
/**
* @return the {@link SchemaFactory}.
* @since 3.0
*/
SchemaFactory getSchemaFactory();
/**
* Create a table with the name given and fields corresponding to the given class. If the table already exists and
* parameter {@code ifNotExists} is {@literal true}, this is a no-op and {@literal false} is returned. If the table

View File

@@ -20,6 +20,7 @@ import java.util.Optional;
import org.springframework.data.cassandra.SessionFactory;
import org.springframework.data.cassandra.core.convert.CassandraConverter;
import org.springframework.data.cassandra.core.convert.SchemaFactory;
import org.springframework.data.cassandra.core.cql.CqlOperations;
import org.springframework.data.cassandra.core.cql.SessionCallback;
import org.springframework.data.cassandra.core.cql.generator.CreateTableCqlGenerator;
@@ -48,6 +49,8 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand
protected static final boolean DEFAULT_DROP_TABLE_IF_EXISTS = false;
private final SchemaFactory schemaFactory;
/**
* Constructor used for a basic template configuration.
*
@@ -56,6 +59,8 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand
*/
public CassandraAdminTemplate(CqlSession session) {
super(session);
this.schemaFactory = new SchemaFactory(getConverter());
}
/**
@@ -66,6 +71,7 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand
*/
public CassandraAdminTemplate(CqlSession session, CassandraConverter converter) {
super(session, converter);
this.schemaFactory = new SchemaFactory(getConverter());
}
/**
@@ -76,6 +82,8 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand
*/
public CassandraAdminTemplate(SessionFactory sessionFactory, CassandraConverter converter) {
super(sessionFactory, converter);
this.schemaFactory = new SchemaFactory(getConverter());
}
/**
@@ -87,6 +95,13 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand
*/
public CassandraAdminTemplate(CqlOperations cqlOperations, CassandraConverter converter) {
super(cqlOperations, converter);
this.schemaFactory = new SchemaFactory(getConverter());
}
@Override
public SchemaFactory getSchemaFactory() {
return schemaFactory;
}
/* (non-Javadoc)
@@ -98,8 +113,8 @@ public class CassandraAdminTemplate extends CassandraTemplate implements Cassand
CassandraPersistentEntity<?> entity = getConverter().getMappingContext().getRequiredPersistentEntity(entityClass);
CreateTableSpecification createTableSpecification = getConverter().getMappingContext()
.getCreateTableSpecificationFor(tableName, entity).ifNotExists(ifNotExists);
CreateTableSpecification createTableSpecification = this.schemaFactory
.getCreateTableSpecificationFor(entity, tableName).ifNotExists(ifNotExists);
getCqlOperations().execute(CreateTableCqlGenerator.toCql(createTableSpecification));
}

View File

@@ -57,6 +57,22 @@ public class CassandraPersistentEntitySchemaCreator {
private final CassandraMappingContext mappingContext;
/**
* Create a new {@link CassandraPersistentEntitySchemaCreator} for the given {@link CassandraMappingContext} and
* {@link CassandraAdminOperations}.
*
* @param mappingContext must not be {@literal null}.
* @param cassandraAdminOperations must not be {@literal null}.
* @since 3.0
*/
public CassandraPersistentEntitySchemaCreator(CassandraAdminOperations cassandraAdminOperations) {
Assert.notNull(cassandraAdminOperations, "CassandraAdminOperations must not be null");
this.cassandraAdminOperations = cassandraAdminOperations;
this.mappingContext = cassandraAdminOperations.getConverter().getMappingContext();
}
/**
* Create a new {@link CassandraPersistentEntitySchemaCreator} for the given {@link CassandraMappingContext} and
* {@link CassandraAdminOperations}.
@@ -96,7 +112,8 @@ public class CassandraPersistentEntitySchemaCreator {
return this.mappingContext.getTableEntities() //
.stream() //
.map(entity -> this.mappingContext.getCreateTableSpecificationFor(entity).ifNotExists(ifNotExists)) //
.map(entity -> cassandraAdminOperations.getSchemaFactory().getCreateTableSpecificationFor(entity)
.ifNotExists(ifNotExists)) //
.collect(Collectors.toList());
}
@@ -122,7 +139,7 @@ public class CassandraPersistentEntitySchemaCreator {
return this.mappingContext.getTableEntities() //
.stream() //
.flatMap(entity -> this.mappingContext.getCreateIndexSpecificationsFor(entity).stream()) //
.flatMap(entity -> cassandraAdminOperations.getSchemaFactory().getCreateIndexSpecificationsFor(entity).stream()) //
.peek(it -> it.ifNotExists(ifNotExists)) //
.collect(Collectors.toList());
}
@@ -161,11 +178,10 @@ public class CassandraPersistentEntitySchemaCreator {
visitUserTypes(entity, udts);
});
specifications
.addAll(udts
.stream().map(identifier -> this.mappingContext
.getCreateUserTypeSpecificationFor(byTableName.get(identifier)).ifNotExists(ifNotExists))
.collect(Collectors.toList()));
specifications.addAll(udts.stream()
.map(identifier -> cassandraAdminOperations.getSchemaFactory()
.getCreateUserTypeSpecificationFor(byTableName.get(identifier)).ifNotExists(ifNotExists))
.collect(Collectors.toList()));
return specifications;
}

View File

@@ -31,6 +31,7 @@ public interface CassandraColumnType extends ColumnType {
* Returns the {@link DataType} associated with this column type.
*
* @return
* @throws org.springframework.data.mapping.MappingException if the column cannot be mapped onto a Cassandra type.
*/
DataType getDataType();

View File

@@ -25,6 +25,8 @@ import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* Central Cassandra specific converter interface from Object to Row.
*
@@ -42,6 +44,14 @@ public interface CassandraConverter
*/
CustomConversions getCustomConversions();
/**
* Returns the {@link CodecRegistry} registered in the {@link CassandraConverter}.
*
* @return the {@link CodecRegistry}.
* @since 3.0
*/
CodecRegistry getCodecRegistry();
/* (non-Javadoc)
* @see org.springframework.data.convert.EntityConverter#getMappingContext()
*/

View File

@@ -88,8 +88,8 @@ public interface ColumnType {
* @return
*/
static CassandraColumnType listOf(CassandraColumnType componentType) {
return new DefaultCassandraColumnType(ClassTypeInformation.LIST, DataTypes.listOf(componentType.getDataType()),
componentType);
return new DefaultCassandraColumnType(ClassTypeInformation.LIST,
() -> DataTypes.listOf(componentType.getDataType()), componentType);
}
/**
@@ -114,7 +114,7 @@ public interface ColumnType {
* @return
*/
static CassandraColumnType setOf(CassandraColumnType componentType) {
return new DefaultCassandraColumnType(ClassTypeInformation.SET, DataTypes.setOf(componentType.getDataType()),
return new DefaultCassandraColumnType(ClassTypeInformation.SET, () -> DataTypes.setOf(componentType.getDataType()),
componentType);
}
@@ -138,7 +138,7 @@ public interface ColumnType {
*/
static CassandraColumnType mapOf(CassandraColumnType keyType, CassandraColumnType valueType) {
return new DefaultCassandraColumnType(ClassTypeInformation.MAP,
DataTypes.mapOf(keyType.getDataType(), valueType.getDataType()), keyType, valueType);
() -> DataTypes.mapOf(keyType.getDataType(), valueType.getDataType()), keyType, valueType);
}
/**

View File

@@ -37,6 +37,7 @@ public interface ColumnTypeResolver {
* @return
* @see CassandraType
* @see CassandraPersistentProperty
* @throws org.springframework.dao.InvalidDataAccessApiUsageException
*/
default CassandraColumnType resolve(CassandraPersistentProperty property) {
@@ -57,6 +58,7 @@ public interface ColumnTypeResolver {
* @return
* @see org.springframework.data.cassandra.core.mapping.CassandraSimpleTypeHolder
* @see CassandraCustomConversions
* @throws org.springframework.dao.InvalidDataAccessApiUsageException
*/
CassandraColumnType resolve(TypeInformation<?> typeInformation);
@@ -67,6 +69,7 @@ public interface ColumnTypeResolver {
* @return
* @see CassandraType
* @see CassandraPersistentProperty
* @throws org.springframework.dao.InvalidDataAccessApiUsageException
*/
CassandraColumnType resolve(CassandraType annotation);

View File

@@ -15,9 +15,11 @@
*/
package org.springframework.data.cassandra.core.convert;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.TypeInformation;
import com.datastax.oss.driver.api.core.type.DataType;
@@ -31,16 +33,23 @@ import com.datastax.oss.driver.api.core.type.UserDefinedType;
*/
class DefaultCassandraColumnType extends DefaultColumnType implements CassandraColumnType {
private final DataType dataType;
private final Lazy<DataType> dataType;
DefaultCassandraColumnType(Class<?> type, DataType dataType, ColumnType... parameters) {
this(ClassTypeInformation.from(type), dataType, parameters);
}
DefaultCassandraColumnType(TypeInformation<?> typeInformation, Supplier<DataType> dataType,
ColumnType... parameters) {
super(typeInformation, parameters);
this.dataType = Lazy.of(dataType);
}
DefaultCassandraColumnType(TypeInformation<?> typeInformation, DataType dataType, ColumnType... parameters) {
super(typeInformation, parameters);
this.dataType = dataType;
this.dataType = Lazy.of(dataType);
}
/*
@@ -48,7 +57,7 @@ class DefaultCassandraColumnType extends DefaultColumnType implements CassandraC
* @see org.springframework.data.cassandra.core.convert.CassandraColumnType#getDataType()
*/
public DataType getDataType() {
return dataType;
return dataType.get();
}
/*

View File

@@ -20,30 +20,39 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.StreamSupport;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.cassandra.core.mapping.BasicCassandraPersistentEntity;
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
import org.springframework.data.cassandra.core.mapping.CassandraSimpleTypeHolder;
import org.springframework.data.cassandra.core.mapping.CassandraType;
import org.springframework.data.cassandra.core.mapping.CassandraType.Name;
import org.springframework.data.cassandra.core.mapping.UserTypeResolver;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.data.TupleValue;
import com.datastax.oss.driver.api.core.data.UdtValue;
import com.datastax.oss.driver.api.core.detach.AttachmentPoint;
import com.datastax.oss.driver.api.core.type.DataType;
import com.datastax.oss.driver.api.core.type.DataTypes;
import com.datastax.oss.driver.api.core.type.ListType;
import com.datastax.oss.driver.api.core.type.MapType;
import com.datastax.oss.driver.api.core.type.SetType;
import com.datastax.oss.driver.api.core.type.UserDefinedType;
import com.datastax.oss.driver.api.core.type.codec.CodecNotFoundException;
import com.datastax.oss.driver.api.core.type.codec.TypeCodec;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
import com.datastax.oss.driver.api.core.type.reflect.GenericType;
@@ -56,20 +65,64 @@ import com.datastax.oss.driver.api.core.type.reflect.GenericType;
*/
class DefaultColumnTypeResolver implements ColumnTypeResolver {
private final CassandraMappingContext mappingContext;
private final UserTypeResolver userTypeResolver;
private final CodecRegistry codecRegistry;
private CustomConversions customConversions;
private final Log log = LogFactory.getLog(getClass());
public DefaultColumnTypeResolver(CassandraMappingContext mappingContext) {
private final MappingContext<? extends CassandraPersistentEntity<?>, ? extends CassandraPersistentProperty> mappingContext;
private final UserTypeResolver userTypeResolver;
private final Supplier<CodecRegistry> codecRegistry;
private final Supplier<CustomConversions> customConversions;
/**
* Creates a new {@link DefaultColumnTypeResolver}.
*
* @param mappingContext
* @param userTypeResolver
* @param codecRegistry
* @param customConversions
*/
public DefaultColumnTypeResolver(
MappingContext<? extends CassandraPersistentEntity<?>, ? extends CassandraPersistentProperty> mappingContext,
UserTypeResolver userTypeResolver, Supplier<CodecRegistry> codecRegistry,
Supplier<CustomConversions> customConversions) {
this.mappingContext = mappingContext;
this.userTypeResolver = mappingContext.getUserTypeResolver();
this.codecRegistry = mappingContext.getCodecRegistry();
this.customConversions = mappingContext.getCustomConversions();
this.userTypeResolver = userTypeResolver;
this.codecRegistry = codecRegistry;
this.customConversions = customConversions;
}
public void setCustomConversions(CustomConversions customConversions) {
this.customConversions = customConversions;
@Override
public CassandraColumnType resolve(CassandraPersistentProperty property) {
Assert.notNull(property, "Property must not be null");
if (property.isAnnotationPresent(CassandraType.class)) {
CassandraType annotation = property.getRequiredAnnotation(CassandraType.class);
if (annotation.type() == Name.UDT && StringUtils.isEmpty(annotation.userTypeName())) {
throw new InvalidDataAccessApiUsageException(
String.format("Expected user type name in property ['%s'] of type ['%s'] in entity [%s]",
property.getName(), property.getType(), property.getOwner().getName()));
}
if ((annotation.type() == Name.LIST || annotation.type() == Name.SET) && annotation.typeArguments().length != 1) {
throw new InvalidDataAccessApiUsageException(String.format(
"Expected [%d] type arguments for property ['%s'] of type ['%s'] in entity [%s]; actual was [%d]", 1,
property.getName(), property.getType(), property.getOwner().getName(), annotation.typeArguments().length));
}
if (annotation.type() == Name.MAP && annotation.typeArguments().length != 2) {
throw new InvalidDataAccessApiUsageException(String.format(
"Expected [%d] type arguments for property ['%s'] of type ['%s'] in entity [%s]; actual was [%d]", 2,
property.getName(), property.getType(), property.getOwner().getName(), annotation.typeArguments().length));
}
return resolve(annotation);
}
return resolve(property.getTypeInformation());
}
/*
@@ -79,9 +132,7 @@ class DefaultColumnTypeResolver implements ColumnTypeResolver {
@Override
public CassandraColumnType resolve(TypeInformation<?> typeInformation) {
Optional<Class<?>> writeTarget = customConversions.getCustomWriteTarget(typeInformation.getType());
return writeTarget.map(it -> {
return getCustomWriteTarget(typeInformation).map(it -> {
return createCassandraTypeDescriptor(tryResolve(it), ClassTypeInformation.from(it));
}).orElseGet(() -> {
@@ -93,17 +144,29 @@ class DefaultColumnTypeResolver implements ColumnTypeResolver {
});
}
private Optional<Class<?>> getCustomWriteTarget(TypeInformation<?> typeInformation) {
return customConversions.get().getCustomWriteTarget(typeInformation.getType());
}
@Nullable
private DataType tryResolve(Class<?> type) {
if (TupleValue.class.isAssignableFrom(type)) {
return DataTypes.tupleOf();
return null;
}
if (UdtValue.class.isAssignableFrom(type)) {
return UnknownUserDefinedType.INSTANCE;
return null;
}
return codecRegistry.codecFor(type).getCqlType();
try {
return getCodecRegistry().codecFor(type).getCqlType();
} catch (CodecNotFoundException e) {
if (log.isDebugEnabled()) {
log.debug(String.format("Cannot resolve Codec for %s", type.getName()), e);
}
return null;
}
}
/*
@@ -113,7 +176,7 @@ class DefaultColumnTypeResolver implements ColumnTypeResolver {
@Override
public CassandraColumnType resolve(CassandraType annotation) {
CassandraType.Name type = annotation.type();
Name type = annotation.type();
switch (type) {
case MAP:
@@ -130,11 +193,10 @@ class DefaultColumnTypeResolver implements ColumnTypeResolver {
case SET:
assertTypeArguments(annotation.typeArguments().length, 1);
DataType componentType = annotation.typeArguments()[0] == CassandraType.Name.UDT
? getUserType(annotation.userTypeName())
DataType componentType = annotation.typeArguments()[0] == Name.UDT ? getUserType(annotation.userTypeName())
: CassandraSimpleTypeHolder.getDataTypeFor(annotation.typeArguments()[0]);
if (type == CassandraType.Name.SET) {
if (type == Name.SET) {
return ColumnType.setOf(createCassandraTypeDescriptor(componentType));
}
@@ -148,6 +210,11 @@ class DefaultColumnTypeResolver implements ColumnTypeResolver {
return ColumnType.tupleOf(DataTypes.tupleOf(dataTypes));
case UDT:
if (StringUtils.isEmpty(annotation.userTypeName())) {
throw new InvalidDataAccessApiUsageException(
"Cannot resolve user type for @CassandraType(type=UDT) without userTypeName");
}
return createCassandraTypeDescriptor(getUserType(annotation.userTypeName()));
default:
return createCassandraTypeDescriptor(CassandraSimpleTypeHolder.getDataTypeFor(type));
@@ -165,9 +232,7 @@ class DefaultColumnTypeResolver implements ColumnTypeResolver {
ClassTypeInformation<?> typeInformation = ClassTypeInformation.from(value.getClass());
Optional<Class<?>> writeTarget = customConversions.getCustomWriteTarget(typeInformation.getType());
return writeTarget.map(it -> {
return getCustomWriteTarget(typeInformation).map(it -> {
return (ColumnType) createCassandraTypeDescriptor(tryResolve(it), typeInformation);
}).orElseGet(() -> {
@@ -195,7 +260,7 @@ class DefaultColumnTypeResolver implements ColumnTypeResolver {
return ColumnType.tupleOf(((TupleValue) value).getType());
}
BasicCassandraPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(typeInformation);
CassandraPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(typeInformation);
if (persistentEntity != null) {
@@ -213,11 +278,12 @@ class DefaultColumnTypeResolver implements ColumnTypeResolver {
private CassandraColumnType createCassandraTypeDescriptor(DataType dataType) {
GenericType<Object> javaType = codecRegistry.codecFor(dataType).getJavaType();
GenericType<Object> javaType = getCodecRegistry().codecFor(dataType).getJavaType();
return ColumnType.create(javaType.getRawType(), dataType);
}
private CassandraColumnType createCassandraTypeDescriptor(DataType dataType, TypeInformation<?> typeInformation) {
private CassandraColumnType createCassandraTypeDescriptor(@Nullable DataType dataType,
TypeInformation<?> typeInformation) {
if (typeInformation.isCollectionLike() || typeInformation.isMap()) {
@@ -277,6 +343,10 @@ class DefaultColumnTypeResolver implements ColumnTypeResolver {
}
}
if (dataType == null) {
return new UnresolvableCassandraType(typeInformation);
}
return new DefaultCassandraColumnType(typeInformation, dataType);
}
@@ -295,12 +365,12 @@ class DefaultColumnTypeResolver implements ColumnTypeResolver {
resolve(typeInformation.getRequiredMapValueType()));
}
BasicCassandraPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(typeInformation);
CassandraPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(typeInformation);
if (persistentEntity != null) {
if (persistentEntity.isUserDefinedType()) {
return new DefaultCassandraColumnType(typeInformation, persistentEntity.getUserType());
return new DefaultCassandraColumnType(typeInformation, getUserType(persistentEntity));
}
if (persistentEntity.isTupleType()) {
@@ -316,26 +386,47 @@ class DefaultColumnTypeResolver implements ColumnTypeResolver {
return new UnresolvableCassandraType(typeInformation);
}
return new DefaultCassandraColumnType(typeInformation, tryResolve(typeInformation.getType()));
DataType dataType = tryResolve(typeInformation.getType());
if (dataType == null) {
return new UnresolvableCassandraType(typeInformation);
}
return new DefaultCassandraColumnType(typeInformation, dataType);
}
private DataType getUserType(CassandraPersistentEntity<?> persistentEntity) {
CqlIdentifier identifier = persistentEntity.getTableName();
com.datastax.oss.driver.api.core.type.UserDefinedType userType = userTypeResolver.resolveType(identifier);
if (userType == null) {
throw new MappingException(String.format("User type [%s] not found", identifier));
}
return userType;
}
private Class<?> resolveToJavaType(DataType dataType) {
TypeCodec<Object> codec = codecRegistry.codecFor(dataType);
TypeCodec<Object> codec = getCodecRegistry().codecFor(dataType);
return codec.getJavaType().getRawType();
}
private CodecRegistry getCodecRegistry() {
return codecRegistry.get();
}
private DataType getUserType(String userTypeName) {
UserDefinedType type = userTypeResolver.resolveType(CqlIdentifier.fromCql(userTypeName));
if (type == null) {
throw new IllegalArgumentException(String.format("Cannot resolve UserDefinedType for [%s]", userTypeName));
throw new MappingException(String.format("Cannot resolve UserDefinedType for [%s]", userTypeName));
}
return type;
}
private void assertTypeArguments(int args, int expected) {
private static void assertTypeArguments(int args, int expected) {
if (args != expected) {
throw new InvalidDataAccessApiUsageException(
@@ -343,147 +434,10 @@ class DefaultColumnTypeResolver implements ColumnTypeResolver {
}
}
enum UnknownUserDefinedType implements com.datastax.oss.driver.api.core.type.UserDefinedType {
INSTANCE;
UnknownUserDefinedType() {}
/*
* (non-Javadoc)
* @see com.datastax.oss.driver.api.core.type.UserDefinedType#getKeyspace()
*/
@Override
public CqlIdentifier getKeyspace() {
return null;
}
/*
* (non-Javadoc)
* @see com.datastax.oss.driver.api.core.type.UserDefinedType#getName()
*/
@Override
public CqlIdentifier getName() {
return CqlIdentifier.fromCql("unknown");
}
/*
* (non-Javadoc)
* @see com.datastax.oss.driver.api.core.type.UserDefinedType#isFrozen()
*/
@Override
public boolean isFrozen() {
return false;
}
/*
* (non-Javadoc)
* @see com.datastax.oss.driver.api.core.type.UserDefinedType#getFieldNames()
*/
@Override
public List<CqlIdentifier> getFieldNames() {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
/*
* (non-Javadoc)
* @see com.datastax.oss.driver.api.core.type.UserDefinedType#firstIndexOf(com.datastax.oss.driver.api.core.CqlIdentifier)
*/
@Override
public int firstIndexOf(CqlIdentifier id) {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
/*
* (non-Javadoc)
* @see com.datastax.oss.driver.api.core.type.UserDefinedType#firstIndexOf(java.lang.String)
*/
@Override
public int firstIndexOf(String name) {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
/*
* (non-Javadoc)
* @see com.datastax.oss.driver.api.core.type.UserDefinedType#getFieldTypes()
*/
@Override
public List<DataType> getFieldTypes() {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
/*
* (non-Javadoc)
* @see com.datastax.oss.driver.api.core.type.UserDefinedType#copy(boolean)
*/
@Override
public com.datastax.oss.driver.api.core.type.UserDefinedType copy(boolean newFrozen) {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
/*
* (non-Javadoc)
* @see com.datastax.oss.driver.api.core.type.UserDefinedType#newValue()
*/
@Override
public UdtValue newValue() {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
/*
* (non-Javadoc)
* @see com.datastax.oss.driver.api.core.type.UserDefinedType#newValue(java.lang.Object[])
*/
@Override
public UdtValue newValue(Object... fields) {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
/*
* (non-Javadoc)
* @see com.datastax.oss.driver.api.core.type.UserDefinedType#getAttachmentPoint()
*/
@Override
public AttachmentPoint getAttachmentPoint() {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
/*
* (non-Javadoc)
* @see com.datastax.oss.driver.api.core.detach.Detachable#isDetached()
*/
@Override
public boolean isDetached() {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
/*
* (non-Javadoc)
* @see com.datastax.oss.driver.api.core.detach.Detachable#attach(com.datastax.oss.driver.api.core.detach.AttachmentPoint)
*/
@Override
public void attach(AttachmentPoint attachmentPoint) {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
}
static class UnresolvableCassandraType extends DefaultCassandraColumnType {
public UnresolvableCassandraType(TypeInformation<?> type, ColumnType... parameters) {
super(type, null, parameters);
}
public UnresolvableCassandraType(Class<?> type, ColumnType... parameters) {
super(type, null, parameters);
super(type, Lazy.empty(), parameters);
}
/*
@@ -495,4 +449,5 @@ class DefaultColumnTypeResolver implements ColumnTypeResolver {
throw new MappingException(String.format("Cannot resolve DataType for %s", getType().getName()));
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2020 the original author or authors.
* Copyright 2020 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.core.mapping;
package org.springframework.data.cassandra.core.convert;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedParameterizedType;
@@ -27,6 +27,9 @@ import java.util.function.BiConsumer;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.data.cassandra.core.cql.keyspace.CreateIndexSpecification;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
import org.springframework.data.cassandra.core.mapping.Indexed;
import org.springframework.data.cassandra.core.mapping.SASI;
import org.springframework.data.cassandra.core.mapping.SASI.NonTokenizingAnalyzed;
import org.springframework.data.cassandra.core.mapping.SASI.Normalization;
import org.springframework.data.cassandra.core.mapping.SASI.StandardAnalyzed;

View File

@@ -43,7 +43,7 @@ import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
import org.springframework.data.cassandra.core.mapping.MapId;
import org.springframework.data.cassandra.core.mapping.MapIdentifiable;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.cassandra.core.mapping.UserTypeResolver;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PreferredConstructor;
@@ -91,6 +91,10 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
private final CassandraMappingContext mappingContext;
private CodecRegistry codecRegistry;
private UserTypeResolver userTypeResolver;
private @Nullable ClassLoader beanClassLoader;
private SpELContext spELContext;
@@ -107,8 +111,11 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
CassandraCustomConversions conversions = new CassandraCustomConversions(Collections.emptyList());
this.mappingContext = newDefaultMappingContext(conversions);
this.codecRegistry = mappingContext.getCodecRegistry();
this.spELContext = new SpELContext(RowReaderPropertyAccessor.INSTANCE);
this.cassandraTypeResolver = new DefaultColumnTypeResolver(mappingContext);
this.cassandraTypeResolver = new DefaultColumnTypeResolver(mappingContext,
userTypeName -> getUserTypeResolver().resolveType(userTypeName), this::getCodecRegistry,
this::getCustomConversions);
this.setCustomConversions(conversions);
}
@@ -124,8 +131,11 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
Assert.notNull(mappingContext, "CassandraMappingContext must not be null");
this.mappingContext = mappingContext;
this.codecRegistry = mappingContext.getCodecRegistry();
this.spELContext = new SpELContext(RowReaderPropertyAccessor.INSTANCE);
this.cassandraTypeResolver = new DefaultColumnTypeResolver(mappingContext);
this.cassandraTypeResolver = new DefaultColumnTypeResolver(mappingContext,
userTypeName -> getUserTypeResolver().resolveType(userTypeName), this::getCodecRegistry,
this::getCustomConversions);
this.setCustomConversions(mappingContext.getCustomConversions());
}
@@ -143,15 +153,6 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
return mappingContext;
}
@Override
public void setCustomConversions(CustomConversions conversions) {
super.setCustomConversions(conversions);
if (this.cassandraTypeResolver != null) {
this.cassandraTypeResolver.setCustomConversions(conversions);
}
}
/* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
@@ -169,11 +170,63 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
}
private TypeCodec<Object> getCodec(CassandraPersistentProperty property) {
return getCodecRegistry().codecFor(getMappingContext().getDataType(property));
return getCodecRegistry().codecFor(cassandraTypeResolver.resolve(property).getDataType());
}
private CodecRegistry getCodecRegistry() {
return getMappingContext().getCodecRegistry();
/**
* Sets the {@link CodecRegistry}.
*
* @param codecRegistry must not be {@literal null}.
* @since 3.0
*/
public void setCodecRegistry(CodecRegistry codecRegistry) {
Assert.notNull(codecRegistry, "CodecRegistry must not be null");
this.codecRegistry = codecRegistry;
}
/**
* Returns the configured {@link CodecRegistry}.
*
* @return the configured {@link CodecRegistry}.
* @since 3.0
*/
@Override
public CodecRegistry getCodecRegistry() {
if (this.codecRegistry == null) {
return mappingContext.getCodecRegistry();
}
return this.codecRegistry;
}
/**
* Sets the {@link UserTypeResolver}.
*
* @param userTypeResolver must not be {@literal null}.
*/
public void setUserTypeResolver(UserTypeResolver userTypeResolver) {
Assert.notNull(userTypeResolver, "UserTypeResolver must not be null");
this.userTypeResolver = userTypeResolver;
}
/**
* Returns the configured {@link UserTypeResolver}.
*
* @return the configured {@link UserTypeResolver}.
* @since 3.0
*/
public UserTypeResolver getUserTypeResolver() {
if (this.userTypeResolver == null) {
return this.mappingContext.getUserTypeResolver();
}
return userTypeResolver;
}
/* (non-Javadoc)

View File

@@ -0,0 +1,383 @@
/*
* Copyright 2020 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
*
* https://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.core.convert;
import static org.springframework.data.cassandra.core.cql.keyspace.CreateTableSpecification.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.springframework.data.cassandra.core.cql.keyspace.CreateIndexSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.CreateTableSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.CreateUserTypeSpecification;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
import org.springframework.data.cassandra.core.mapping.UserTypeResolver;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.data.UdtValue;
import com.datastax.oss.driver.api.core.detach.AttachmentPoint;
import com.datastax.oss.driver.api.core.type.DataType;
import com.datastax.oss.driver.api.core.type.UserDefinedType;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* Factory for Cassandra Schema objects such as user-defined types, tables and indexes.
*
* @author Mark Paluch
* @since 3.0
* @see CreateUserTypeSpecification
* @see CreateTableSpecification
* @see CreateIndexSpecification
* @see org.springframework.data.cassandra.core.mapping.CassandraMappingContext
*/
public class SchemaFactory {
private final MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> mappingContext;
private final ColumnTypeResolver typeResolver;
/**
* Creates a new {@link SchemaFactory} given {@link CassandraConverter}.
*
* @param converter must not be null.
*/
public SchemaFactory(CassandraConverter converter) {
Assert.notNull(converter, "CassandraConverter must not be null");
this.mappingContext = converter.getMappingContext();
this.typeResolver = new DefaultColumnTypeResolver(mappingContext, ShallowUserTypeResolver.INSTANCE,
converter::getCodecRegistry, converter::getCustomConversions);
}
/**
* Creates a new {@link SchemaFactory} given {@link MappingContext}, {@link CustomConversions} and
* {@link CodecRegistry}.
*
* @param mappingContext must not be null.
* @param customConversions must not be null.
* @param codecRegistry must not be null.
*/
public SchemaFactory(
MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> mappingContext,
CustomConversions customConversions, CodecRegistry codecRegistry) {
Assert.notNull(mappingContext, "MappingContext must not be null");
Assert.notNull(customConversions, "CustomConversions must not be null");
Assert.notNull(codecRegistry, "CodecRegistry must not be null");
this.mappingContext = mappingContext;
this.typeResolver = new DefaultColumnTypeResolver(mappingContext, ShallowUserTypeResolver.INSTANCE,
() -> codecRegistry, () -> customConversions);
}
/**
* Returns a {@link CreateTableSpecification} for the given entity, including all mapping information.
*
* @param entityType must not be {@literal null}.
* @return the {@link CreateTableSpecification} derived from {@code entityType}.
*/
public CreateTableSpecification getCreateTableSpecificationFor(Class<?> entityType) {
Assert.notNull(entityType, "Entity type must not be null");
return getCreateTableSpecificationFor(mappingContext.getRequiredPersistentEntity(entityType));
}
/**
* Returns a {@link CreateTableSpecification} for the given entity, including all mapping information.
*
* @param entity must not be {@literal null}.
* @return the {@link CreateTableSpecification} derived from {@link CassandraPersistentEntity}.
*/
public CreateTableSpecification getCreateTableSpecificationFor(CassandraPersistentEntity<?> entity) {
Assert.notNull(entity, "CassandraPersistentEntity must not be null");
return getCreateTableSpecificationFor(entity, entity.getTableName());
}
/**
* Returns a {@link CreateTableSpecification} for the given entity using {@link CqlIdentifier table name}, including
* all mapping information.
*
* @param entity must not be {@literal null}.
* @param tableName must not be {@literal null}.
* @return
* @since 2.2
*/
public CreateTableSpecification getCreateTableSpecificationFor(CassandraPersistentEntity<?> entity,
CqlIdentifier tableName) {
Assert.notNull(tableName, "Table name must not be null");
Assert.notNull(entity, "CassandraPersistentEntity must not be null");
CreateTableSpecification specification = createTable(tableName);
for (CassandraPersistentProperty property : entity) {
if (property.isCompositePrimaryKey()) {
CassandraPersistentEntity<?> primaryKeyEntity = mappingContext
.getRequiredPersistentEntity(property.getRawType());
for (CassandraPersistentProperty primaryKeyProperty : primaryKeyEntity) {
DataType dataType = getDataType(primaryKeyProperty);
if (primaryKeyProperty.isPartitionKeyColumn()) {
specification.partitionKeyColumn(primaryKeyProperty.getRequiredColumnName(), dataType);
} else { // cluster column
specification.clusteredKeyColumn(primaryKeyProperty.getRequiredColumnName(), dataType,
primaryKeyProperty.getPrimaryKeyOrdering());
}
}
} else {
DataType type = UserTypeUtil.potentiallyFreeze(getDataType(property));
if (property.isIdProperty() || property.isPartitionKeyColumn()) {
specification.partitionKeyColumn(property.getRequiredColumnName(), type);
} else if (property.isClusterKeyColumn()) {
specification.clusteredKeyColumn(property.getRequiredColumnName(), type, property.getPrimaryKeyOrdering());
} else {
specification.column(property.getRequiredColumnName(), type);
}
}
}
if (specification.getPartitionKeyColumns().isEmpty()) {
throw new MappingException(String.format("No partition key columns found in entity [%s]", entity.getType()));
}
return specification;
}
private DataType getDataType(CassandraPersistentProperty property) {
try {
return typeResolver.resolve(property).getDataType();
} catch (MappingException e) {
throw new MappingException(String.format(
"Cannot resolve DataType for type [%s] for property [%s] in entity [%s]; Consider registering a Converter or annotating the property with @CassandraType.",
property.getType(), property.getName(), property.getOwner().getName()), e);
}
}
/**
* Returns {@link CreateIndexSpecification index specifications} derived from {@link CassandraPersistentEntity}.
*
* @param entityType must not be {@literal null}.
* @return the {@link CreateTableSpecification} derived from {@code entityType}.
*/
public List<CreateIndexSpecification> getCreateIndexSpecificationsFor(Class<?> entityType) {
Assert.notNull(entityType, "Entity type must not be null");
return getCreateIndexSpecificationsFor(mappingContext.getRequiredPersistentEntity(entityType));
}
/**
* Returns {@link CreateIndexSpecification index specifications} derived from {@link CassandraPersistentEntity}.
*
* @param entity must not be {@literal null}.
* @return
* @since 2.0
*/
public List<CreateIndexSpecification> getCreateIndexSpecificationsFor(CassandraPersistentEntity<?> entity) {
Assert.notNull(entity, "CassandraPersistentEntity must not be null");
return getCreateIndexSpecificationsFor(entity, entity.getTableName());
}
/**
* Returns {@link CreateIndexSpecification index specifications} derived from {@link CassandraPersistentEntity} using
* {@link CqlIdentifier table name}.
*
* @param entity must not be {@literal null}.
* @param tableName must not be {@literal null}.
* @return
* @since 2.0
*/
public List<CreateIndexSpecification> getCreateIndexSpecificationsFor(CassandraPersistentEntity<?> entity,
CqlIdentifier tableName) {
Assert.notNull(entity, "CassandraPersistentEntity must not be null");
Assert.notNull(tableName, "Table name must not be null");
List<CreateIndexSpecification> indexes = new ArrayList<>();
for (CassandraPersistentProperty property : entity) {
if (property.isCompositePrimaryKey()) {
indexes.addAll(getCreateIndexSpecificationsFor(mappingContext.getRequiredPersistentEntity(property)));
} else {
indexes.addAll(IndexSpecificationFactory.createIndexSpecifications(property));
}
}
indexes.forEach(it -> it.tableName(entity.getTableName()));
return indexes;
}
/**
* Returns a {@link CreateUserTypeSpecification} for the given entity, including all mapping information.
*
* @param entity must not be {@literal null}.
*/
public CreateUserTypeSpecification getCreateUserTypeSpecificationFor(CassandraPersistentEntity<?> entity) {
Assert.notNull(entity, "CassandraPersistentEntity must not be null");
CreateUserTypeSpecification specification = CreateUserTypeSpecification.createType(entity.getTableName());
for (CassandraPersistentProperty property : entity) {
// Use frozen literal to not resolve types from Cassandra; At this stage, they might be not created yet.
specification.field(property.getRequiredColumnName(), UserTypeUtil.potentiallyFreeze(getDataType(property)));
}
if (specification.getFields().isEmpty()) {
throw new MappingException(String.format("No fields in user type [%s]", entity.getType()));
}
return specification;
}
enum ShallowUserTypeResolver implements UserTypeResolver {
INSTANCE;
@Override
public UserDefinedType resolveType(CqlIdentifier typeName) {
return new ShallowUserDefinedType(typeName, false);
}
}
static class ShallowUserDefinedType implements com.datastax.oss.driver.api.core.type.UserDefinedType {
private final CqlIdentifier name;
private final boolean frozen;
public ShallowUserDefinedType(String name, boolean frozen) {
this(CqlIdentifier.fromInternal(name), frozen);
}
public ShallowUserDefinedType(CqlIdentifier name, boolean frozen) {
this.name = name;
this.frozen = frozen;
}
@Override
public CqlIdentifier getKeyspace() {
return null;
}
@Override
public CqlIdentifier getName() {
return name;
}
@Override
public boolean isFrozen() {
return frozen;
}
@Override
public List<CqlIdentifier> getFieldNames() {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
@Override
public int firstIndexOf(CqlIdentifier id) {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
@Override
public int firstIndexOf(String name) {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
@Override
public List<DataType> getFieldTypes() {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
@Override
public com.datastax.oss.driver.api.core.type.UserDefinedType copy(boolean newFrozen) {
return new ShallowUserDefinedType(this.name, newFrozen);
}
@Override
public UdtValue newValue() {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
@Override
public UdtValue newValue(@edu.umd.cs.findbugs.annotations.NonNull Object... fields) {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
@Override
public AttachmentPoint getAttachmentPoint() {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
@Override
public boolean isDetached() {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
@Override
public void attach(@edu.umd.cs.findbugs.annotations.NonNull AttachmentPoint attachmentPoint) {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof com.datastax.oss.driver.api.core.type.UserDefinedType))
return false;
com.datastax.oss.driver.api.core.type.UserDefinedType that = (com.datastax.oss.driver.api.core.type.UserDefinedType) o;
return isFrozen() == that.isFrozen() && Objects.equals(getName(), that.getName());
}
@Override
public int hashCode() {
return Objects.hash(name, frozen);
}
@Override
public String toString() {
return "UDT(" + name.asCql(true) + ")";
}
}
}

View File

@@ -169,7 +169,8 @@ public class UpdateMapper extends QueryMapper {
if (collection.isEmpty()) {
int protocolCode = field.getProperty().map(property -> getMappingContext().getDataType(property))
int protocolCode = field.getProperty()
.map(property -> getConverter().getColumnTypeResolver().resolve(property).getDataType())
.map(DataType::getProtocolCode).orElse(ProtocolConstants.DataType.LIST);
if (protocolCode == ProtocolConstants.DataType.SET) {
@@ -203,7 +204,7 @@ public class UpdateMapper extends QueryMapper {
if (field.getProperty().isPresent()) {
DataType dataType = getMappingContext().getDataType(field.getProperty().get());
DataType dataType = getConverter().getColumnTypeResolver().resolve(field.getProperty().get()).getDataType();
if (dataType instanceof SetType && !(mappedValue instanceof Set)) {
Collection<Object> collection = new HashSet<>();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2020 the original author or authors.
* Copyright 2020 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.core.mapping;
package org.springframework.data.cassandra.core.convert;
import org.springframework.util.Assert;

View File

@@ -35,7 +35,6 @@ import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.type.TupleType;
/**
* Cassandra specific {@link BasicPersistentEntity} implementation that adds Cassandra specific metadata.
@@ -231,15 +230,6 @@ public class BasicCassandraPersistentEntity<T> extends BasicPersistentEntity<T,
return false;
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity#getTupleType()
*/
@Override
@Nullable
public TupleType getTupleType() {
return null;
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity#isUserDefinedType()
*/
@@ -248,12 +238,4 @@ public class BasicCassandraPersistentEntity<T> extends BasicPersistentEntity<T,
return false;
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity#getUserType()
*/
@Override
@Nullable
public com.datastax.oss.driver.api.core.type.UserDefinedType getUserType() {
return null;
}
}

View File

@@ -15,29 +15,23 @@
*/
package org.springframework.data.cassandra.core.mapping;
import static org.springframework.data.cassandra.core.mapping.CassandraType.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedParameterizedType;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.expression.BeanFactoryAccessor;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.cassandra.core.cql.Ordering;
import org.springframework.data.cassandra.core.cql.PrimaryKeyType;
import org.springframework.data.cassandra.util.SpelUtils;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.SimpleTypeHolder;
@@ -50,9 +44,6 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.type.DataType;
import com.datastax.oss.driver.api.core.type.DataTypes;
import com.datastax.oss.driver.api.core.type.UserDefinedType;
/**
* Cassandra specific {@link org.springframework.data.mapping.model.AnnotationBasedPersistentProperty} implementation.
@@ -73,8 +64,6 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
private @Nullable StandardEvaluationContext spelContext;
private final @Nullable UserTypeResolver userTypeResolver;
/**
* Create a new {@link BasicCassandraPersistentProperty}.
*
@@ -85,23 +74,7 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
public BasicCassandraPersistentProperty(Property property, CassandraPersistentEntity<?> owner,
SimpleTypeHolder simpleTypeHolder) {
this(property, owner, simpleTypeHolder, null);
}
/**
* Create a new {@link BasicCassandraPersistentProperty}.
*
* @param property the actual {@link Property} in the domain entity corresponding to this persistent entity.
* @param owner the containing object or {@link CassandraPersistentEntity} of this persistent property.
* @param simpleTypeHolder mapping of Java [simple|wrapper] types to Cassandra data types.
* @param userTypeResolver resolver for user-defined types.
*/
public BasicCassandraPersistentProperty(Property property, CassandraPersistentEntity<?> owner,
SimpleTypeHolder simpleTypeHolder, @Nullable UserTypeResolver userTypeResolver) {
super(property, owner, simpleTypeHolder);
this.userTypeResolver = userTypeResolver;
}
/* (non-Javadoc)
@@ -162,128 +135,6 @@ public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentP
return annotation != null ? annotation.ordering() : null;
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty#getDataType()
*/
@Override
public DataType getDataType() {
DataType dataType = findDataType();
if (dataType == null) {
throw new InvalidDataAccessApiUsageException(String.format(
"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;
}
@Nullable
private DataType findDataType() {
CassandraType cassandraType = findAnnotation(CassandraType.class);
if (cassandraType != null) {
return getDataTypeFor(cassandraType);
}
if (isMap()) {
List<TypeInformation<?>> args = getTypeInformation().getTypeArguments();
assertTypeArguments(args.size(), 2);
return DataTypes.mapOf(getDataTypeFor(args.get(0).getType()), getDataTypeFor(args.get(1).getType()));
}
if (isCollectionLike()) {
List<TypeInformation<?>> args = getTypeInformation().getTypeArguments();
assertTypeArguments(args.size(), 1);
if (Set.class.isAssignableFrom(getType())) {
return DataTypes.setOf(getDataTypeFor(args.get(0).getType()));
}
if (List.class.isAssignableFrom(getType())) {
return DataTypes.listOf(getDataTypeFor(args.get(0).getType()));
}
}
return CassandraSimpleTypeHolder.getDataTypeFor(getType());
}
private DataType getDataTypeFor(CassandraType annotation) {
Name type = annotation.type();
switch (type) {
case MAP:
assertTypeArguments(annotation.typeArguments().length, 2);
return DataTypes.mapOf(CassandraSimpleTypeHolder.getDataTypeFor(annotation.typeArguments()[0]),
CassandraSimpleTypeHolder.getDataTypeFor(annotation.typeArguments()[1]));
case LIST:
assertTypeArguments(annotation.typeArguments().length, 1);
return annotation.typeArguments()[0] == Name.UDT
? DataTypes.listOf(getUserType(annotation))
: DataTypes.listOf(CassandraSimpleTypeHolder.getDataTypeFor(annotation.typeArguments()[0]));
case SET:
assertTypeArguments(annotation.typeArguments().length, 1);
return annotation.typeArguments()[0] == Name.UDT
? DataTypes.setOf(getUserType(annotation))
: DataTypes.setOf(CassandraSimpleTypeHolder.getDataTypeFor(annotation.typeArguments()[0]));
case UDT:
return getUserType(annotation);
default:
return CassandraSimpleTypeHolder.getDataTypeFor(type);
}
}
private DataType getUserType(CassandraType annotation) {
if (!StringUtils.hasText(annotation.userTypeName())) {
throw new InvalidDataAccessApiUsageException(
String.format("Expected user type name in property ['%s'] of type ['%s'] in entity [%s]", getName(),
getType(), getOwner().getName()));
}
Assert.state(this.userTypeResolver != null, "UserTypeResolver is null");
CqlIdentifier identifier = CqlIdentifier.fromCql(annotation.userTypeName());
UserDefinedType userType = this.userTypeResolver.resolveType(identifier);
if (userType == null) {
throw new MappingException(String.format("User type [%s] not found", identifier));
}
return userType;
}
private 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 property [%1$s] of type ['%2$s'] in entity [%3$s]",
getName(), getType(), getOwner().getName()));
}
return dataType;
}
private void assertTypeArguments(int args, int expected) {
if (args != expected) {
throw new InvalidDataAccessApiUsageException(String.format(
"Expected [%1$s] type arguments for property ['%2$s'] of type ['%3$s'] in entity [%4$s]; actual was [%5$d]",
expected, getName(), getType(), getOwner().getName(), args));
}
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty#isCompositePrimaryKey()
*/

View File

@@ -16,17 +16,9 @@
package org.springframework.data.cassandra.core.mapping;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.type.DataType;
import com.datastax.oss.driver.api.core.type.TupleType;
/**
* Cassandra Tuple-specific {@link org.springframework.data.mapping.PersistentEntity} for a mapped tuples. Mapped tuples
@@ -39,28 +31,15 @@ import com.datastax.oss.driver.api.core.type.TupleType;
*/
public class BasicCassandraPersistentTupleEntity<T> extends BasicCassandraPersistentEntity<T> {
private final Lazy<TupleType> tupleType;
/**
* Creates a new {@link BasicCassandraPersistentTupleEntity} given {@link TypeInformation} and
* {@link TupleTypeFactory}.
* Creates a new {@link BasicCassandraPersistentTupleEntity} given {@link TypeInformation}.
*
* @param information must not be {@literal null}.
* @param tupleTypeFactory must not be {@literal null}.
*/
public BasicCassandraPersistentTupleEntity(TypeInformation<T> information, TupleTypeFactory tupleTypeFactory) {
public BasicCassandraPersistentTupleEntity(TypeInformation<T> information) {
super(information, CassandraPersistentTupleMetadataVerifier.INSTANCE, TuplePropertyComparator.INSTANCE);
Assert.notNull(tupleTypeFactory, "TupleTypeFactory must not be null");
this.tupleType = Lazy.of(() -> tupleTypeFactory.create(getTupleFieldDataTypes()));
}
private List<DataType> getTupleFieldDataTypes() {
return StreamSupport.stream(spliterator(), false).sorted(TuplePropertyComparator.INSTANCE)
.map(CassandraPersistentProperty::getDataType).collect(Collectors.toList());
}
/* (non-Javadoc)
@@ -82,14 +61,6 @@ public class BasicCassandraPersistentTupleEntity<T> extends BasicCassandraPersis
return true;
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.mapping.BasicCassandraPersistentEntity#getTupleType()
*/
@Override
public TupleType getTupleType() {
return this.tupleType.get();
}
/**
* {@link CassandraPersistentProperty} comparator using to sort properties by their
* {@link CassandraPersistentProperty#getRequiredOrdinal()}.

View File

@@ -44,21 +44,7 @@ public class BasicCassandraPersistentTupleProperty extends BasicCassandraPersist
public BasicCassandraPersistentTupleProperty(Property property, CassandraPersistentEntity<?> owner,
SimpleTypeHolder simpleTypeHolder) {
this(property, owner, simpleTypeHolder, null);
}
/**
* Create a new {@link BasicCassandraPersistentTupleProperty}.
*
* @param property the actual {@link Property} in the domain entity corresponding to this persistent entity.
* @param owner the containing object or {@link CassandraPersistentEntity} of this persistent property.
* @param simpleTypeHolder mapping of Java [simple|wrapper] types to Cassandra data types.
* @param userTypeResolver resolver for user-defined types.
*/
public BasicCassandraPersistentTupleProperty(Property property, CassandraPersistentEntity<?> owner,
SimpleTypeHolder simpleTypeHolder, @Nullable UserTypeResolver userTypeResolver) {
super(property, owner, simpleTypeHolder, userTypeResolver);
super(property, owner, simpleTypeHolder);
this.ordinal = findOrdinal();
}

View File

@@ -15,11 +15,14 @@
*/
package org.springframework.data.cassandra.core.mapping;
import static org.springframework.data.cassandra.core.cql.keyspace.CreateTableSpecification.*;
import static org.springframework.data.cassandra.core.mapping.CassandraType.*;
import java.util.*;
import java.util.function.Supplier;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
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;
@@ -27,32 +30,21 @@ import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.cassandra.core.convert.CassandraCustomConversions;
import org.springframework.data.cassandra.core.cql.keyspace.CreateIndexSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.CreateTableSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.CreateUserTypeSpecification;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.context.AbstractMappingContext;
import org.springframework.data.mapping.context.MappingContext;
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;
import org.springframework.util.StringUtils;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.data.TupleValue;
import com.datastax.oss.driver.api.core.data.UdtValue;
import com.datastax.oss.driver.api.core.detach.AttachmentPoint;
import com.datastax.oss.driver.api.core.type.DataType;
import com.datastax.oss.driver.api.core.type.DataTypes;
import com.datastax.oss.driver.api.core.type.TupleType;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
@@ -76,15 +68,13 @@ public class CassandraMappingContext
private @Nullable ClassLoader beanClassLoader;
private CustomConversions customConversions = new CassandraCustomConversions(Collections.emptyList());
private @Deprecated CustomConversions customConversions = new CassandraCustomConversions(Collections.emptyList());
private Mapping mapping = new Mapping();
private TupleTypeFactory tupleTypeFactory = SimpleTupleTypeFactory.DEFAULT;
private @Deprecated @Nullable UserTypeResolver userTypeResolver;
private @Nullable UserTypeResolver userTypeResolver;
private CodecRegistry codecRegistry = CodecRegistry.DEFAULT;
private @Deprecated CodecRegistry codecRegistry = CodecRegistry.DEFAULT;
// caches
private final Map<CqlIdentifier, Set<CassandraPersistentEntity<?>>> entitySetsByTableName = new HashMap<>();
@@ -105,7 +95,10 @@ public class CassandraMappingContext
* @param userTypeResolver must not be {@literal null}.
* @param tupleTypeFactory must not be {@literal null}.
* @since 2.1
* @deprecated since 3.0, {@link UserTypeResolver} and {@link TupleTypeFactory} no longer required here as high-level
* type resolution went into {@link org.springframework.data.cassandra.core.convert.CassandraConverter}.
*/
@Deprecated
public CassandraMappingContext(UserTypeResolver userTypeResolver, TupleTypeFactory tupleTypeFactory) {
setUserTypeResolver(userTypeResolver);
@@ -192,7 +185,10 @@ public class CassandraMappingContext
*
* @param customConversions must not be {@literal null}.
* @since 1.5
* @deprecated since 3.0. Use custom conversion through
* {@link org.springframework.data.cassandra.core.convert.MappingCassandraConverter}.
*/
@Deprecated
public void setCustomConversions(CustomConversions customConversions) {
Assert.notNull(customConversions, "CustomConversions must not be null");
@@ -200,6 +196,12 @@ public class CassandraMappingContext
this.customConversions = customConversions;
}
/**
* @return
* @deprecated since 3.0. Use custom conversion through
* {@link org.springframework.data.cassandra.core.convert.MappingCassandraConverter}.
*/
@Deprecated
public CustomConversions getCustomConversions() {
return customConversions;
}
@@ -239,7 +241,10 @@ public class CassandraMappingContext
*
* @param codecRegistry must not be {@literal null}.
* @since 2.2
* @deprecated since 3.0. Set {@link CodecRegistry} directly on
* {@link org.springframework.data.cassandra.core.convert.CassandraConverter}.
*/
@Deprecated
public void setCodecRegistry(CodecRegistry codecRegistry) {
Assert.notNull(codecRegistry, "CodecRegistry must not be null");
@@ -247,7 +252,12 @@ public class CassandraMappingContext
this.codecRegistry = codecRegistry;
}
@NonNull
/**
* @return
* @deprecated since 3.0. Retrieve {@link CodecRegistry} directly from
* {@link org.springframework.data.cassandra.core.convert.CassandraConverter}.
*/
@Deprecated
public CodecRegistry getCodecRegistry() {
return this.codecRegistry;
}
@@ -257,25 +267,21 @@ public class CassandraMappingContext
*
* @param tupleTypeFactory must not be {@literal null}.
* @since 2.1
* @deprecated since 3.0. Tuple type creation uses
* {@link com.datastax.oss.driver.api.core.type.DataTypes#tupleOf(DataType...)}
*/
public void setTupleTypeFactory(TupleTypeFactory tupleTypeFactory) {
Assert.notNull(tupleTypeFactory, "TupleTypeFactory must not be null");
this.tupleTypeFactory = tupleTypeFactory;
}
@NonNull
protected TupleTypeFactory getTupleTypeFactory() {
return this.tupleTypeFactory;
}
@Deprecated
public void setTupleTypeFactory(TupleTypeFactory tupleTypeFactory) {}
/**
* Sets the {@link UserTypeResolver}.
*
* @param userTypeResolver must not be {@literal null}.
* @since 1.5
* @deprecated since 3.0. Set {@link UserTypeResolver} directly on
* {@link org.springframework.data.cassandra.core.convert.CassandraConverter}.
*/
@Deprecated
public void setUserTypeResolver(UserTypeResolver userTypeResolver) {
Assert.notNull(userTypeResolver, "UserTypeResolver must not be null");
@@ -283,7 +289,13 @@ public class CassandraMappingContext
this.userTypeResolver = userTypeResolver;
}
/**
* @return
* @deprecated since 3.0. Retrieve {@link UserTypeResolver} directly from
* {@link org.springframework.data.cassandra.core.convert.CassandraConverter}.
*/
@Nullable
@Deprecated
public UserTypeResolver getUserTypeResolver() {
return this.userTypeResolver;
}
@@ -350,8 +362,8 @@ public class CassandraMappingContext
protected <T> BasicCassandraPersistentEntity<T> createPersistentEntity(TypeInformation<T> typeInformation) {
BasicCassandraPersistentEntity<T> entity = isUserDefinedType(typeInformation)
? new CassandraUserTypePersistentEntity<>(typeInformation, getVerifier(), resolveUserTypeResolver())
: isTuple(typeInformation) ? new BasicCassandraPersistentTupleEntity<>(typeInformation, getTupleTypeFactory())
? new CassandraUserTypePersistentEntity<>(typeInformation, getVerifier())
: isTuple(typeInformation) ? new BasicCassandraPersistentTupleEntity<>(typeInformation)
: new BasicCassandraPersistentEntity<>(typeInformation, getVerifier());
Optional.ofNullable(this.applicationContext).ifPresent(entity::setApplicationContext);
@@ -367,16 +379,6 @@ public class CassandraMappingContext
return AnnotatedElementUtils.hasAnnotation(typeInformation.getType(), UserDefinedType.class);
}
@NonNull
private UserTypeResolver resolveUserTypeResolver() {
UserTypeResolver resolvedUserTypeResolver = getUserTypeResolver();
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)
*/
@@ -385,8 +387,8 @@ public class CassandraMappingContext
BasicCassandraPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
BasicCassandraPersistentProperty persistentProperty = owner.isTupleType()
? new BasicCassandraPersistentTupleProperty(property, owner, simpleTypeHolder, getUserTypeResolver())
: new BasicCassandraPersistentProperty(property, owner, simpleTypeHolder, getUserTypeResolver());
? new BasicCassandraPersistentTupleProperty(property, owner, simpleTypeHolder)
: new BasicCassandraPersistentProperty(property, owner, simpleTypeHolder);
Optional.ofNullable(this.applicationContext).ifPresent(persistentProperty::setApplicationContext);
@@ -434,543 +436,4 @@ public class CassandraMappingContext
.anyMatch(identifier::equals);
}
/**
* Returns a {@link CreateTableSpecification} for the given entity, including all mapping information.
*
* @param entity must not be {@literal null}.
* @return
*/
public CreateTableSpecification getCreateTableSpecificationFor(CassandraPersistentEntity<?> entity) {
Assert.notNull(entity, "CassandraPersistentEntity must not be null");
return getCreateTableSpecificationFor(entity.getTableName(), entity);
}
/**
* Returns a {@link CreateTableSpecification} for the given entity using {@code tableName}, including all mapping
* information.
*
* @param tableName must not be {@literal null}.
* @param entity must not be {@literal null}.
* @return
* @since 2.2
*/
public CreateTableSpecification getCreateTableSpecificationFor(
com.datastax.oss.driver.api.core.CqlIdentifier tableName, CassandraPersistentEntity<?> entity) {
Assert.notNull(tableName, "Table name must not be null");
Assert.notNull(entity, "CassandraPersistentEntity must not be null");
CreateTableSpecification specification = createTable(tableName);
for (CassandraPersistentProperty property : entity) {
if (property.isCompositePrimaryKey()) {
CassandraPersistentEntity<?> primaryKeyEntity = getRequiredPersistentEntity(property.getRawType());
for (CassandraPersistentProperty primaryKeyProperty : primaryKeyEntity) {
DataType dataType = getDataTypeWithUserTypeFactory(primaryKeyProperty, DataTypeProvider.ShallowType);
if (primaryKeyProperty.isPartitionKeyColumn()) {
specification.partitionKeyColumn(primaryKeyProperty.getRequiredColumnName(), dataType);
} else { // cluster column
specification.clusteredKeyColumn(primaryKeyProperty.getRequiredColumnName(), dataType,
primaryKeyProperty.getPrimaryKeyOrdering());
}
}
} else {
DataType type = UserTypeUtil
.potentiallyFreeze(getDataTypeWithUserTypeFactory(property, DataTypeProvider.ShallowType));
if (property.isIdProperty() || property.isPartitionKeyColumn()) {
specification.partitionKeyColumn(property.getRequiredColumnName(), type);
} else if (property.isClusterKeyColumn()) {
specification.clusteredKeyColumn(property.getRequiredColumnName(), type, property.getPrimaryKeyOrdering());
} else {
specification.column(property.getRequiredColumnName(), type);
}
}
}
if (specification.getPartitionKeyColumns().isEmpty()) {
throw new MappingException(String.format("No partition key columns found in entity [%s]", entity.getType()));
}
return specification;
}
/**
* @param entity must not be {@literal null}.
* @return
* @since 2.0
*/
public List<CreateIndexSpecification> getCreateIndexSpecificationsFor(CassandraPersistentEntity<?> entity) {
Assert.notNull(entity, "CassandraPersistentEntity must not be null");
List<CreateIndexSpecification> indexes = new ArrayList<>();
for (CassandraPersistentProperty property : entity) {
if (property.isCompositePrimaryKey()) {
indexes.addAll(getCreateIndexSpecificationsFor(getRequiredPersistentEntity(property)));
} else {
indexes.addAll(IndexSpecificationFactory.createIndexSpecifications(property));
}
}
indexes.forEach(it -> it.tableName(entity.getTableName()));
return indexes;
}
/**
* Returns a {@link CreateUserTypeSpecification} for the given entity, including all mapping information.
*
* @param entity must not be {@literal null}.
*/
public CreateUserTypeSpecification getCreateUserTypeSpecificationFor(CassandraPersistentEntity<?> entity) {
Assert.notNull(entity, "CassandraPersistentEntity must not be null");
CreateUserTypeSpecification specification = CreateUserTypeSpecification.createType(entity.getTableName());
for (CassandraPersistentProperty property : entity) {
// Use frozen literal to not resolve types from Cassandra; At this stage, they might be not created yet.
specification.field(property.getRequiredColumnName(),
getDataTypeWithUserTypeFactory(property, DataTypeProvider.FrozenLiteral));
}
if (specification.getFields().isEmpty()) {
throw new MappingException(String.format("No fields in user type [%s]", entity.getType()));
}
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 CassandraSimpleTypeHolder.getDataTypeFor(this.customConversions.getCustomWriteTarget(type).orElse(type));
}
public TupleType getTupleType(CassandraPersistentEntity<?> persistentEntity) {
Assert.notNull(persistentEntity, "CassandraPersistentEntity must not be null");
Assert.isTrue(persistentEntity.isTupleType(), "CassandraPersistentEntity is not a mapped tuple type");
return getTupleType(DataTypeProvider.EntityUserType, persistentEntity);
}
/**
* Retrieve the data type of the property. Cassandra {@link DataType types} are determined using simple types and
* configured {@link org.springframework.data.convert.CustomConversions}.
*
* @param property 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(CassandraPersistentProperty property) {
return getDataTypeWithUserTypeFactory(property, DataTypeProvider.EntityUserType);
}
private DataType getDataTypeWithUserTypeFactory(CassandraPersistentProperty property,
DataTypeProvider dataTypeProvider) {
if (property.isAnnotationPresent(CassandraType.class)) {
CassandraType annotation = property.getRequiredAnnotation(CassandraType.class);
if (annotation.type() == Name.TUPLE) {
DataType[] dataTypes = Arrays.stream(annotation.typeArguments()).map(CassandraSimpleTypeHolder::getDataTypeFor)
.toArray(DataType[]::new);
return getTupleTypeFactory().create(dataTypes);
}
if (annotation.type() == Name.UDT) {
com.datastax.oss.driver.api.core.CqlIdentifier userTypeName = com.datastax.oss.driver.api.core.CqlIdentifier
.fromCql(annotation.userTypeName());
DataType userType = dataTypeProvider.getUserType(userTypeName, resolveUserTypeResolver());
if (userType == null) {
throw new MappingException(String.format("User type [%s] not found", userTypeName));
}
DataType dataType = getUserDataType(property.getTypeInformation(), userType);
if (dataType != null) {
return dataType;
}
}
return property.getDataType();
}
if (TupleValue.class.isAssignableFrom(property.getType())) {
throw new MappingException(String.format(
"Unsupported raw TupleType to DataType for property [%s] in entity [%s]; Consider adding @CassandraType.",
property.getName(), property.getOwner().getName()));
}
if (UdtValue.class.isAssignableFrom(property.getType())) {
throw new MappingException(String.format(
"Unsupported raw UdtValue to DataType for property [%s] in entity [%s]; Consider adding @CassandraType.",
property.getName(), property.getOwner().getName()));
}
try {
DataType dataType = getDataTypeWithUserTypeFactory(property.getTypeInformation(), dataTypeProvider,
property::getDataType);
if (dataType == null) {
throw new MappingException(
String.format("Cannot resolve DataType for property [%s] in entity [%s]; Consider adding @CassandraType.",
property.getName(), property.getOwner().getName()));
}
return dataType;
} catch (InvalidDataAccessApiUsageException e) {
throw new MappingException(String.format("%s. Consider adding @CassandraType.", e.getMessage()), e);
}
}
@Nullable
private DataType getDataTypeWithUserTypeFactory(TypeInformation<?> typeInformation, DataTypeProvider dataTypeProvider,
Supplier<DataType> fallback) {
Optional<DataType> customWriteTarget = this.customConversions.getCustomWriteTarget(typeInformation.getType())
.map(it -> {
typeInformation.getType();
return CassandraSimpleTypeHolder.getDataTypeFor(it);
});
DataType dataType = customWriteTarget.orElseGet(() -> {
Class<?> propertyType = typeInformation.getRequiredActualType().getType();
return this.customConversions.getCustomWriteTarget(propertyType).filter(it -> !typeInformation.isMap())
.map(it -> {
if (typeInformation.isCollectionLike()) {
if (List.class.isAssignableFrom(typeInformation.getType())) {
return DataTypes.listOf(CassandraSimpleTypeHolder.getDataTypeFor(it));
}
if (Set.class.isAssignableFrom(typeInformation.getType())) {
return DataTypes.setOf(CassandraSimpleTypeHolder.getDataTypeFor(it));
}
}
return CassandraSimpleTypeHolder.getDataTypeFor(it);
}).orElse(null);
});
if (dataType != null) {
return dataType;
}
if (typeInformation.isCollectionLike()) {
TypeInformation<?> componentType = typeInformation.getRequiredActualType();
BasicCassandraPersistentEntity<?> persistentEntity = getPersistentEntity(componentType);
TypeInformation<?> typeToUse = persistentEntity != null ? persistentEntity.getTypeInformation() : componentType;
if (List.class.isAssignableFrom(typeInformation.getType())) {
return DataTypes.listOf(getDataTypeWithUserTypeFactory(typeToUse, dataTypeProvider, fallback));
}
if (Set.class.isAssignableFrom(typeInformation.getType())) {
return DataTypes.setOf(getDataTypeWithUserTypeFactory(typeToUse, dataTypeProvider, fallback));
}
throw new IllegalArgumentException("Unsupported collection type: " + typeInformation);
}
if (typeInformation.isMap()) {
return getMapDataType(typeInformation, dataTypeProvider);
}
BasicCassandraPersistentEntity<?> persistentEntity = getPersistentEntity(typeInformation);
if (persistentEntity != null) {
if (persistentEntity.isUserDefinedType()) {
DataType udtType = dataTypeProvider.getDataType(persistentEntity);
if (udtType != null) {
return udtType;
}
} else if (persistentEntity.isTupleType()) {
return getTupleType(dataTypeProvider, persistentEntity);
}
return dataTypeProvider.getDataType(persistentEntity);
}
DataType determinedType = CassandraSimpleTypeHolder.getDataTypeFor(typeInformation.getType());
if (determinedType != null) {
return determinedType;
}
return fallback.get();
}
private TupleType getTupleType(DataTypeProvider dataTypeProvider, CassandraPersistentEntity<?> persistentEntity) {
List<DataType> types = new ArrayList<>();
for (CassandraPersistentProperty persistentProperty : persistentEntity) {
types.add(getDataTypeWithUserTypeFactory(persistentProperty, dataTypeProvider));
}
return getTupleTypeFactory().create(types);
}
@SuppressWarnings("all")
private DataType getMapDataType(TypeInformation<?> typeInformation, DataTypeProvider dataTypeProvider) {
TypeInformation<?> keyTypeInformation = typeInformation.getComponentType();
TypeInformation<?> valueTypeInformation = typeInformation.getMapValueType();
DataType keyType = getDataTypeWithUserTypeFactory(keyTypeInformation, dataTypeProvider, () -> {
DataType type = CassandraSimpleTypeHolder.getDataTypeFor(keyTypeInformation.getType());
if (type != null) {
return type;
}
throw new MappingException(String.format("Cannot resolve key type for [%s]", typeInformation));
});
DataType valueType = getDataTypeWithUserTypeFactory(valueTypeInformation, dataTypeProvider, () -> {
DataType type = CassandraSimpleTypeHolder.getDataTypeFor(valueTypeInformation.getType());
if (type != null) {
return type;
}
throw new MappingException("Cannot resolve value type for " + typeInformation + ".");
});
return DataTypes.mapOf(keyType, valueType);
}
@Nullable
private DataType getUserDataType(TypeInformation<?> property, @Nullable DataType elementType) {
if (property.isCollectionLike()) {
if (List.class.isAssignableFrom(property.getType())) {
return DataTypes.listOf(elementType);
}
if (Set.class.isAssignableFrom(property.getType())) {
return DataTypes.setOf(elementType);
}
}
return !(property.isCollectionLike() || property.isMap()) ? elementType : null;
}
/**
* @author Jens Schauder
* @author Mark Paluch
* @since 1.5.1
*/
enum DataTypeProvider {
EntityUserType {
@Override
public DataType getDataType(CassandraPersistentEntity<?> entity) {
return entity.isTupleType() ? entity.getTupleType() : entity.getUserType();
}
@Override
DataType getUserType(com.datastax.oss.driver.api.core.CqlIdentifier userTypeName,
UserTypeResolver userTypeResolver) {
return userTypeResolver.resolveType(userTypeName);
}
},
ShallowType {
@Override
public DataType getDataType(CassandraPersistentEntity<?> entity) {
return entity.isTupleType() ? entity.getTupleType() : new ShallowUserDefinedType(entity.getTableName(), false);
}
@Override
DataType getUserType(com.datastax.oss.driver.api.core.CqlIdentifier userTypeName,
UserTypeResolver userTypeResolver) {
return new ShallowUserDefinedType(userTypeName, false);
}
},
FrozenLiteral {
@Override
public DataType getDataType(CassandraPersistentEntity<?> entity) {
return new ShallowUserDefinedType(entity.getTableName(), true);
}
@Override
DataType getUserType(com.datastax.oss.driver.api.core.CqlIdentifier userTypeName,
UserTypeResolver userTypeResolver) {
return new ShallowUserDefinedType(userTypeName, true);
}
};
/**
* Return the data type for the {@link CassandraPersistentEntity}.
*
* @param entity must not be {@literal null}.
* @return the {@link DataType}.
*/
@Nullable
abstract DataType getDataType(CassandraPersistentEntity<?> entity);
/**
* Return the user-defined type {@code userTypeName}.
*
* @param userTypeName must not be {@literal null}.
* @param userTypeResolver must not be {@literal null}.
* @return the {@link DataType}.
* @since 2.0.1
*/
@Nullable
abstract DataType getUserType(com.datastax.oss.driver.api.core.CqlIdentifier userTypeName,
UserTypeResolver userTypeResolver);
}
static class ShallowUserDefinedType implements com.datastax.oss.driver.api.core.type.UserDefinedType {
private final CqlIdentifier name;
private final boolean frozen;
public ShallowUserDefinedType(String name, boolean frozen) {
this(CqlIdentifier.fromInternal(name), frozen);
}
public ShallowUserDefinedType(CqlIdentifier name, boolean frozen) {
this.name = name;
this.frozen = frozen;
}
@Override
public CqlIdentifier getKeyspace() {
return null;
}
@Override
public CqlIdentifier getName() {
return name;
}
@Override
public boolean isFrozen() {
return frozen;
}
@Override
public List<CqlIdentifier> getFieldNames() {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
@Override
public int firstIndexOf(CqlIdentifier id) {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
@Override
public int firstIndexOf(String name) {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
@Override
public List<DataType> getFieldTypes() {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
@Override
public com.datastax.oss.driver.api.core.type.UserDefinedType copy(boolean newFrozen) {
return new ShallowUserDefinedType(this.name, newFrozen);
}
@Override
public UdtValue newValue() {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
@Override
public UdtValue newValue(@edu.umd.cs.findbugs.annotations.NonNull Object... fields) {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
@Override
public AttachmentPoint getAttachmentPoint() {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
@Override
public boolean isDetached() {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
@Override
public void attach(@edu.umd.cs.findbugs.annotations.NonNull AttachmentPoint attachmentPoint) {
throw new UnsupportedOperationException(
"This implementation should only be used internally, this is likely a driver bug");
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof com.datastax.oss.driver.api.core.type.UserDefinedType))
return false;
com.datastax.oss.driver.api.core.type.UserDefinedType that = (com.datastax.oss.driver.api.core.type.UserDefinedType) o;
return isFrozen() == that.isFrozen() && Objects.equals(getName(), that.getName());
}
@Override
public int hashCode() {
return Objects.hash(name, frozen);
}
@Override
public String toString() {
return "ShallowUserDefinedType{" + "name=" + name + ", frozen=" + frozen + '}';
}
}
}

View File

@@ -16,11 +16,9 @@
package org.springframework.data.cassandra.core.mapping;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.type.TupleType;
/**
* Cassandra specific {@link PersistentEntity} abstraction.
@@ -79,14 +77,6 @@ public interface CassandraPersistentEntity<T> extends PersistentEntity<T, Cassan
*/
boolean isTupleType();
/**
* @return the {@link TupleType} matching the data types from {@link BasicCassandraPersistentTupleProperty mapped
* tuple elements}.
* @since 2.1
*/
@Nullable
TupleType getTupleType();
/**
* @return {@literal true} if the type is a mapped user defined type.
* @since 1.5
@@ -94,12 +84,4 @@ public interface CassandraPersistentEntity<T> extends PersistentEntity<T, Cassan
*/
boolean isUserDefinedType();
/**
* @return the CQL {@link UserType} if the type is a mapped user defined type, otherwise {@literal null}.
* @since 1.5
* @see UserDefinedType
*/
@Nullable
com.datastax.oss.driver.api.core.type.UserDefinedType getUserType();
}

View File

@@ -19,14 +19,12 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedType;
import org.springframework.context.ApplicationContextAware;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.cassandra.core.cql.Ordering;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.type.DataType;
/**
* Cassandra specific {@link org.springframework.data.mapping.PersistentProperty} extension.
@@ -88,15 +86,6 @@ public interface CassandraPersistentProperty
return columnName;
}
/**
* 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
*/
DataType getDataType();
/**
* Whether to force-quote the column names of this property.
*

View File

@@ -15,10 +15,7 @@
*/
package org.springframework.data.cassandra.core.mapping;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.CqlIdentifier;
@@ -32,27 +29,16 @@ import com.datastax.oss.driver.api.core.CqlIdentifier;
*/
public class CassandraUserTypePersistentEntity<T> extends BasicCassandraPersistentEntity<T> {
private final UserTypeResolver resolver;
private final Object lock = new Object();
private volatile @Nullable com.datastax.oss.driver.api.core.type.UserDefinedType userType;
/**
* Create a new {@link CassandraUserTypePersistentEntity}.
*
* @param typeInformation must not be {@literal null}.
* @param verifier must not be {@literal null}.
* @param resolver must not be {@literal null}.
*/
public CassandraUserTypePersistentEntity(TypeInformation<T> typeInformation,
CassandraPersistentEntityMetadataVerifier verifier, UserTypeResolver resolver) {
CassandraPersistentEntityMetadataVerifier verifier) {
super(typeInformation, verifier);
Assert.notNull(resolver, "UserTypeResolver must not be null");
this.resolver = resolver;
}
/* (non-Javadoc)
@@ -77,29 +63,4 @@ public class CassandraUserTypePersistentEntity<T> extends BasicCassandraPersiste
public boolean isUserDefinedType() {
return true;
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.mapping.BasicCassandraPersistentEntity#getUserType()
*/
@Override
public com.datastax.oss.driver.api.core.type.UserDefinedType getUserType() {
if (userType == null) {
synchronized (lock) {
if (userType == null) {
CqlIdentifier identifier = determineTableName();
com.datastax.oss.driver.api.core.type.UserDefinedType userType = resolver.resolveType(identifier);
if (userType == null) {
throw new MappingException(String.format("User type [%s] not found", identifier));
}
this.userType = userType;
}
}
}
return userType;
}
}

View File

@@ -30,6 +30,7 @@ import com.datastax.oss.driver.internal.core.type.DefaultTupleType;
* @author Mark Paluch
* @since 3.0
*/
public enum SimpleTupleTypeFactory implements TupleTypeFactory {
/**

View File

@@ -30,8 +30,10 @@ import com.datastax.oss.driver.api.core.type.TupleType;
* @since 2.1
* @see SimpleTupleTypeFactory
* @see CodecRegistryTupleTypeFactory
* @deprecated since 3.0
*/
@FunctionalInterface
@Deprecated
public interface TupleTypeFactory {
/**

View File

@@ -35,7 +35,6 @@ import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.Statement;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* Base class for {@link RepositoryQuery} implementations for Cassandra.
@@ -48,7 +47,6 @@ public abstract class AbstractCassandraQuery extends CassandraRepositoryQuerySup
private final CassandraOperations operations;
private final CodecRegistry codecRegistry;
private static CassandraConverter toConverter(CassandraOperations operations) {
@@ -73,7 +71,6 @@ public abstract class AbstractCassandraQuery extends CassandraRepositoryQuerySup
super(queryMethod, toMappingContext(operations));
this.operations = operations;
this.codecRegistry = operations.getConverter().getMappingContext().getCodecRegistry();
}
/**
@@ -94,7 +91,7 @@ public abstract class AbstractCassandraQuery extends CassandraRepositoryQuerySup
public Object execute(Object[] parameters) {
CassandraParameterAccessor parameterAccessor = new ConvertingParameterAccessor(toConverter(getOperations()),
new CassandraParametersParameterAccessor(getQueryMethod(), parameters), codecRegistry);
new CassandraParametersParameterAccessor(getQueryMethod(), parameters));
ResultProcessor resultProcessor = getQueryMethod().getResultProcessor().withDynamicProjection(parameterAccessor);

View File

@@ -39,7 +39,6 @@ import org.springframework.util.Assert;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.Statement;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* Base class for reactive {@link RepositoryQuery} implementations for Cassandra.
@@ -53,8 +52,6 @@ public abstract class AbstractReactiveCassandraQuery extends CassandraRepository
private final ReactiveCassandraOperations operations;
private final CodecRegistry codecRegistry;
/**
* Create a new {@link AbstractReactiveCassandraQuery} from the given {@link CassandraQueryMethod} and
* {@link CassandraOperations}.
@@ -67,7 +64,6 @@ public abstract class AbstractReactiveCassandraQuery extends CassandraRepository
super(method, getRequiredMappingContext(operations));
this.operations = operations;
this.codecRegistry = operations.getConverter().getMappingContext().getCodecRegistry();
}
/*
@@ -102,7 +98,7 @@ public abstract class AbstractReactiveCassandraQuery extends CassandraRepository
parameters);
CassandraParameterAccessor convertingParameterAccessor = new ConvertingParameterAccessor(
getRequiredConverter(getReactiveCassandraOperations()), parameterAccessor, codecRegistry);
getRequiredConverter(getReactiveCassandraOperations()), parameterAccessor);
Statement<?> statement = createQuery(convertingParameterAccessor);

View File

@@ -18,7 +18,6 @@ package org.springframework.data.cassandra.repository.query;
import java.util.Collection;
import java.util.Iterator;
import java.util.Optional;
import java.util.Set;
import org.springframework.data.cassandra.core.convert.CassandraConverter;
import org.springframework.data.cassandra.core.cql.QueryOptions;
@@ -26,12 +25,9 @@ import org.springframework.data.cassandra.core.mapping.CassandraPersistentProper
import org.springframework.data.cassandra.core.mapping.CassandraType;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import com.datastax.oss.driver.api.core.type.DataType;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* Custom {@link org.springframework.data.repository.query.ParameterAccessor} that uses a {@link CassandraConverter} to
@@ -43,14 +39,11 @@ import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
*/
class ConvertingParameterAccessor implements CassandraParameterAccessor {
private static final TypeInformation<Set> SET = ClassTypeInformation.from(Set.class);
private final CassandraConverter converter;
private final CassandraParameterAccessor delegate;
ConvertingParameterAccessor(CassandraConverter converter, CassandraParameterAccessor delegate,
CodecRegistry codecRegistry) {
ConvertingParameterAccessor(CassandraConverter converter, CassandraParameterAccessor delegate) {
this.converter = converter;
this.delegate = delegate;

View File

@@ -33,14 +33,17 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.cassandra.core.convert.SchemaFactory;
import org.springframework.data.cassandra.core.cql.CqlOperations;
import org.springframework.data.cassandra.core.cql.keyspace.CreateUserTypeSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.UserTypeNameSpecification;
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.core.mapping.UserDefinedType;
import org.springframework.data.convert.CustomConversions;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* Unit tests for {@link CassandraPersistentEntitySchemaCreator}.
@@ -66,6 +69,9 @@ public class CassandraPersistentEntitySchemaCreatorUnitTests extends CassandraPe
});
when(adminOperations.getCqlOperations()).thenReturn(operations);
when(adminOperations.getSchemaFactory()).thenReturn(new SchemaFactory(context,
new CustomConversions(CustomConversions.StoreConversions.NONE, Collections.emptyList()),
CodecRegistry.DEFAULT));
}
@Test // DATACASS-687

View File

@@ -46,7 +46,6 @@ import org.junit.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.core.CassandraTemplate;
import org.springframework.data.cassandra.core.mapping.SimpleTupleTypeFactory;
import org.springframework.data.cassandra.domain.AllPossibleTypes;
import org.springframework.data.cassandra.repository.support.SchemaTestUtils;
import org.springframework.data.cassandra.support.CassandraVersion;
@@ -470,7 +469,7 @@ public class CassandraTypeMappingIntegrationTests extends AbstractKeyspaceCreati
@Test // DATACASS-284
public void shouldReadAndWriteTupleType() {
TupleType tupleType = SimpleTupleTypeFactory.DEFAULT.create(DataTypes.TEXT, DataTypes.BIGINT);
TupleType tupleType = DataTypes.tupleOf(DataTypes.TEXT, DataTypes.BIGINT);
AllPossibleTypes entity = new AllPossibleTypes("1");
@@ -487,7 +486,7 @@ public class CassandraTypeMappingIntegrationTests extends AbstractKeyspaceCreati
@Test // DATACASS-284
public void shouldReadAndWriteListOfTuples() {
TupleType tupleType = SimpleTupleTypeFactory.DEFAULT.create(DataTypes.TEXT, DataTypes.BIGINT);
TupleType tupleType = DataTypes.tupleOf(DataTypes.TEXT, DataTypes.BIGINT);
ListOfTuples entity = new ListOfTuples();

View File

@@ -21,16 +21,20 @@ import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.junit.Test;
import org.springframework.data.cassandra.core.mapping.BasicCassandraPersistentEntity;
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
import org.springframework.data.cassandra.core.mapping.CassandraType;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.util.ClassTypeInformation;
import com.datastax.oss.driver.api.core.data.TupleValue;
import com.datastax.oss.driver.api.core.type.DataTypes;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* Unit tests for {@link DefaultColumnTypeResolver}.
@@ -40,7 +44,8 @@ import com.datastax.oss.driver.api.core.type.DataTypes;
public class ColumnTypeResolverUnitTests {
CassandraMappingContext mappingContext = new CassandraMappingContext();
ColumnTypeResolver resolver = new DefaultColumnTypeResolver(mappingContext);
ColumnTypeResolver resolver = new DefaultColumnTypeResolver(mappingContext, null, () -> CodecRegistry.DEFAULT,
mappingContext::getCustomConversions);
@Test // DATACASS-743
public void shouldResolveSimpleType() {
@@ -177,10 +182,23 @@ public class ColumnTypeResolverUnitTests {
BasicCassandraPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(Person.class);
assertThat(resolver.resolve(entity.getRequiredPersistentProperty("tupleValue")).getType())
CassandraColumnType columnType = resolver.resolve(entity.getRequiredPersistentProperty("tupleValue"));
assertThat(columnType.getType())
.isEqualTo(TupleValue.class);
assertThat(resolver.resolve(entity.getRequiredPersistentProperty("tupleValue")).getDataType())
.isEqualTo(DataTypes.tupleOf());
assertThatThrownBy(columnType::getDataType).isInstanceOf(MappingException.class);
}
@Test // DATACASS-375, DATACASS-743
public void UuidshouldMapToUUIDByDefault() {
CassandraPersistentProperty uuidProperty = mappingContext.getRequiredPersistentEntity(TypeWithUUIDColumn.class)
.getRequiredPersistentProperty("uuid");
CassandraPersistentProperty timeUUIDProperty = mappingContext.getRequiredPersistentEntity(TypeWithUUIDColumn.class)
.getRequiredPersistentProperty("timeUUID");
assertThat(resolver.resolve(uuidProperty).getDataType()).isEqualTo(DataTypes.UUID);
assertThat(resolver.resolve(timeUUIDProperty).getDataType()).isEqualTo(DataTypes.TIMEUUID);
}
static class Person {
@@ -213,6 +231,13 @@ public class ColumnTypeResolverUnitTests {
TupleValue tupleValue;
}
static class TypeWithUUIDColumn {
UUID uuid;
@CassandraType(type = CassandraType.Name.TIMEUUID) UUID timeUUID;
}
enum MyEnum {
INSTANCE;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2020 the original author or authors.
* Copyright 2020 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.core.mapping;
package org.springframework.data.cassandra.core.convert;
import static org.assertj.core.api.Assertions.*;
import static org.junit.Assume.*;
@@ -29,6 +29,10 @@ import org.springframework.data.cassandra.core.cql.generator.CreateIndexCqlGener
import org.springframework.data.cassandra.core.cql.generator.CreateTableCqlGenerator;
import org.springframework.data.cassandra.core.cql.keyspace.CreateIndexSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.CreateTableSpecification;
import org.springframework.data.cassandra.core.mapping.BasicCassandraPersistentEntity;
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.core.mapping.Indexed;
import org.springframework.data.cassandra.core.mapping.SASI;
import org.springframework.data.cassandra.core.mapping.SASI.StandardAnalyzed;
import org.springframework.data.cassandra.support.CassandraVersion;
import org.springframework.data.cassandra.test.util.AbstractKeyspaceCreatingIntegrationTest;
@@ -46,6 +50,7 @@ import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata;
public class IndexCreationIntegrationTests extends AbstractKeyspaceCreatingIntegrationTest {
private CassandraMappingContext mappingContext = new CassandraMappingContext();
private SchemaFactory schemaFactory = new SchemaFactory(new MappingCassandraConverter(mappingContext));
private Version cassandraVersion;
@Before
@@ -60,8 +65,8 @@ public class IndexCreationIntegrationTests extends AbstractKeyspaceCreatingInteg
public void shouldCreateSecondaryIndex() throws InterruptedException {
BasicCassandraPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(WithSecondaryIndex.class);
CreateTableSpecification createTable = mappingContext.getCreateTableSpecificationFor(entity);
List<CreateIndexSpecification> createIndexes = mappingContext.getCreateIndexSpecificationsFor(entity);
CreateTableSpecification createTable = schemaFactory.getCreateTableSpecificationFor(entity);
List<CreateIndexSpecification> createIndexes = schemaFactory.getCreateIndexSpecificationsFor(entity);
session.execute(CreateTableCqlGenerator.toCql(createTable));
createIndexes.forEach(it -> session.execute(CreateIndexCqlGenerator.toCql(it)));
@@ -78,8 +83,8 @@ public class IndexCreationIntegrationTests extends AbstractKeyspaceCreatingInteg
public void shouldCreateSasiIndex() throws InterruptedException {
BasicCassandraPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(WithSasiIndex.class);
CreateTableSpecification createTable = mappingContext.getCreateTableSpecificationFor(entity);
List<CreateIndexSpecification> createIndexes = mappingContext.getCreateIndexSpecificationsFor(entity);
CreateTableSpecification createTable = schemaFactory.getCreateTableSpecificationFor(entity);
List<CreateIndexSpecification> createIndexes = schemaFactory.getCreateIndexSpecificationsFor(entity);
session.execute(CreateTableCqlGenerator.toCql(createTable));
createIndexes.forEach(it -> session.execute(CreateIndexCqlGenerator.toCql(it)));

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2020 the original author or authors.
* Copyright 2020 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.core.mapping;
package org.springframework.data.cassandra.core.convert;
import static org.assertj.core.api.Assertions.*;
@@ -26,6 +26,11 @@ import org.springframework.data.annotation.AccessType;
import org.springframework.data.annotation.AccessType.Type;
import org.springframework.data.cassandra.core.cql.keyspace.CreateIndexSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.CreateIndexSpecification.ColumnFunction;
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentProperty;
import org.springframework.data.cassandra.core.mapping.Indexed;
import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn;
import org.springframework.data.cassandra.core.mapping.SASI;
import org.springframework.data.cassandra.core.mapping.SASI.NonTokenizingAnalyzed;
import org.springframework.data.cassandra.core.mapping.SASI.Normalization;
import org.springframework.data.cassandra.core.mapping.SASI.StandardAnalyzed;
@@ -91,8 +96,8 @@ public class IndexSpecificationFactoryUnitTests {
assertThat(simpleSasi.isCustom()).isTrue();
assertThat(simpleSasi.getUsing()).isEqualTo("org.apache.cassandra.index.sasi.SASIIndex");
assertThat(simpleSasi.getColumnFunction()).isEqualTo(ColumnFunction.NONE);
assertThat(simpleSasi.getOptions()).containsEntry("mode", "PREFIX")
.doesNotContainKeys("analyzed", "analyzer_class");
assertThat(simpleSasi.getOptions()).containsEntry("mode", "PREFIX").doesNotContainKeys("analyzed",
"analyzer_class");
}
@Test // DATACASS-306
@@ -101,8 +106,7 @@ public class IndexSpecificationFactoryUnitTests {
CreateIndexSpecification simpleSasi = createIndexFor(IndexedType.class, "sasiStandard");
assertThat(simpleSasi.getColumnFunction()).isEqualTo(ColumnFunction.NONE);
assertThat(simpleSasi.getOptions()).containsEntry("mode", "PREFIX")
.containsEntry("analyzed", "true")
assertThat(simpleSasi.getOptions()).containsEntry("mode", "PREFIX").containsEntry("analyzed", "true")
.containsEntry("tokenization_skip_stop_words", "false")
.containsEntry("analyzer_class", "org.apache.cassandra.index.sasi.analyzer.StandardAnalyzer")
.containsEntry("tokenization_locale", "de");
@@ -115,8 +119,7 @@ public class IndexSpecificationFactoryUnitTests {
assertThat(simpleSasi.getColumnFunction()).isEqualTo(ColumnFunction.NONE);
assertThat(simpleSasi.getOptions()).containsEntry("tokenization_skip_stop_words", "true")
.containsEntry("tokenization_locale", "de")
.containsEntry("tokenization_enable_stemming", "true")
.containsEntry("tokenization_locale", "de").containsEntry("tokenization_enable_stemming", "true")
.containsEntry("tokenization_normalize_uppercase", "true")
.doesNotContainKey("tokenization_normalize_lowercase");
}
@@ -136,8 +139,7 @@ public class IndexSpecificationFactoryUnitTests {
CreateIndexSpecification simpleSasi = createIndexFor(IndexedType.class, "sasiNontokenizing");
assertThat(simpleSasi.getOptions()).containsEntry("mode", "PREFIX")
.containsEntry("analyzed", "true")
assertThat(simpleSasi.getOptions()).containsEntry("mode", "PREFIX").containsEntry("analyzed", "true")
.containsEntry("case_sensitive", "true")
.containsEntry("analyzer_class", "org.apache.cassandra.index.sasi.analyzer.NonTokenizingAnalyzer")
.doesNotContainKeys("normalize_lowercase", "normalize_uppercase");
@@ -149,8 +151,7 @@ public class IndexSpecificationFactoryUnitTests {
CreateIndexSpecification simpleSasi = createIndexFor(IndexedType.class, "sasiNontokenizingLowercase");
assertThat(simpleSasi.getOptions()).containsEntry("normalize_lowercase", "true")
.containsEntry("case_sensitive", "false")
.doesNotContainKey("normalize_uppercase");
.containsEntry("case_sensitive", "false").doesNotContainKey("normalize_uppercase");
}

View File

@@ -39,6 +39,7 @@ import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.data.TupleValue;
import com.datastax.oss.driver.api.core.type.DataTypes;
import com.datastax.oss.driver.api.core.type.TupleType;
/**
* Unit tests for mapped tuples through {@link MappingCassandraConverter}.
@@ -67,10 +68,12 @@ public class MappingCassandraConverterMappedTupleUnitTests {
BasicCassandraPersistentEntity<?> entity = this.mappingContext.getRequiredPersistentEntity(MappedTuple.class);
TupleValue value = entity.getTupleType().newValue("hello", 1);
CassandraColumnType type = mappingCassandraConverter.getColumnTypeResolver().resolve(entity.getTypeInformation());
TupleValue value = ((TupleType) type.getDataType()).newValue("hello", 1);
this.rowMock = RowMockUtil.newRowMock(column("name", "Jon Doe", DataTypes.TEXT),
column("tuple", value, entity.getTupleType()));
column("tuple", value, type.getDataType()));
Person person = this.mappingCassandraConverter.read(Person.class, rowMock);

View File

@@ -44,7 +44,6 @@ import org.springframework.data.cassandra.core.cql.keyspace.CreateUserTypeSpecif
import org.springframework.data.cassandra.core.cql.util.StatementBuilder;
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.core.mapping.Element;
import org.springframework.data.cassandra.core.mapping.SimpleTupleTypeFactory;
import org.springframework.data.cassandra.core.mapping.Table;
import org.springframework.data.cassandra.core.mapping.Tuple;
import org.springframework.data.cassandra.core.mapping.UserDefinedType;
@@ -105,8 +104,9 @@ public class MappingCassandraConverterTupleIntegrationTests extends AbstractSpri
this.session.execute("DROP TABLE IF EXISTS person;");
CassandraMappingContext mappingContext = converter.getMappingContext();
SchemaFactory schemaFactory = new SchemaFactory(converter);
CreateUserTypeSpecification createAddress = mappingContext
CreateUserTypeSpecification createAddress = schemaFactory
.getCreateUserTypeSpecificationFor(mappingContext.getRequiredPersistentEntity(AddressUserType.class));
this.session.execute(CreateUserTypeCqlGenerator.toCql(createAddress));
@@ -127,7 +127,7 @@ public class MappingCassandraConverterTupleIntegrationTests extends AbstractSpri
@Test // DATACASS-651
public void shouldInsertRowWithTuple() {
TupleType tupleType = SimpleTupleTypeFactory.DEFAULT.create(DataTypes.TEXT, DataTypes.INT);
TupleType tupleType = DataTypes.tupleOf(DataTypes.TEXT, DataTypes.INT);
Person person = new Person();
@@ -207,7 +207,7 @@ public class MappingCassandraConverterTupleIntegrationTests extends AbstractSpri
person.setMapOfTuples(Collections.singletonMap("foo", tuple));
TupleType tupleType = SimpleTupleTypeFactory.DEFAULT.create(DataTypes.TEXT, DataTypes.INT);
TupleType tupleType = DataTypes.tupleOf(DataTypes.TEXT, DataTypes.INT);
person.setMapOfTupleValues(Collections.singletonMap("mykey", tupleType.newValue("hello", 42)));
StatementFactory statementFactory = new StatementFactory(new UpdateMapper(converter));

View File

@@ -218,7 +218,9 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring
CassandraPersistentEntity<?> persistentEntity = converter.getMappingContext()
.getRequiredPersistentEntity(AddressUserType.class);
UdtValue udtValue = persistentEntity.getUserType().newValue();
com.datastax.oss.driver.api.core.type.UserDefinedType udtType = (com.datastax.oss.driver.api.core.type.UserDefinedType) converter
.getColumnTypeResolver().resolve(persistentEntity.getTypeInformation()).getDataType();
UdtValue udtValue = udtType.newValue();
udtValue.setString("zip", "69469");
udtValue.setString("city", "Weinheim");
udtValue.setList("streetlines", Arrays.asList("Heckenpfad", "14"), String.class);
@@ -256,7 +258,10 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring
CassandraPersistentEntity<?> persistentEntity = converter.getMappingContext()
.getRequiredPersistentEntity(AddressUserType.class);
UdtValue udtValue = persistentEntity.getUserType().newValue();
com.datastax.oss.driver.api.core.type.UserDefinedType udtType = (com.datastax.oss.driver.api.core.type.UserDefinedType) converter
.getColumnTypeResolver().resolve(persistentEntity.getTypeInformation()).getDataType();
UdtValue udtValue = udtType.newValue();
udtValue.setString("zip", "69469");
udtValue.setString("city", "Weinheim");
udtValue.setList("streetlines", Arrays.asList("Heckenpfad", "14"), String.class);

View File

@@ -352,8 +352,7 @@ public class QueryMapperUnitTests {
Filter mappedObject = this.queryMapper.getMappedObject(filter,
this.mappingContext.getRequiredPersistentEntity(Person.class));
TupleValue tupleValue = this.mappingContext.getRequiredPersistentEntity(MappedTuple.class).getTupleType()
.newValue();
TupleValue tupleValue = DataTypes.tupleOf(DataTypes.TEXT).newValue();
tupleValue.setString(0, "foo");

View File

@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.cassandra.core.mapping;
package org.springframework.data.cassandra.core.convert;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.data.cassandra.core.mapping.CassandraType.*;
@@ -24,10 +25,12 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import org.junit.Before;
@@ -35,42 +38,320 @@ import org.junit.Test;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.annotation.Id;
import org.springframework.data.cassandra.core.convert.CassandraCustomConversions;
import org.springframework.data.cassandra.core.cql.Ordering;
import org.springframework.data.cassandra.core.cql.PrimaryKeyType;
import org.springframework.data.cassandra.core.cql.keyspace.ColumnSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.CreateIndexSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.CreateIndexSpecification.ColumnFunction;
import org.springframework.data.cassandra.core.cql.keyspace.CreateTableSpecification;
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.core.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.core.mapping.CassandraType;
import org.springframework.data.cassandra.core.mapping.Element;
import org.springframework.data.cassandra.core.mapping.Indexed;
import org.springframework.data.cassandra.core.mapping.PrimaryKey;
import org.springframework.data.cassandra.core.mapping.PrimaryKeyClass;
import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn;
import org.springframework.data.cassandra.core.mapping.Table;
import org.springframework.data.cassandra.core.mapping.Tuple;
import org.springframework.data.cassandra.domain.AllPossibleTypes;
import org.springframework.data.cassandra.support.UserDefinedTypeBuilder;
import org.springframework.data.mapping.MappingException;
import org.springframework.util.StringUtils;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.data.TupleValue;
import com.datastax.oss.driver.api.core.data.UdtValue;
import com.datastax.oss.driver.api.core.type.DataType;
import com.datastax.oss.driver.api.core.type.DataTypes;
import com.datastax.oss.driver.api.core.type.ListType;
import com.datastax.oss.driver.api.core.type.SetType;
import com.datastax.oss.driver.api.core.type.TupleType;
import com.datastax.oss.driver.api.core.type.UserDefinedType;
import com.datastax.oss.protocol.internal.ProtocolConstants;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Unit tests for {@link CassandraMappingContext} targeted on {@link CreateTableSpecification}.
* Unit tests for {@link SchemaFactory}.
*
* @author Matthew T. Adams
* @author Mark Paluch
* @author Vagif Zeynalov
* @soundtrack Black Rose - Volbeat
*/
public class CreateTableSpecificationBasicCassandraMappingContextUnitTests {
public class SchemaFactoryUnitTests {
private CassandraMappingContext ctx = new CassandraMappingContext();
CassandraMappingContext ctx = new CassandraMappingContext();
SchemaFactory schemaFactory;
@Before
public void setUp() {
public void before() {
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(new PersonReadConverter());
converters.add(new PersonWriteConverter());
converters.add(HumanToStringConverter.INSTANCE);
CassandraCustomConversions customConversions = new CassandraCustomConversions(converters);
ctx.setCustomConversions(customConversions);
schemaFactory = new SchemaFactory(new MappingCassandraConverter(ctx));
}
@Test // DATACASS-340
public void createdTableSpecificationShouldConsiderClusterColumnOrdering() {
CassandraPersistentEntity<?> persistentEntity = ctx
.getRequiredPersistentEntity(EntityWithOrderedClusteredColumns.class);
CreateTableSpecification tableSpecification = schemaFactory.getCreateTableSpecificationFor(persistentEntity);
assertThat(tableSpecification.getPartitionKeyColumns()).hasSize(1);
assertThat(tableSpecification.getClusteredKeyColumns()).hasSize(3);
ColumnSpecification breed = tableSpecification.getClusteredKeyColumns().get(0);
assertThat(breed.getName().toString()).isEqualTo("breed");
assertThat(breed.getOrdering()).isEqualTo(Ordering.ASCENDING);
ColumnSpecification color = tableSpecification.getClusteredKeyColumns().get(1);
assertThat(color.getName().toString()).isEqualTo("color");
assertThat(color.getOrdering()).isEqualTo(Ordering.DESCENDING);
ColumnSpecification kind = tableSpecification.getClusteredKeyColumns().get(2);
assertThat(kind.getName().toString()).isEqualTo("kind");
assertThat(kind.getOrdering()).isEqualTo(Ordering.ASCENDING);
}
@Test // DATACASS-340
public void createdTableSpecificationShouldConsiderPrimaryKeyClassClusterColumnOrdering() {
CassandraPersistentEntity<?> persistentEntity = ctx
.getRequiredPersistentEntity(EntityWithPrimaryKeyWithOrderedClusteredColumns.class);
CreateTableSpecification tableSpecification = schemaFactory.getCreateTableSpecificationFor(persistentEntity);
assertThat(tableSpecification.getPartitionKeyColumns()).hasSize(1);
assertThat(tableSpecification.getClusteredKeyColumns()).hasSize(3);
ColumnSpecification breed = tableSpecification.getClusteredKeyColumns().get(0);
assertThat(breed.getName().toString()).isEqualTo("breed");
assertThat(breed.getOrdering()).isEqualTo(Ordering.ASCENDING);
ColumnSpecification color = tableSpecification.getClusteredKeyColumns().get(1);
assertThat(color.getName().toString()).isEqualTo("color");
assertThat(color.getOrdering()).isEqualTo(Ordering.DESCENDING);
ColumnSpecification kind = tableSpecification.getClusteredKeyColumns().get(2);
assertThat(kind.getName().toString()).isEqualTo("kind");
assertThat(kind.getOrdering()).isEqualTo(Ordering.ASCENDING);
}
@Test // DATACASS-487
public void shouldCreateTableForMappedAndConvertedColumn() {
UserDefinedType mappedudt = UserDefinedTypeBuilder.forName("mappedudt").withField("foo", DataTypes.ASCII).build();
this.ctx.setUserTypeResolver(typeName -> mappedudt);
CassandraPersistentEntity<?> persistentEntity = this.ctx.getRequiredPersistentEntity(WithMapOfMixedTypes.class);
CreateTableSpecification tableSpecification = schemaFactory.getCreateTableSpecificationFor(persistentEntity);
assertThat(tableSpecification.getColumns()).hasSize(2);
ColumnSpecification column = tableSpecification.getColumns().get(1);
assertThat(column.getType().asCql(true, true)).isEqualTo("map<frozen<mappedudt>, list<text>>");
}
@PrimaryKeyClass
private static class CompositePrimaryKeyClassWithProperties implements Serializable {
String firstname;
String lastname;
@PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.PARTITIONED)
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
@PrimaryKeyColumn(name = "mylastname", ordinal = 2, type = PrimaryKeyType.CLUSTERED)
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
}
@Table
static class EntityWithOrderedClusteredColumns {
@PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) String species;
@PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.ASCENDING) String breed;
@PrimaryKeyColumn(ordinal = 2, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.DESCENDING) String color;
@PrimaryKeyColumn(ordinal = 3, type = PrimaryKeyType.CLUSTERED) String kind;
}
@PrimaryKeyClass
static class PrimaryKeyWithOrderedClusteredColumns implements Serializable {
@PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) String species;
@PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.ASCENDING) String breed;
@PrimaryKeyColumn(ordinal = 2, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.DESCENDING) String color;
@PrimaryKeyColumn(ordinal = 3, type = PrimaryKeyType.CLUSTERED) String kind;
}
@Table
private static class EntityWithPrimaryKeyWithOrderedClusteredColumns {
@PrimaryKey PrimaryKeyWithOrderedClusteredColumns key;
}
@Test // DATACASS-213
public void createIndexShouldConsiderAnnotatedProperties() {
List<CreateIndexSpecification> specifications = schemaFactory
.getCreateIndexSpecificationsFor(ctx.getRequiredPersistentEntity(IndexedType.class));
CreateIndexSpecification firstname = getSpecificationFor("first_name", specifications);
assertThat(firstname.getColumnName()).isEqualTo(CqlIdentifier.fromCql("first_name"));
assertThat(firstname.getTableName()).isEqualTo(CqlIdentifier.fromCql("indexedtype"));
assertThat(firstname.getName()).isEqualTo(CqlIdentifier.fromCql("my_index"));
assertThat(firstname.getColumnFunction()).isEqualTo(ColumnFunction.NONE);
CreateIndexSpecification phoneNumbers = getSpecificationFor("phoneNumbers", specifications);
assertThat(phoneNumbers.getColumnName()).isEqualTo(CqlIdentifier.fromCql("phoneNumbers"));
assertThat(phoneNumbers.getTableName()).isEqualTo(CqlIdentifier.fromCql("indexedtype"));
assertThat(phoneNumbers.getName()).isNull();
assertThat(phoneNumbers.getColumnFunction()).isEqualTo(ColumnFunction.NONE);
}
@Test // DATACASS-213
public void createIndexForClusteredPrimaryKeyShouldConsiderAnnotatedAccessors() {
List<CreateIndexSpecification> specifications = schemaFactory
.getCreateIndexSpecificationsFor(ctx.getRequiredPersistentEntity(CompositeKeyEntity.class));
CreateIndexSpecification entries = getSpecificationFor("last_name", specifications);
assertThat(entries.getColumnName()).isEqualTo(CqlIdentifier.fromCql("last_name"));
assertThat(entries.getTableName()).isEqualTo(CqlIdentifier.fromCql("compositekeyentity"));
assertThat(entries.getName()).isEqualTo(CqlIdentifier.fromCql("my_index"));
assertThat(entries.getColumnFunction()).isEqualTo(ColumnFunction.NONE);
}
@Test // DATACASS-284, DATACASS-651
public void shouldRejectUntypedTuples() {
assertThatThrownBy(() -> this.schemaFactory
.getCreateTableSpecificationFor(this.ctx.getRequiredPersistentEntity(UntypedTupleEntity.class)))
.isInstanceOf(MappingException.class);
assertThatThrownBy(() -> this.schemaFactory
.getCreateTableSpecificationFor(this.ctx.getRequiredPersistentEntity(UntypedTupleMapEntity.class)))
.isInstanceOf(MappingException.class);
}
@Test // DATACASS-284
public void shouldCreateTableForTypedTupleType() {
CreateTableSpecification tableSpecification = this.schemaFactory
.getCreateTableSpecificationFor(this.ctx.getRequiredPersistentEntity(TypedTupleEntity.class));
assertThat(tableSpecification.getColumns()).hasSize(2);
ColumnSpecification column = tableSpecification.getColumns().get(1);
assertThat(column.getType()).isInstanceOf(TupleType.class);
assertThat(column.getType()).isEqualTo(DataTypes.tupleOf(DataTypes.TEXT, DataTypes.BIGINT));
}
@Test // DATACASS-651
public void shouldCreateTableForEntityWithMapOfTuples() {
CreateTableSpecification tableSpecification = this.schemaFactory
.getCreateTableSpecificationFor(this.ctx.getRequiredPersistentEntity(EntityWithMapOfTuples.class));
assertThat(tableSpecification.getColumns()).hasSize(2);
ColumnSpecification column = tableSpecification.getColumns().get(1);
assertThat(column.getName()).isEqualTo(CqlIdentifier.fromCql("map"));
assertThat(column.getType().asCql(true, true)).isEqualTo("map<text, frozen<tuple<mappedudt, human_udt, text>>>");
}
private static CreateIndexSpecification getSpecificationFor(String column,
List<CreateIndexSpecification> specifications) {
return specifications.stream().filter(it -> it.getColumnName().equals(CqlIdentifier.fromCql(column))).findFirst()
.orElseThrow(() -> new NoSuchElementException(column));
}
static class IndexedType {
@PrimaryKeyColumn("first_name") @Indexed("my_index") String firstname;
@Indexed List<String> phoneNumbers;
}
@PrimaryKeyClass
static class CompositeKeyWithIndex {
@PrimaryKeyColumn(value = "first_name", type = PrimaryKeyType.PARTITIONED) String firstname;
@PrimaryKeyColumn("last_name") @Indexed("my_index") String lastname;
}
static class CompositeKeyEntity {
@PrimaryKey CompositeKeyWithIndex key;
}
static class InvalidMapIndex {
@Indexed Map<@Indexed String, String> mixed;
}
@Test // DATACASS-506
public void shouldCreatedUserTypeSpecificationsWithAnnotatedTypeName() {
assertThat(schemaFactory.getCreateUserTypeSpecificationFor(ctx.getRequiredPersistentEntity(WithUdt.class)))
.isNotNull();
assertThat(schemaFactory.getCreateUserTypeSpecificationFor(ctx.getRequiredPersistentEntity(Nested.class)))
.isNotNull();
}
@Test // DATACASS-172
public void createTableForComplexPrimaryKeyShouldFail() {
try {
schemaFactory
.getCreateTableSpecificationFor(ctx.getRequiredPersistentEntity(EntityWithComplexPrimaryKeyColumn.class));
fail("Missing MappingException");
} catch (MappingException e) {
assertThat(e).hasMessageContaining(
"Cannot resolve DataType for type [class java.lang.Object] for property [complexObject]");
}
try {
schemaFactory.getCreateTableSpecificationFor(ctx.getRequiredPersistentEntity(EntityWithComplexId.class));
fail("Missing MappingException");
} catch (MappingException e) {
assertThat(e).hasMessageContaining(
"Cannot resolve DataType for type [class java.lang.Object] for property [complexObject]");
}
try {
schemaFactory.getCreateTableSpecificationFor(
ctx.getRequiredPersistentEntity(EntityWithPrimaryKeyClassWithComplexId.class));
fail("Missing MappingException");
} catch (MappingException e) {
assertThat(e).hasMessageContaining(
"Cannot resolve DataType for type [class java.lang.Object] for property [complexObject]");
}
}
@Test // DATACASS-296
@@ -78,7 +359,7 @@ public class CreateTableSpecificationBasicCassandraMappingContextUnitTests {
CassandraPersistentEntity<?> persistentEntity = ctx.getRequiredPersistentEntity(Employee.class);
CreateTableSpecification specification = ctx.getCreateTableSpecificationFor(persistentEntity);
CreateTableSpecification specification = schemaFactory.getCreateTableSpecificationFor(persistentEntity);
assertThat(getColumnType("human", specification)).isEqualTo(DataTypes.TEXT);
@@ -102,7 +383,7 @@ public class CreateTableSpecificationBasicCassandraMappingContextUnitTests {
CassandraPersistentEntity<?> persistentEntity = ctx.getRequiredPersistentEntity(Employee.class);
CreateTableSpecification specification = ctx.getCreateTableSpecificationFor(persistentEntity);
CreateTableSpecification specification = schemaFactory.getCreateTableSpecificationFor(persistentEntity);
assertThat(getColumnType("floater", specification)).isEqualTo(DataTypes.FLOAT);
@@ -248,26 +529,6 @@ public class CreateTableSpecificationBasicCassandraMappingContextUnitTests {
@Test // DATACASS-172
public void columnsShouldMapToUdt() {
UserDefinedType human_udt = new CassandraMappingContext.ShallowUserDefinedType("human_udt", false);
UserDefinedType species_udt = new CassandraMappingContext.ShallowUserDefinedType("species_udt", false);
UserDefinedType peeps_udt = new CassandraMappingContext.ShallowUserDefinedType("peeps_udt", false);
ctx.setUserTypeResolver(typeName -> {
if (typeName.equals(human_udt.getName())) {
return human_udt;
}
if (typeName.equals(species_udt.getName())) {
return species_udt;
}
if (typeName.equals(peeps_udt.getName())) {
return peeps_udt;
}
return null;
});
CreateTableSpecification specification = getCreateTableSpecificationFor(WithUdtFields.class);
assertThat(getColumnType("human", specification).asCql(false, true)).isEqualTo("human_udt");
@@ -278,7 +539,7 @@ public class CreateTableSpecificationBasicCassandraMappingContextUnitTests {
@Test // DATACASS-172
public void columnsShouldMapToMappedUserType() {
UserDefinedType mappedUdt = new CassandraMappingContext.ShallowUserDefinedType("mappedudt", true);
UserDefinedType mappedUdt = new SchemaFactory.ShallowUserDefinedType("mappedudt", true);
ctx.setUserTypeResolver(typeName -> {
@@ -336,7 +597,8 @@ public class CreateTableSpecificationBasicCassandraMappingContextUnitTests {
CqlIdentifier customTableName = CqlIdentifier.fromCql("my_custom_came");
CassandraPersistentEntity<?> persistentEntity = ctx.getRequiredPersistentEntity(Employee.class);
CreateTableSpecification specification = ctx.getCreateTableSpecificationFor(customTableName, persistentEntity);
CreateTableSpecification specification = schemaFactory.getCreateTableSpecificationFor(persistentEntity,
customTableName);
assertThat(specification).isNotNull();
assertThat(specification.getName()).isEqualTo(customTableName);
@@ -348,7 +610,7 @@ public class CreateTableSpecificationBasicCassandraMappingContextUnitTests {
ctx.setCustomConversions(customConversions);
CassandraPersistentEntity<?> persistentEntity = ctx.getRequiredPersistentEntity(persistentEntityClass);
return ctx.getCreateTableSpecificationFor(persistentEntity);
return schemaFactory.getCreateTableSpecificationFor(persistentEntity);
}
private DataType getColumnType(String columnName, CreateTableSpecification specification) {
@@ -405,10 +667,8 @@ public class CreateTableSpecificationBasicCassandraMappingContextUnitTests {
@Id String id;
@CassandraType(type = Name.UDT, userTypeName = "human_udt") UdtValue human;
@CassandraType(type = Name.LIST, typeArguments = Name.UDT,
userTypeName = "species_udt") List<UdtValue> friends;
@CassandraType(type = Name.SET, typeArguments = Name.UDT,
userTypeName = "peeps_udt") Set<UdtValue> people;
@CassandraType(type = Name.LIST, typeArguments = Name.UDT, userTypeName = "species_udt") List<UdtValue> friends;
@CassandraType(type = Name.SET, typeArguments = Name.UDT, userTypeName = "peeps_udt") Set<UdtValue> people;
}
@Data
@@ -473,4 +733,98 @@ public class CreateTableSpecificationBasicCassandraMappingContextUnitTests {
}
}
}
@Table
private static class InvalidEntityWithIdAndPrimaryKeyColumn {
@Id String foo;
@PrimaryKeyColumn String bar;
}
@Table
static class EntityWithComplexPrimaryKeyColumn {
@PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) Object complexObject;
}
@Table
static class EntityWithComplexId {
@Id Object complexObject;
}
@PrimaryKeyClass
static class PrimaryKeyClassWithComplexId {
@PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) Object complexObject;
}
@Table
static class EntityWithPrimaryKeyClassWithComplexId {
@Id PrimaryKeyClassWithComplexId primaryKeyClassWithComplexId;
}
@Table
private static class X {
@PrimaryKey String key;
}
@Table
private static class Y {
@PrimaryKey String key;
}
@Table
private static class WithUdt {
@Id String id;
@CassandraType(type = Name.UDT, userTypeName = "mappedudt") UdtValue udtValue;
@CassandraType(type = Name.UDT, userTypeName = "NestedType") Nested nested;
}
@Table
private static class WithMapOfMixedTypes {
@Id String id;
Map<MappedUdt, List<Human>> people;
}
enum HumanToStringConverter implements Converter<Human, String> {
INSTANCE;
@Override
public String convert(Human source) {
return "hello";
}
}
@org.springframework.data.cassandra.core.mapping.UserDefinedType(value = "NestedType")
public static class Nested {
String s1;
@CassandraType(type = Name.UDT, userTypeName = "AnotherNestedType") AnotherNested anotherNested;
}
@org.springframework.data.cassandra.core.mapping.UserDefinedType(value = "AnotherNestedType")
public static class AnotherNested {
String str;
}
@Table
static class TypedTupleEntity {
@Id String id;
@CassandraType(type = Name.TUPLE, typeArguments = { Name.VARCHAR, Name.BIGINT }) TupleValue typed;
}
@Table
static class EntityWithMapOfTuples {
@Id String id;
Map<String, MappedTuple> map;
}
@Table
static class UntypedTupleEntity {
@Id String id;
TupleValue untyped;
}
@Table
static class UntypedTupleMapEntity {
@Id String id;
Map<String, TupleValue> untyped;
}
}

View File

@@ -25,7 +25,6 @@ import java.lang.reflect.Field;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.junit.Test;
@@ -35,7 +34,6 @@ import org.springframework.data.util.ClassTypeInformation;
import org.springframework.util.ReflectionUtils;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.protocol.internal.ProtocolConstants;
/**
* Unit tests for {@link BasicCassandraPersistentProperty}.
@@ -102,26 +100,6 @@ public class BasicCassandraPersistentPropertyUnitTests {
assertThat(persistentProperty.isPrimaryKeyColumn()).isTrue();
}
@Test // DATACASS-259
public void shouldConsiderComposedCassandraTypeAnnotation() {
CassandraPersistentProperty persistentProperty = getPropertyFor(TypeWithComposedCassandraTypeAnnotation.class,
"column");
assertThat(persistentProperty.getDataType().getProtocolCode()).isEqualTo(ProtocolConstants.DataType.COUNTER);
assertThat(persistentProperty.findAnnotation(CassandraType.class)).isNotNull();
}
@Test // DATACASS-375
public void UuidshouldMapToUUIDByDefault() {
CassandraPersistentProperty uuidProperty = getPropertyFor(TypeWithUUIDColumn.class, "uuid");
CassandraPersistentProperty timeUUIDProperty = getPropertyFor(TypeWithUUIDColumn.class, "timeUUID");
assertThat(uuidProperty.getDataType().getProtocolCode()).isEqualTo(ProtocolConstants.DataType.UUID);
assertThat(timeUUIDProperty.getDataType().getProtocolCode()).isEqualTo(ProtocolConstants.DataType.TIMEUUID);
}
@Test // DATACASS-568
public void shouldFindAnnotationInMapTypes() {
@@ -144,6 +122,15 @@ public class BasicCassandraPersistentPropertyUnitTests {
assertThat(findAnnotatedType(TypeWithCollections.class, "subtype")).isNull();
}
@Test // DATACASS-259
public void shouldConsiderComposedCassandraTypeAnnotation() {
CassandraPersistentProperty persistentProperty = getPropertyFor(TypeWithComposedCassandraTypeAnnotation.class,
"column");
assertThat(persistentProperty.findAnnotation(CassandraType.class)).isNotNull();
}
private AnnotatedType findAnnotatedType(Class<?> type, String parameterized) {
return getPropertyFor(TypeWithMaps.class, parameterized).findAnnotatedType(Indexed.class);
}
@@ -221,13 +208,6 @@ public class BasicCassandraPersistentPropertyUnitTests {
@ComposedCassandraTypeAnnotation String column;
}
static class TypeWithUUIDColumn {
UUID uuid;
@CassandraType(type = Name.TIMEUUID) UUID timeUUID;
}
static class TypeWithMaps {
Map<String, String> parameterized;

View File

@@ -16,24 +16,17 @@
package org.springframework.data.cassandra.core.mapping;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mapping.MappingException;
import com.datastax.oss.driver.api.core.type.DataTypes;
import com.datastax.oss.driver.internal.core.type.DefaultTupleType;
/**
* Unit tests for {@link BasicCassandraPersistentTupleEntity}.
*
@@ -44,13 +37,6 @@ public class BasicCassandraPersistentTupleEntityUnitTests {
CassandraMappingContext mappingContext = new CassandraMappingContext();
@Mock TupleTypeFactory tupleTypeFactory;
@Before
public void before() {
this.mappingContext.setTupleTypeFactory(tupleTypeFactory);
}
@Test // DATACASS-523
public void shouldCreatePersistentTupleEntity() {
@@ -75,21 +61,6 @@ public class BasicCassandraPersistentTupleEntityUnitTests {
assertThat(propertyNames).containsSequence("street", "city", "sortOrder");
}
@Test // DATACASS-523
public void shouldCreateTupleType() {
when(this.tupleTypeFactory.create(anyList()))
.thenReturn(new DefaultTupleType(Arrays.asList(DataTypes.TEXT, DataTypes.TEXT, DataTypes.INT)));
BasicCassandraPersistentEntity<?> entity = this.mappingContext.getRequiredPersistentEntity(Address.class);
entity.verify();
entity.getTupleType();
verify(tupleTypeFactory).create(Arrays.asList(DataTypes.TEXT, DataTypes.TEXT, DataTypes.INT));
}
@Test // DATACASS-523
public void shouldReportDuplicateMappings() {

View File

@@ -15,14 +15,13 @@
*/
package org.springframework.data.cassandra.core.mapping;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.*;
import java.lang.reflect.Field;
import java.util.Date;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.mapping.model.Property;
@@ -37,8 +36,6 @@ import org.springframework.util.ReflectionUtils;
@RunWith(MockitoJUnitRunner.class)
public class BasicCassandraPersistentTuplePropertyUnitTests {
@Mock TupleTypeFactory tupleTypeFactory;
@Test // DATACASS-523
public void mappedTupleShouldNotReportColumnName() {
@@ -64,7 +61,7 @@ public class BasicCassandraPersistentTuplePropertyUnitTests {
}
private <T> BasicCassandraPersistentEntity<T> getEntity(Class<T> type) {
return new BasicCassandraPersistentTupleEntity<>(ClassTypeInformation.from(type), tupleTypeFactory);
return new BasicCassandraPersistentTupleEntity<>(ClassTypeInformation.from(type));
}
@Tuple

View File

@@ -27,6 +27,7 @@ import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.cassandra.core.convert.SchemaFactory;
import org.springframework.data.cassandra.core.cql.PrimaryKeyType;
import org.springframework.data.cassandra.core.cql.keyspace.ColumnSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.CreateTableSpecification;
@@ -46,6 +47,8 @@ public class CassandraCompositePrimaryKeyUnitTests {
CassandraMappingContext context;
SchemaFactory schemaFactory;
CassandraPersistentEntity<?> entity;
CassandraPersistentEntity<?> key;
@@ -54,6 +57,7 @@ public class CassandraCompositePrimaryKeyUnitTests {
public void setup() {
context = new CassandraMappingContext();
schemaFactory = new SchemaFactory(context, context.getCustomConversions(), context.getCodecRegistry());
entity = context.getRequiredPersistentEntity(ClassTypeInformation.from(TypeWithCompositeKey.class));
key = context.getRequiredPersistentEntity(ClassTypeInformation.from(CompositeKey.class));
}
@@ -68,7 +72,7 @@ public class CassandraCompositePrimaryKeyUnitTests {
assertThat(property.isIdProperty()).isTrue();
assertThat(property.isCompositePrimaryKey()).isTrue();
CreateTableSpecification spec = context.getCreateTableSpecificationFor(entity);
CreateTableSpecification spec = schemaFactory.getCreateTableSpecificationFor(entity);
List<ColumnSpecification> partitionKeyColumns = spec.getPartitionKeyColumns();
assertThat(partitionKeyColumns).hasSize(1);

View File

@@ -20,8 +20,6 @@ import static org.mockito.Mockito.*;
import static org.springframework.data.cassandra.core.mapping.CassandraType.*;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -35,25 +33,14 @@ import org.springframework.data.annotation.Id;
import org.springframework.data.cassandra.core.convert.CassandraCustomConversions;
import org.springframework.data.cassandra.core.cql.Ordering;
import org.springframework.data.cassandra.core.cql.PrimaryKeyType;
import org.springframework.data.cassandra.core.cql.keyspace.ColumnSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.CreateIndexSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.CreateIndexSpecification.ColumnFunction;
import org.springframework.data.cassandra.core.cql.keyspace.CreateTableSpecification;
import org.springframework.data.cassandra.domain.AllPossibleTypes;
import org.springframework.data.cassandra.support.UserDefinedTypeBuilder;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.util.ClassTypeInformation;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.data.TupleValue;
import com.datastax.oss.driver.api.core.data.UdtValue;
import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata;
import com.datastax.oss.driver.api.core.type.DataTypes;
import com.datastax.oss.driver.api.core.type.MapType;
import com.datastax.oss.driver.api.core.type.TupleType;
import com.datastax.oss.driver.api.core.type.UserDefinedType;
import com.datastax.oss.driver.internal.core.type.DefaultTupleType;
/**
* Unit tests for {@link CassandraMappingContext}.
@@ -197,75 +184,6 @@ public class CassandraMappingContextUnitTests {
assertThat(lastname.getColumnName().toString()).isEqualTo("mylastname");
}
@Test // DATACASS-340
public void createdTableSpecificationShouldConsiderClusterColumnOrdering() {
CassandraPersistentEntity<?> persistentEntity = mappingContext
.getRequiredPersistentEntity(EntityWithOrderedClusteredColumns.class);
CreateTableSpecification tableSpecification = mappingContext.getCreateTableSpecificationFor(persistentEntity);
assertThat(tableSpecification.getPartitionKeyColumns()).hasSize(1);
assertThat(tableSpecification.getClusteredKeyColumns()).hasSize(3);
ColumnSpecification breed = tableSpecification.getClusteredKeyColumns().get(0);
assertThat(breed.getName().toString()).isEqualTo("breed");
assertThat(breed.getOrdering()).isEqualTo(Ordering.ASCENDING);
ColumnSpecification color = tableSpecification.getClusteredKeyColumns().get(1);
assertThat(color.getName().toString()).isEqualTo("color");
assertThat(color.getOrdering()).isEqualTo(Ordering.DESCENDING);
ColumnSpecification kind = tableSpecification.getClusteredKeyColumns().get(2);
assertThat(kind.getName().toString()).isEqualTo("kind");
assertThat(kind.getOrdering()).isEqualTo(Ordering.ASCENDING);
}
@Test // DATACASS-340
public void createdTableSpecificationShouldConsiderPrimaryKeyClassClusterColumnOrdering() {
CassandraPersistentEntity<?> persistentEntity = mappingContext
.getRequiredPersistentEntity(EntityWithPrimaryKeyWithOrderedClusteredColumns.class);
CreateTableSpecification tableSpecification = mappingContext.getCreateTableSpecificationFor(persistentEntity);
assertThat(tableSpecification.getPartitionKeyColumns()).hasSize(1);
assertThat(tableSpecification.getClusteredKeyColumns()).hasSize(3);
ColumnSpecification breed = tableSpecification.getClusteredKeyColumns().get(0);
assertThat(breed.getName().toString()).isEqualTo("breed");
assertThat(breed.getOrdering()).isEqualTo(Ordering.ASCENDING);
ColumnSpecification color = tableSpecification.getClusteredKeyColumns().get(1);
assertThat(color.getName().toString()).isEqualTo("color");
assertThat(color.getOrdering()).isEqualTo(Ordering.DESCENDING);
ColumnSpecification kind = tableSpecification.getClusteredKeyColumns().get(2);
assertThat(kind.getName().toString()).isEqualTo("kind");
assertThat(kind.getOrdering()).isEqualTo(Ordering.ASCENDING);
}
@Test // DATACASS-487
public void shouldCreateTableForMappedAndConvertedColumn() {
UserDefinedType mappedudt = UserDefinedTypeBuilder.forName("mappedudt").withField("foo", DataTypes.ASCII).build();
this.mappingContext.setUserTypeResolver(typeName -> mappedudt);
this.mappingContext.setCustomConversions(
new CassandraCustomConversions(Collections.singletonList(HumanToStringConverter.INSTANCE)));
CassandraPersistentEntity<?> persistentEntity = this.mappingContext
.getRequiredPersistentEntity(WithMapOfMixedTypes.class);
CreateTableSpecification tableSpecification = this.mappingContext.getCreateTableSpecificationFor(persistentEntity);
assertThat(tableSpecification.getColumns()).hasSize(2);
ColumnSpecification column = tableSpecification.getColumns().get(1);
assertThat(column.getType().asCql(true, true)).isEqualTo("map<frozen<mappedudt>, list<text>>");
}
@Table
private static class PrimaryKeyOnPropertyWithPrimaryKeyClass {
@@ -330,82 +248,6 @@ public class CassandraMappingContextUnitTests {
@PrimaryKey PrimaryKeyWithOrderedClusteredColumns key;
}
@Test // DATACASS-213
public void createIndexShouldConsiderAnnotatedProperties() {
List<CreateIndexSpecification> specifications = mappingContext
.getCreateIndexSpecificationsFor(mappingContext.getRequiredPersistentEntity(IndexedType.class));
CreateIndexSpecification firstname = getSpecificationFor("first_name", specifications);
assertThat(firstname.getColumnName()).isEqualTo(CqlIdentifier.fromCql("first_name"));
assertThat(firstname.getTableName()).isEqualTo(CqlIdentifier.fromCql("indexedtype"));
assertThat(firstname.getName()).isEqualTo(CqlIdentifier.fromCql("my_index"));
assertThat(firstname.getColumnFunction()).isEqualTo(ColumnFunction.NONE);
CreateIndexSpecification phoneNumbers = getSpecificationFor("phoneNumbers", specifications);
assertThat(phoneNumbers.getColumnName()).isEqualTo(CqlIdentifier.fromCql("phoneNumbers"));
assertThat(phoneNumbers.getTableName()).isEqualTo(CqlIdentifier.fromCql("indexedtype"));
assertThat(phoneNumbers.getName()).isNull();
assertThat(phoneNumbers.getColumnFunction()).isEqualTo(ColumnFunction.NONE);
}
@Test // DATACASS-213
public void createIndexForClusteredPrimaryKeyShouldConsiderAnnotatedAccessors() {
List<CreateIndexSpecification> specifications = mappingContext
.getCreateIndexSpecificationsFor(mappingContext.getRequiredPersistentEntity(CompositeKeyEntity.class));
CreateIndexSpecification entries = getSpecificationFor("last_name", specifications);
assertThat(entries.getColumnName()).isEqualTo(CqlIdentifier.fromCql("last_name"));
assertThat(entries.getTableName()).isEqualTo(CqlIdentifier.fromCql("compositekeyentity"));
assertThat(entries.getName()).isEqualTo(CqlIdentifier.fromCql("my_index"));
assertThat(entries.getColumnFunction()).isEqualTo(ColumnFunction.NONE);
}
@Test // DATACASS-284, DATACASS-651
public void shouldRejectUntypedTuples() {
assertThatThrownBy(() -> this.mappingContext
.getCreateTableSpecificationFor(this.mappingContext.getRequiredPersistentEntity(UntypedTupleEntity.class)))
.isInstanceOf(MappingException.class);
assertThatThrownBy(() -> this.mappingContext
.getCreateTableSpecificationFor(this.mappingContext.getRequiredPersistentEntity(UntypedTupleMapEntity.class)))
.isInstanceOf(MappingException.class);
}
@Test // DATACASS-284
public void shouldCreateTableForTypedTupleType() {
CreateTableSpecification tableSpecification = this.mappingContext
.getCreateTableSpecificationFor(this.mappingContext.getRequiredPersistentEntity(TypedTupleEntity.class));
assertThat(tableSpecification.getColumns()).hasSize(2);
ColumnSpecification column = tableSpecification.getColumns().get(1);
assertThat(column.getType()).isInstanceOf(TupleType.class);
assertThat(column.getType()).isEqualTo(new DefaultTupleType(Arrays.asList(DataTypes.TEXT, DataTypes.BIGINT)));
}
@Test // DATACASS-651
public void shouldCreateTableForEntityWithMapOfTuples() {
CreateTableSpecification tableSpecification = this.mappingContext
.getCreateTableSpecificationFor(this.mappingContext.getRequiredPersistentEntity(EntityWithMapOfTuples.class));
assertThat(tableSpecification.getColumns()).hasSize(2);
ColumnSpecification column = tableSpecification.getColumns().get(1);
assertThat(column.getType()).isInstanceOf(MapType.class);
assertThat(column.getType())
.isEqualTo(DataTypes.mapOf(DataTypes.TEXT, new DefaultTupleType(Arrays.asList(DataTypes.TEXT))));
}
private static CreateIndexSpecification getSpecificationFor(String column,
List<CreateIndexSpecification> specifications) {
@@ -453,46 +295,6 @@ public class CassandraMappingContextUnitTests {
assertThat(mappingContext.shouldCreatePersistentEntityFor(ClassTypeInformation.from(Human.class))).isFalse();
}
@Test // DATACASS-349
public void propertyTypeShouldConsiderRegisteredConverterForPropertyType() {
mappingContext.setCustomConversions(
new CassandraCustomConversions(Collections.singletonList(StringMapToStringConverter.INSTANCE)));
CassandraPersistentEntity<?> persistentEntity = mappingContext
.getRequiredPersistentEntity(TypeWithCustomConvertedMap.class);
assertThat(mappingContext.getDataType(persistentEntity.getRequiredPersistentProperty("stringMap")))
.isEqualTo(DataTypes.TEXT);
assertThat(mappingContext.getDataType(persistentEntity.getRequiredPersistentProperty("blobMap")))
.isEqualTo(DataTypes.ASCII);
}
@Test // DATACASS-349
public void propertyTypeShouldConsiderRegisteredConverterForCollectionComponentType() {
mappingContext.setCustomConversions(
new CassandraCustomConversions(Collections.singletonList(HumanToStringConverter.INSTANCE)));
CassandraPersistentEntity<?> persistentEntity = mappingContext
.getRequiredPersistentEntity(TypeWithListOfHumans.class);
assertThat(mappingContext.getDataType(persistentEntity.getRequiredPersistentProperty("humans")))
.isEqualTo(DataTypes.listOf(DataTypes.TEXT));
}
@Test // DATACASS-302
public void propertyTypeShouldMapToTime() {
CassandraPersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(AllPossibleTypes.class);
assertThat(mappingContext.getDataType(persistentEntity.getRequiredPersistentProperty("time")))
.isEqualTo(DataTypes.TIME);
assertThat(mappingContext.getDataType(persistentEntity.getRequiredPersistentProperty("jodaLocalTime")))
.isEqualTo(DataTypes.TIME);
}
@Test // DATACASS-172, DATACASS-455
public void shouldRegisterUdtTypes() {
@@ -566,45 +368,6 @@ public class CassandraMappingContextUnitTests {
assertThat(mappingContext.usesUserType(CqlIdentifier.fromCql("mappedudt"))).isTrue();
}
@Test // DATACASS-506
public void shouldCreatedUserTypeSpecificationsWithAnnotatedTypeName() {
assertThat(
mappingContext.getCreateUserTypeSpecificationFor(mappingContext.getRequiredPersistentEntity(WithUdt.class)))
.isNotNull();
assertThat(
mappingContext.getCreateUserTypeSpecificationFor(mappingContext.getRequiredPersistentEntity(Nested.class)))
.isNotNull();
}
@Test // DATACASS-172
public void createTableForComplexPrimaryKeyShouldFail() {
try {
mappingContext.getCreateTableSpecificationFor(
mappingContext.getRequiredPersistentEntity(EntityWithComplexPrimaryKeyColumn.class));
fail("Missing MappingException");
} catch (MappingException e) {
assertThat(e).hasMessageContaining("Unknown type [class java.lang.Object] for property [complexObject]");
}
try {
mappingContext
.getCreateTableSpecificationFor(mappingContext.getRequiredPersistentEntity(EntityWithComplexId.class));
fail("Missing MappingException");
} catch (MappingException e) {
assertThat(e).hasMessageContaining("Unknown type [class java.lang.Object] for property [complexObject]");
}
try {
mappingContext.getCreateTableSpecificationFor(
mappingContext.getRequiredPersistentEntity(EntityWithPrimaryKeyClassWithComplexId.class));
fail("Missing MappingException");
} catch (MappingException e) {
assertThat(e).hasMessageContaining("Unknown type [class java.lang.Object] for property [complexObject]");
}
}
@Test // DATACASS-282, DATACASS-455
public void shouldNotRetainInvalidEntitiesInCache() {
@@ -632,26 +395,11 @@ public class CassandraMappingContextUnitTests {
@PrimaryKeyColumn String bar;
}
@Table
static class EntityWithComplexPrimaryKeyColumn {
@PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) Object complexObject;
}
@Table
static class EntityWithComplexId {
@Id Object complexObject;
}
@PrimaryKeyClass
static class PrimaryKeyClassWithComplexId {
@PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) Object complexObject;
}
@Table
static class EntityWithPrimaryKeyClassWithComplexId {
@Id PrimaryKeyClassWithComplexId primaryKeyClassWithComplexId;
}
private static class Human {}
@Table
@@ -679,12 +427,6 @@ public class CassandraMappingContextUnitTests {
@CassandraType(type = Name.UDT, userTypeName = "NestedType") Nested nested;
}
@Table
private static class WithMapOfMixedTypes {
@Id String id;
Map<MappedUdt, List<Human>> people;
}
enum HumanToStringConverter implements Converter<Human, String> {
INSTANCE;
@@ -695,63 +437,14 @@ public class CassandraMappingContextUnitTests {
}
}
@Table
private static class TypeWithCustomConvertedMap {
@Id String id;
Map<String, Collection<String>> stringMap;
@CassandraType(type = Name.ASCII) Map<String, Collection<String>> blobMap;
}
@Table
private static class TypeWithListOfHumans {
@Id String id;
List<Human> humans;
}
@WritingConverter
enum StringMapToStringConverter implements Converter<Map<String, Collection<String>>, String> {
INSTANCE;
@Override
public String convert(Map<String, Collection<String>> source) {
return "serialized";
}
}
@org.springframework.data.cassandra.core.mapping.UserDefinedType(value = "NestedType")
public static class Nested {
String s1;
@CassandraType(type = Name.UDT,
userTypeName = "AnotherNestedType") AnotherNested anotherNested;
@CassandraType(type = Name.UDT, userTypeName = "AnotherNestedType") AnotherNested anotherNested;
}
@org.springframework.data.cassandra.core.mapping.UserDefinedType(value = "AnotherNestedType")
public static class AnotherNested {
String str;
}
@Table
static class TypedTupleEntity {
@Id String id;
@CassandraType(type = Name.TUPLE, typeArguments = { Name.VARCHAR, Name.BIGINT }) TupleValue typed;
}
@Table
static class EntityWithMapOfTuples {
@Id String id;
Map<String, MappedTuple> map;
}
@Table
static class UntypedTupleEntity {
@Id String id;
TupleValue untyped;
}
@Table
static class UntypedTupleMapEntity {
@Id String id;
Map<String, TupleValue> untyped;
}
}

View File

@@ -24,7 +24,6 @@ import java.lang.annotation.Target;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.core.annotation.AliasFor;
@@ -40,8 +39,6 @@ import com.datastax.oss.driver.api.core.CqlIdentifier;
@RunWith(MockitoJUnitRunner.class)
public class CassandraUserTypePersistentEntityUnitTests {
@Mock UserTypeResolver userTypeResolverMock;
@Test // DATACASS-172
public void isUserDefinedTypeShouldReportTrue() {
@@ -83,7 +80,7 @@ public class CassandraUserTypePersistentEntityUnitTests {
}
private <T> CassandraUserTypePersistentEntity<T> getEntity(Class<T> entityClass) {
return new CassandraUserTypePersistentEntity<>(ClassTypeInformation.from(entityClass), null, userTypeResolverMock);
return new CassandraUserTypePersistentEntity<>(ClassTypeInformation.from(entityClass), null);
}
@UserDefinedType

View File

@@ -34,7 +34,6 @@ import org.springframework.data.cassandra.core.mapping.CassandraPersistentProper
import org.springframework.data.cassandra.repository.query.ConvertingParameterAccessor.PotentiallyConvertingIterator;
import com.datastax.oss.driver.api.core.type.DataTypes;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* Unit tests for {@link ConvertingParameterAccessor}.
@@ -56,15 +55,13 @@ public class ConvertingParameterAccessorUnitTests {
this.converter = new MappingCassandraConverter();
this.converter.afterPropertiesSet();
this.convertingParameterAccessor = new ConvertingParameterAccessor(converter, mockParameterAccessor,
CodecRegistry.DEFAULT);
this.convertingParameterAccessor = new ConvertingParameterAccessor(converter, mockParameterAccessor);
}
@Test // DATACASS-296
public void shouldReturnNullBindableValue() {
ConvertingParameterAccessor accessor = new ConvertingParameterAccessor(converter, mockParameterAccessor,
CodecRegistry.DEFAULT);
ConvertingParameterAccessor accessor = new ConvertingParameterAccessor(converter, mockParameterAccessor);
assertThat(accessor.getBindableValue(0)).isNull();
}
@@ -75,8 +72,7 @@ public class ConvertingParameterAccessorUnitTests {
when(mockParameterAccessor.getBindableValue(0)).thenReturn("hello");
ConvertingParameterAccessor accessor = new ConvertingParameterAccessor(converter, mockParameterAccessor,
CodecRegistry.DEFAULT);
ConvertingParameterAccessor accessor = new ConvertingParameterAccessor(converter, mockParameterAccessor);
assertThat(accessor.getBindableValue(0)).isEqualTo((Object) "hello");
}

View File

@@ -263,7 +263,7 @@ public class PartTreeCassandraQueryUnitTests {
args);
return partTreeQuery.createQuery(
new ConvertingParameterAccessor(mockCassandraOperations.getConverter(), accessor, CodecRegistry.DEFAULT));
new ConvertingParameterAccessor(mockCassandraOperations.getConverter(), accessor));
}
private PartTreeCassandraQuery createQueryForMethod(Class<?> repositoryInterface, String methodName,

View File

@@ -47,7 +47,6 @@ import org.springframework.util.ClassUtils;
import com.datastax.oss.driver.api.core.DefaultConsistencyLevel;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* Unit tests for {@link ReactivePartTreeCassandraQuery}.
@@ -177,8 +176,7 @@ public class ReactivePartTreeCassandraQueryUnitTests {
CassandraParameterAccessor accessor = new CassandraParametersParameterAccessor(partTreeQuery.getQueryMethod(),
args);
return partTreeQuery.createQuery(new ConvertingParameterAccessor(mockCassandraOperations.getConverter(), accessor,
CodecRegistry.DEFAULT));
return partTreeQuery.createQuery(new ConvertingParameterAccessor(mockCassandraOperations.getConverter(), accessor));
}
private ReactivePartTreeCassandraQuery createQueryForMethod(Class<?> repositoryInterface, String methodName,

View File

@@ -60,7 +60,6 @@ import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.data.UdtValue;
import com.datastax.oss.driver.api.core.type.DataTypes;
import com.datastax.oss.driver.api.core.type.UserDefinedType;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
/**
* Unit tests for {@link StringBasedCassandraQuery}.
@@ -327,8 +326,7 @@ public class StringBasedCassandraQueryUnitTests {
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByCreatedDate", LocalDate.class);
CassandraParameterAccessor accessor = new ConvertingParameterAccessor(converter,
new CassandraParametersParameterAccessor(cassandraQuery.getQueryMethod(), LocalDate.of(2010, 7, 4)),
CodecRegistry.DEFAULT);
new CassandraParametersParameterAccessor(cassandraQuery.getQueryMethod(), LocalDate.of(2010, 7, 4)));
SimpleStatement actual = cassandraQuery.createQuery(accessor);
@@ -347,8 +345,7 @@ public class StringBasedCassandraQueryUnitTests {
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByMainAddress", AddressType.class);
CassandraParameterAccessor accessor = new ConvertingParameterAccessor(converter,
new CassandraParametersParameterAccessor(cassandraQuery.getQueryMethod(), new AddressType()),
CodecRegistry.DEFAULT);
new CassandraParametersParameterAccessor(cassandraQuery.getQueryMethod(), new AddressType()));
SimpleStatement actual = cassandraQuery.createQuery(accessor);
@@ -360,8 +357,13 @@ public class StringBasedCassandraQueryUnitTests {
public void bindsUdtValuePropertyCorrectly() throws Exception {
StringBasedCassandraQuery cassandraQuery = getQueryMethod("findByMainAddress", UdtValue.class);
UserDefinedType addressType = UserDefinedTypeBuilder.forName("address").withField("city", DataTypes.TEXT)
.withField("country", DataTypes.TEXT).build();
when(udtValue.getType()).thenReturn(addressType);
CassandraParameterAccessor accessor = new ConvertingParameterAccessor(converter,
new CassandraParametersParameterAccessor(cassandraQuery.getQueryMethod(), udtValue), CodecRegistry.DEFAULT);
new CassandraParametersParameterAccessor(cassandraQuery.getQueryMethod(), udtValue));
SimpleStatement actual = cassandraQuery.createQuery(accessor);

View File

@@ -48,8 +48,7 @@ class StubParameterAccessor implements CassandraParameterAccessor {
* @return
*/
public static ConvertingParameterAccessor getAccessor(CassandraConverter converter, Object... parameters) {
return new ConvertingParameterAccessor(converter, new StubParameterAccessor(parameters),
CodecRegistry.DEFAULT);
return new ConvertingParameterAccessor(converter, new StubParameterAccessor(parameters));
}
@SuppressWarnings("unchecked")

View File

@@ -18,6 +18,7 @@ package org.springframework.data.cassandra.repository.support;
import java.util.Optional;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.core.convert.SchemaFactory;
import org.springframework.data.cassandra.core.cql.SessionCallback;
import org.springframework.data.cassandra.core.cql.generator.CreateTableCqlGenerator;
import org.springframework.data.cassandra.core.cql.keyspace.CreateTableSpecification;
@@ -44,6 +45,7 @@ public class SchemaTestUtils {
CassandraMappingContext mappingContext = operations.getConverter().getMappingContext();
CassandraPersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(entityClass);
SchemaFactory schemaFactory = new SchemaFactory(operations.getConverter());
operations.getCqlOperations().execute((SessionCallback<Object>) session -> {
@@ -51,7 +53,7 @@ public class SchemaTestUtils {
.flatMap(it -> it.getTable(persistentEntity.getTableName()));
if (!table.isPresent()) {
CreateTableSpecification tableSpecification = mappingContext.getCreateTableSpecificationFor(persistentEntity);
CreateTableSpecification tableSpecification = schemaFactory.getCreateTableSpecificationFor(persistentEntity);
operations.getCqlOperations().execute(new CreateTableCqlGenerator(tableSpecification).toCql());
}
return null;

View File

@@ -139,6 +139,8 @@ Paging state now uses `ByteBuffer`.
* Introduction of `StatementBuilder` to functionally build statements as the QueryBuilder API uses immutable statement types.
* `Session` bean renamed from `session` to `cassandraSession` and `SessionFactory` bean renamed from `sessionFactory` to `cassandraSessionFactory`.
* `ReactiveSession` bean renamed from `reactiveSession` to `reactiveCassandraSession` and `ReactiveSessionFactory` bean renamed from `reactiveSessionFactory` to `reactiveCassandraSessionFactory`.
* Data type resolution was moved into `ColumnTypeResolver` so all `DataType`-related methods were moved from `CassandraPersistentEntity`/`CassandraPersistentProperty` into `ColumnTypeResolver` (affected methods are `MappingContext.getDataType(…)`, `CassandraPersistentProperty.getDataType()`, `CassandraPersistentEntity.getUserType()`, and `CassandraPersistentEntity.getTupleType()`).
* Schema creation was moved from `MappingContext` to `SchemaFactory` (affected methods are `CassandraMappingContext.getCreateTableSpecificationFor(…)`, `CassandraMappingContext.getCreateIndexSpecificationsFor(…)`, and `CassandraMappingContext.getCreateUserTypeSpecificationFor(…)`).
== Deprecations
@@ -151,6 +153,8 @@ Paging state now uses `ByteBuffer`.
* Spring Data's `CqlIdentifier`, use the driver `CqlIdentifier` instead.
* `forceQuote` attributes as quoting is no longer required. `CqlIdentifier` properly escapes reserved keywords and takes care of case-sensitivity.
* `fetchSize` on `QueryOptions` and `…CqlTemplate` types was deprecated, use `pageSize` instead
* `CassandraMappingContext.setUserTypeResolver(…)`, `CassandraMappingContext.setCodecRegistry(…)`, and `CassandraMappingContext.setCustomConversions(…)`: Configure these properties on `CassandraConverter`.
* `TupleTypeFactory` and `CassandraMappingContext.setTupleTypeFactory(…)`: `TupleTypeFactory` is no longer used as the Cassandra driver ships with a `DataTypes.tupleOf(…)` factory method.
* Schema creation via `CqlSessionFactoryBean` (`cassandra:session`) is deprecated.
Keyspace creation via `CqlSessionFactoryBean` (`cassandra:session`) is not affected.