DATACASS-506 - Resolve annotated and nested user-defined type references to stubs for UDT creation.

We now resolve user-defined type references to type stubs when constructing the create specification for a user-defined type instead of looking up the type from Cassandra if the particular property is annotated with @CassandraType(type = UDT). This applies to user-defined types nested in the to-be-created type. Stubbing is necessary to not prevent user-type creation during create specification construction. The actual execution happens after all creation specifications are built.
This commit is contained in:
Mark Paluch
2017-10-24 16:08:09 +02:00
parent c7eefe0a03
commit e685f55087
3 changed files with 74 additions and 8 deletions

View File

@@ -57,6 +57,7 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.DataType.Name;
/**
* Default implementation of a {@link MappingContext} for Cassandra using {@link CassandraPersistentEntity} and
@@ -489,6 +490,25 @@ public class CassandraMappingContext
DataTypeProvider dataTypeProvider) {
if (property.isAnnotationPresent(CassandraType.class)) {
CassandraType annotation = property.getRequiredAnnotation(CassandraType.class);
if (annotation.type() == Name.UDT) {
CqlIdentifier userTypeName = CqlIdentifier.of(annotation.userTypeName());
DataType userType = dataTypeProvider.getUserType(userTypeName, userTypeResolver);
if (userType == null) {
throw new MappingException(String.format("User type [%s] not found", userTypeName));
}
DataType dataType = getUserDataType(property, userType);
if (dataType != null) {
return dataType;
}
}
return property.getDataType();
}
@@ -496,7 +516,7 @@ public class CassandraMappingContext
if (persistentEntity != null && persistentEntity.isUserDefinedType()) {
DataType dataType = getUserDataType(property, dataTypeProvider, persistentEntity);
DataType dataType = getUserDataType(property, dataTypeProvider.getDataType(persistentEntity));
if (dataType != null) {
return dataType;
@@ -542,10 +562,7 @@ public class CassandraMappingContext
}
@Nullable
private DataType getUserDataType(CassandraPersistentProperty property, DataTypeProvider dataTypeProvider,
CassandraPersistentEntity<?> persistentEntity) {
DataType elementType = dataTypeProvider.getDataType(persistentEntity);
private DataType getUserDataType(CassandraPersistentProperty property, DataType elementType) {
if (property.isCollectionLike()) {
@@ -583,6 +600,7 @@ public class CassandraMappingContext
/**
* @author Jens Schauder
* @author Mark Paluch
* @since 1.5.1
*/
enum DataTypeProvider {
@@ -593,6 +611,11 @@ public class CassandraMappingContext
public DataType getDataType(CassandraPersistentEntity<?> entity) {
return entity.getUserType();
}
@Override
DataType getUserType(CqlIdentifier userTypeName, UserTypeResolver userTypeResolver) {
return userTypeResolver.resolveType(userTypeName);
}
},
FrozenLiteral {
@@ -601,6 +624,11 @@ public class CassandraMappingContext
public DataType getDataType(CassandraPersistentEntity<?> entity) {
return new FrozenLiteralDataType(entity.getTableName());
}
@Override
DataType getUserType(CqlIdentifier userTypeName, UserTypeResolver userTypeResolver) {
return new FrozenLiteralDataType(userTypeName);
}
};
/**
@@ -609,6 +637,18 @@ public class CassandraMappingContext
* @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(CqlIdentifier userTypeName, UserTypeResolver userTypeResolver);
}
}

View File

@@ -304,8 +304,8 @@ public class CassandraMappingContextUnitTests {
@Test // DATACASS-213
public void createIndexShouldConsiderAnnotatedProperties() {
List<CreateIndexSpecification> specifications = mappingContext.getCreateIndexSpecificationsFor(
mappingContext.getRequiredPersistentEntity(IndexedType.class));
List<CreateIndexSpecification> specifications = mappingContext
.getCreateIndexSpecificationsFor(mappingContext.getRequiredPersistentEntity(IndexedType.class));
CreateIndexSpecification firstname = getSpecificationFor("first_name", specifications);
@@ -473,6 +473,17 @@ public class CassandraMappingContextUnitTests {
assertThat(mappingContext.usesUserType(CqlIdentifier.of("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() {
@@ -573,6 +584,8 @@ public class CassandraMappingContextUnitTests {
@Id String id;
@CassandraType(type = DataType.Name.UDT, userTypeName = "mappedudt") UDTValue udtValue;
@CassandraType(type = DataType.Name.UDT, userTypeName = "NestedType") Nested nested;
}
enum HumanToStringConverter implements Converter<Human, String> {
@@ -611,4 +624,16 @@ public class CassandraMappingContextUnitTests {
return "serialized";
}
}
@UserDefinedType(value = "NestedType")
public static class Nested {
String s1;
@CassandraType(type = Name.UDT, userTypeName = "AnotherNestedType") AnotherNested anotherNested;
}
@UserDefinedType(value = "AnotherNestedType")
public static class AnotherNested {
String str;
}
}

View File

@@ -118,12 +118,13 @@ public class ConvertingParameterAccessorUnitTests {
assertThat(list.get(0)).isInstanceOf(com.datastax.driver.core.LocalDate.class);
}
@Test // DATACASS-7
@Test // DATACASS-7, DATACASS-506
@SuppressWarnings({ "rawtypes", "unchecked" })
public void shouldProvideTypeBasedOnPropertyType() {
when(mockProperty.getDataType()).thenReturn(DataType.varchar());
when(mockProperty.isAnnotationPresent(CassandraType.class)).thenReturn(true);
when(mockProperty.getRequiredAnnotation(CassandraType.class)).thenReturn(mock(CassandraType.class));
when(mockParameterAccessor.getParameterType(0)).thenReturn((Class) String.class);
assertThat(convertingParameterAccessor.getDataType(0, mockProperty)).isEqualTo(DataType.varchar());