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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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()
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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)
|
||||
|
||||
@@ -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) + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<>();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
*/
|
||||
|
||||
@@ -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()}.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 + '}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.datastax.oss.driver.internal.core.type.DefaultTupleType;
|
||||
* @author Mark Paluch
|
||||
* @since 3.0
|
||||
*/
|
||||
|
||||
public enum SimpleTupleTypeFactory implements TupleTypeFactory {
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 {
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
@@ -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");
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user