diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/ColumnReader.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/ColumnReader.java index a4c57ae6d..3cd0e8d0b 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/ColumnReader.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/ColumnReader.java @@ -19,6 +19,7 @@ import java.util.List; import org.springframework.data.cassandra.core.cql.CqlIdentifier; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; import com.datastax.driver.core.CodecRegistry; import com.datastax.driver.core.ColumnDefinitions; @@ -51,50 +52,83 @@ public class ColumnReader { * Returns the row's column value. */ @Nullable - public Object get(CqlIdentifier name) { - return get(name.toCql()); + public Object get(CqlIdentifier columnName) { + return get(columnName.toCql()); } /** * Returns the row's column value. */ @Nullable - public Object get(String name) { - return get(getColumnIndex(name)); + public Object get(String columnName) { + return get(getColumnIndex(columnName)); } /** - * Read data from a Column using the {@code index}. + * Read data from Column at the given {@code index}. * - * @param index - * @return + * @param columnIndex {@link Integer#TYPE index} of the Column. + * @return the value of the Column in at index in the Row, or {@literal null} if the Column contains no value. */ @Nullable - public Object get(int index) { + public Object get(int columnIndex) { - if (row.isNull(index)) { + if (row.isNull(columnIndex)) { return null; } - DataType type = columns.getType(index); + DataType type = columns.getType(columnIndex); if (type.isCollection()) { - return getCollection(index, type); + return getCollection(columnIndex, type); } if (Name.TUPLE.equals(type.getName())) { - return row.getTupleValue(index); + return row.getTupleValue(columnIndex); } if (Name.UDT.equals(type.getName())) { - return row.getUDTValue(index); + return row.getUDTValue(columnIndex); } - return row.getObject(index); + return row.getObject(columnIndex); + } + + /** + * Returns the row's column value as an instance of the given type. + * + * @throws ClassCastException if the value cannot be converted to the requested type. + */ + @Nullable + public T get(CqlIdentifier columnName, Class requestedType) { + return get(columnName.toCql(), requestedType); + } + + /** + * Returns the row's column value as an instance of the given type. + * + * @throws ClassCastException if the value cannot be converted to the requested type. + */ + @Nullable + public T get(String columnName, Class requestedType) { + return get(getColumnIndex(columnName), requestedType); + } + + /** + * Returns the row's column value as an instance of the given type. + * + * @throws ClassCastException if the value cannot be converted to the requested type. + */ + @Nullable + public T get(int columnIndex, Class requestedType) { + + Object value = get(columnIndex); + + return requestedType.cast(value); } @Nullable - private Object getCollection(int i, DataType type) { + private Object getCollection(int index, DataType type) { List collectionTypes = type.getTypeArguments(); @@ -102,71 +136,36 @@ public class ColumnReader { if (collectionTypes.size() == 1) { DataType valueType = collectionTypes.get(0); + TypeCodec typeCodec = codecRegistry.codecFor(valueType); + if (type.equals(DataType.list(valueType))) { - return row.getList(i, typeCodec.getJavaType().getRawType()); + return row.getList(index, typeCodec.getJavaType().getRawType()); } if (type.equals(DataType.set(valueType))) { - return row.getSet(i, typeCodec.getJavaType().getRawType()); + return row.getSet(index, typeCodec.getJavaType().getRawType()); } } // Map if (type.getName() == Name.MAP) { - return row.getObject(i); + return row.getObject(index); } - throw new IllegalStateException("Unknown Collection type encountered. Valid collections are Set, List and Map."); + throw new IllegalStateException("Unknown Collection type encountered; valid collections are List, Set and Map."); + } + + private int getColumnIndex(String columnName) { + + int index = columns.getIndexOf(columnName); + + Assert.isTrue(index > -1, String.format("Column [%s] does not exist in table", columnName)); + + return index; } public Row getRow() { return row; } - - /** - * Returns the row's column value as an instance of the given type. - * - * @throws ClassCastException if the value cannot be converted to the requested type. - */ - @Nullable - public T get(CqlIdentifier name, Class requestedType) { - return get(getColumnIndex(name.toCql()), requestedType); - } - - /** - * Returns the row's column value as an instance of the given type. - * - * @throws ClassCastException if the value cannot be converted to the requested type. - */ - @Nullable - public T get(String name, Class requestedType) { - return get(columns.getIndexOf(name), requestedType); - } - - /** - * Returns the row's column value as an instance of the given type. - * - * @throws ClassCastException if the value cannot be converted to the requested type. - */ - @Nullable - public T get(int i, Class requestedType) { - - Object o = get(i); - - if (o == null) { - return null; - } - - return requestedType.cast(o); - } - - private int getColumnIndex(String name) { - - int indexOf = columns.getIndexOf(name); - if (indexOf == -1) { - throw new IllegalArgumentException("Column does not exist in Cassandra table: " + name); - } - return indexOf; - } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java index 5b3a01b84..cf0118ec7 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java @@ -15,8 +15,6 @@ */ package org.springframework.data.cassandra.core.convert; -import lombok.AllArgsConstructor; - import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -24,8 +22,8 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.AllArgsConstructor; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.context.ApplicationContext; @@ -56,6 +54,9 @@ import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.datastax.driver.core.CodecRegistry; import com.datastax.driver.core.DataType; import com.datastax.driver.core.Row; @@ -116,6 +117,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter private static CassandraMappingContext createMappingContext() { CassandraMappingContext mappingContext = new CassandraMappingContext(); + mappingContext.setCustomConversions(new CassandraCustomConversions(Collections.emptyList())); return mappingContext; @@ -142,7 +144,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter */ @Override public CassandraMappingContext getMappingContext() { - return mappingContext; + return this.mappingContext; } /** @@ -173,8 +175,8 @@ public class MappingCassandraConverter extends AbstractCassandraConverter return getConversionService().convert(row, type); } - CassandraPersistentEntity persistentEntity = (CassandraPersistentEntity) getMappingContext() - .getRequiredPersistentEntity(typeInfo); + CassandraPersistentEntity persistentEntity = + (CassandraPersistentEntity) getMappingContext().getRequiredPersistentEntity(typeInfo); return readEntityFromRow(persistentEntity, row); } @@ -185,10 +187,11 @@ public class MappingCassandraConverter extends AbstractCassandraConverter BasicCassandraRowValueProvider rowValueProvider = new BasicCassandraRowValueProvider(row, expressionEvaluator); - PersistentEntityParameterValueProvider parameterValueProvider = new PersistentEntityParameterValueProvider<>( - entity, new MappingAndConvertingValueProvider(rowValueProvider), null); + PersistentEntityParameterValueProvider parameterValueProvider = + getParameterValueProvider(entity, rowValueProvider); EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity); + S instance = instantiator.createInstance(entity, parameterValueProvider); readPropertiesFromRow(entity, rowValueProvider, getConvertingAccessor(instance, entity)); @@ -200,13 +203,14 @@ public class MappingCassandraConverter extends AbstractCassandraConverter DefaultSpELExpressionEvaluator expressionEvaluator = new DefaultSpELExpressionEvaluator(udtValue, spELContext); - CassandraUDTValueProvider valueProvider = new CassandraUDTValueProvider(udtValue, CodecRegistry.DEFAULT_INSTANCE, - expressionEvaluator); + CassandraUDTValueProvider valueProvider = + new CassandraUDTValueProvider(udtValue, CodecRegistry.DEFAULT_INSTANCE, expressionEvaluator); - PersistentEntityParameterValueProvider parameterValueProvider = getParameterValueProvider( - entity, valueProvider); + PersistentEntityParameterValueProvider parameterValueProvider = + getParameterValueProvider(entity, valueProvider); EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity); + S instance = instantiator.createInstance(entity, parameterValueProvider); readProperties(entity, valueProvider, getConvertingAccessor(instance, entity)); @@ -216,8 +220,9 @@ public class MappingCassandraConverter extends AbstractCassandraConverter private PersistentEntityParameterValueProvider getParameterValueProvider( CassandraPersistentEntity entity, CassandraValueProvider valueProvider) { - return new PersistentEntityParameterValueProvider<>(entity, new MappingAndConvertingValueProvider(valueProvider), - null); + + return new PersistentEntityParameterValueProvider<>(entity, + new MappingAndConvertingValueProvider(valueProvider), null); } protected void readPropertiesFromRow(CassandraPersistentEntity entity, CassandraRowValueProvider row, @@ -244,7 +249,8 @@ public class MappingCassandraConverter extends AbstractCassandraConverter if (property.isCompositePrimaryKey()) { - CassandraPersistentEntity keyEntity = mappingContext.getRequiredPersistentEntity(property); + CassandraPersistentEntity keyEntity = getMappingContext().getRequiredPersistentEntity(property); + Object key = propertyAccessor.getProperty(property); if (key == null) { @@ -270,7 +276,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter protected Object instantiatePrimaryKey(CassandraPersistentEntity entity, CassandraPersistentProperty keyProperty, CassandraValueProvider propertyProvider) { - return instantiators.getInstantiatorFor(entity).createInstance(entity, + return this.instantiators.getInstantiatorFor(entity).createInstance(entity, getParameterValueProvider(entity, propertyProvider)); } @@ -314,6 +320,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter Assert.notNull(source, "Value must not be null"); Class beanClassLoaderClass = transformClassToBeanClassLoaderClass(source.getClass()); + CassandraPersistentEntity entity = getMappingContext().getRequiredPersistentEntity(beanClassLoaderClass); write(source, sink, entity); @@ -321,9 +328,11 @@ public class MappingCassandraConverter extends AbstractCassandraConverter @SuppressWarnings("unchecked") private Class transformClassToBeanClassLoaderClass(Class entity) { + try { - return (Class) ClassUtils.forName(entity.getName(), beanClassLoader); - } catch (ClassNotFoundException | LinkageError e) { + return (Class) ClassUtils.forName(entity.getName(), this.beanClassLoader); + } + catch (ClassNotFoundException | LinkageError ignore) { return entity; } } @@ -359,6 +368,47 @@ public class MappingCassandraConverter extends AbstractCassandraConverter writeInsertFromWrapper(getConvertingAccessor(object, entity), insert, entity); } + protected void writeInsertFromWrapper(ConvertingPropertyAccessor accessor, Insert insert, + CassandraPersistentEntity entity) { + + for (CassandraPersistentProperty property : entity) { + + Object value = getWriteValue(property, accessor); + + if (log.isDebugEnabled()) { + log.debug("doWithProperties Property.type {}, Property.value {}", property.getType().getName(), value); + } + + if (property.isCompositePrimaryKey()) { + + if (log.isDebugEnabled()) { + log.debug("Property is a compositeKey"); + } + + if (value == null) { + continue; + } + + CassandraPersistentEntity compositePrimaryKey = + getMappingContext().getRequiredPersistentEntity(property); + + writeInsertFromWrapper(getConvertingAccessor(value, compositePrimaryKey), insert, compositePrimaryKey); + + continue; + } + + if (value == null) { + continue; + } + + if (log.isDebugEnabled()) { + log.debug("Adding insert.value [{}] - [{}]", property.getColumnName().toCql(), value); + } + + insert.value(property.getColumnName().toCql(), value); + } + } + private void writeMapFromWrapper(ConvertingPropertyAccessor accessor, Map insert, CassandraPersistentEntity entity) { @@ -380,7 +430,9 @@ public class MappingCassandraConverter extends AbstractCassandraConverter continue; } - CassandraPersistentEntity compositePrimaryKey = mappingContext.getRequiredPersistentEntity(property); + CassandraPersistentEntity compositePrimaryKey = + getMappingContext().getRequiredPersistentEntity(property); + writeMapFromWrapper(getConvertingAccessor(value, compositePrimaryKey), insert, compositePrimaryKey); continue; @@ -394,59 +446,21 @@ public class MappingCassandraConverter extends AbstractCassandraConverter } } - protected void writeInsertFromWrapper(final ConvertingPropertyAccessor accessor, final Insert insert, + protected void writeUpdateFromObject(Object object, Update update, CassandraPersistentEntity entity) { + writeUpdateFromWrapper(getConvertingAccessor(object, entity), update, entity); + } + + protected void writeUpdateFromWrapper(ConvertingPropertyAccessor accessor, Update update, CassandraPersistentEntity entity) { for (CassandraPersistentProperty property : entity) { Object value = getWriteValue(property, accessor); - if (log.isDebugEnabled()) { - log.debug("doWithProperties Property.type {}, Property.value {}", property.getType().getName(), value); - } - if (property.isCompositePrimaryKey()) { - if (log.isDebugEnabled()) { - log.debug("Property is a compositeKey"); - } - - if (value == null) { - continue; - } - - CassandraPersistentEntity compositePrimaryKey = mappingContext.getRequiredPersistentEntity(property); - writeInsertFromWrapper(getConvertingAccessor(value, compositePrimaryKey), insert, compositePrimaryKey); - - continue; - } - - if (value == null) { - continue; - } - - if (log.isDebugEnabled()) { - log.debug("Adding insert.value [{}] - [{}]", property.getColumnName().toCql(), value); - } - - insert.value(property.getColumnName().toCql(), value); - } - } - - protected void writeUpdateFromObject(final Object object, final Update update, CassandraPersistentEntity entity) { - writeUpdateFromWrapper(getConvertingAccessor(object, entity), update, entity); - } - - protected void writeUpdateFromWrapper(final ConvertingPropertyAccessor accessor, final Update update, - final CassandraPersistentEntity entity) { - - for (CassandraPersistentProperty property : entity) { - - Object value = getWriteValue(property, accessor); - - if (property.isCompositePrimaryKey()) { - - CassandraPersistentEntity compositePrimaryKey = mappingContext.getRequiredPersistentEntity(property); + CassandraPersistentEntity compositePrimaryKey = + getMappingContext().getRequiredPersistentEntity(property); if (value == null) { continue; @@ -481,8 +495,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter Object value = getWriteValue(property, accessor); if (log.isDebugEnabled()) { - log.debug("writeUDTValueWhereFromObject Property.type {}, Property.value {}", property.getType().getName(), - value); + log.debug("writeUDTValueWhereFromObject Property.type {}, Property.value {}", property.getType().getName(), value); } if (log.isDebugEnabled()) { @@ -513,7 +526,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter if (id instanceof MapId) { CassandraPersistentEntity whereEntity = compositeIdProperty != null - ? mappingContext.getRequiredPersistentEntity(compositeIdProperty) + ? getMappingContext().getRequiredPersistentEntity(compositeIdProperty) : entity; return getWhereClauses(MapId.class.cast(id), whereEntity); @@ -531,8 +544,8 @@ public class MappingCassandraConverter extends AbstractCassandraConverter String.format("Cannot use [%s] as composite Id for [%s]", id, entity.getName())); } - CassandraPersistentEntity compositePrimaryKey = mappingContext - .getRequiredPersistentEntity(compositeIdProperty); + CassandraPersistentEntity compositePrimaryKey = + getMappingContext().getRequiredPersistentEntity(compositeIdProperty); return getWhereClauses(getConvertingAccessor(id, compositePrimaryKey), compositePrimaryKey); } @@ -557,7 +570,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter return source; } - private Collection getWhereClauses(final ConvertingPropertyAccessor accessor, + private Collection getWhereClauses(ConvertingPropertyAccessor accessor, CassandraPersistentEntity entity) { Assert.isTrue(entity.isCompositePrimaryKey(), @@ -586,7 +599,8 @@ public class MappingCassandraConverter extends AbstractCassandraConverter if (persistentProperty == null) { throw new IllegalArgumentException(String.format( - "MapId contains references [%s] that is an unknown property of [%s]", entry.getKey(), entity.getName())); + "MapId contains references [%s] that is an unknown property of [%s]", + entry.getKey(), entity.getName())); } Object writeValue = getWriteValue(entry.getValue(), persistentProperty.getTypeInformation()); @@ -647,9 +661,9 @@ public class MappingCassandraConverter extends AbstractCassandraConverter */ private ConvertingPropertyAccessor getConvertingAccessor(Object source, CassandraPersistentEntity entity) { - PersistentPropertyAccessor propertyAccessor = (source instanceof PersistentPropertyAccessor + PersistentPropertyAccessor propertyAccessor = source instanceof PersistentPropertyAccessor ? (PersistentPropertyAccessor) source - : entity.getPropertyAccessor(source)); + : entity.getPropertyAccessor(source); return new ConvertingPropertyAccessor(propertyAccessor, getConversionService()); } @@ -730,12 +744,23 @@ public class MappingCassandraConverter extends AbstractCassandraConverter Class requestedTargetType = typeInformation != null ? typeInformation.getType() : Object.class; if (getCustomConversions().hasCustomWriteTarget(value.getClass(), requestedTargetType)) { - return getConversionService().convert(value, getCustomConversions() - .getCustomWriteTarget(value.getClass(), requestedTargetType).orElse(requestedTargetType)); + + Class resolvedTargetType = getCustomConversions() + .getCustomWriteTarget(value.getClass(), requestedTargetType) + .orElse(requestedTargetType); + + return getConversionService().convert(value, resolvedTargetType); } if (getCustomConversions().hasCustomWriteTarget(value.getClass())) { - return getConversionService().convert(value, getCustomConversions().getCustomWriteTarget(value.getClass()).get()); + + Class resolvedTargetType = getCustomConversions() + .getCustomWriteTarget(value.getClass()) + .orElseThrow(() -> new IllegalStateException( + String.format("Unable to determined custom write target for value type [%s]", + value.getClass().getName()))); + + return getConversionService().convert(value, resolvedTargetType); } if (getCustomConversions().isSimpleType(value.getClass())) { @@ -743,7 +768,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter } TypeInformation type = typeInformation != null ? typeInformation - : ClassTypeInformation.from((Class) value.getClass()); + : ClassTypeInformation.from((Class) value.getClass()); if (value instanceof Collection) { return writeCollectionInternal((Collection) value, type); @@ -754,6 +779,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter } TypeInformation actualType = type.getRequiredActualType(); + BasicCassandraPersistentEntity entity = getMappingContext().getPersistentEntity(actualType.getType()); if (entity != null && entity.isUserDefinedType()) { @@ -771,6 +797,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter private Object writeCollectionInternal(Collection source, TypeInformation type) { Collection converted = CollectionFactory.createCollection(getCollectionType(type), source.size()); + TypeInformation actualType = type.getRequiredActualType(); for (Object element : source) { @@ -788,9 +815,8 @@ public class MappingCassandraConverter extends AbstractCassandraConverter TypeInformation valueType = type.getRequiredMapValueType(); for (Entry entry : source.entrySet()) { - - Object key = convertToColumnType(entry.getKey(), keyType); - converted.put(key, convertToColumnType(entry.getValue(), valueType)); + converted.put(convertToColumnType(entry.getKey(), keyType), + convertToColumnType(entry.getValue(), valueType)); } return converted; @@ -888,20 +914,18 @@ public class MappingCassandraConverter extends AbstractCassandraConverter if (property.isCompositePrimaryKey()) { - CassandraPersistentEntity keyEntity = mappingContext.getRequiredPersistentEntity(property); + CassandraPersistentEntity keyEntity = getMappingContext().getRequiredPersistentEntity(property); + return instantiatePrimaryKey(keyEntity, property, row); } Object value = row.getPropertyValue(property); - if (value == null) { - return null; - } - - return convertReadValue(value, property.getTypeInformation()); + return value == null ? null : convertReadValue(value, property.getTypeInformation()); } @Nullable + @SuppressWarnings("unchecked") private Object convertReadValue(Object value, TypeInformation typeInformation) { if (getCustomConversions().hasCustomWriteTarget(typeInformation.getRequiredActualType().getType()) @@ -911,7 +935,8 @@ public class MappingCassandraConverter extends AbstractCassandraConverter Collection original = (Collection) value; - Collection converted = CollectionFactory.createCollection(typeInformation.getType(), original.size()); + Collection converted = + CollectionFactory.createCollection(typeInformation.getType(), original.size()); for (Object element : original) { converted.add(getConversionService().convert(element, typeInformation.getRequiredActualType().getType())); @@ -929,8 +954,8 @@ public class MappingCassandraConverter extends AbstractCassandraConverter return readMapInternal((Map) value, typeInformation); } - BasicCassandraPersistentEntity persistentEntity = getMappingContext() - .getPersistentEntity(typeInformation.getRequiredActualType()); + BasicCassandraPersistentEntity persistentEntity = + getMappingContext().getPersistentEntity(typeInformation.getRequiredActualType()); if (persistentEntity != null && persistentEntity.isUserDefinedType() && value instanceof UDTValue) { return readEntityFromUdt(persistentEntity, (UDTValue) value); @@ -942,84 +967,102 @@ public class MappingCassandraConverter extends AbstractCassandraConverter /** * Reads the given {@link Collection} into a collection of the given {@link TypeInformation}. * - * @param sourceValue must not be {@literal null}. + * @param source must not be {@literal null}. * @param targetType must not be {@literal null}. * @return the converted {@link Collection} or array, will never be {@literal null}. */ @Nullable @SuppressWarnings({ "rawtypes", "unchecked" }) - private Object readCollectionOrArrayInternal(Collection sourceValue, TypeInformation targetType) { + private Object readCollectionOrArrayInternal(Collection source, TypeInformation targetType) { - Assert.notNull(targetType, "Target type must not be null!"); + Assert.notNull(targetType, "Target type must not be null"); - Class collectionType = targetType.getType(); + Class collectionType = resolveCollectionType(targetType); + Class elementType = resolveElementType(targetType); - TypeInformation componentType = targetType.getComponentType(); - Class rawComponentType = componentType != null ? componentType.getType() : List.class; + Collection collection = targetType.getType().isArray() ? new ArrayList<>() + : CollectionFactory.createCollection(collectionType, elementType, source.size()); - collectionType = Collection.class.isAssignableFrom(collectionType) ? collectionType : List.class; - Collection items = targetType.getType().isArray() ? new ArrayList<>() - : CollectionFactory.createCollection(collectionType, rawComponentType, sourceValue.size()); - - if (sourceValue.isEmpty()) { - return getPotentiallyConvertedSimpleRead(items, collectionType); + if (source.isEmpty()) { + return getPotentiallyConvertedSimpleRead(collection, collectionType); } - BasicCassandraPersistentEntity entity = getMappingContext().getPersistentEntity(rawComponentType); + BasicCassandraPersistentEntity entity = getMappingContext().getPersistentEntity(elementType); if (entity != null && entity.isUserDefinedType()) { - - for (Object udtValue : sourceValue) { - items.add(readEntityFromUdt(entity, (UDTValue) udtValue)); + for (Object udtValue : source) { + collection.add(readEntityFromUdt(entity, (UDTValue) udtValue)); } } else { - for (Object item : sourceValue) { - items.add(getPotentiallyConvertedSimpleRead(item, rawComponentType)); + for (Object element : source) { + collection.add(getPotentiallyConvertedSimpleRead(element, elementType)); } } - return getPotentiallyConvertedSimpleRead(items, targetType.getType()); + return getPotentiallyConvertedSimpleRead(collection, targetType.getType()); + } + + private Class resolveCollectionType(TypeInformation typeInformation) { + + Class collectionType = typeInformation.getType(); + + return Collection.class.isAssignableFrom(collectionType) ? collectionType : List.class; + } + + private Class resolveElementType(TypeInformation typeInformation) { + + TypeInformation componentType = typeInformation.getComponentType(); + + return componentType != null ? componentType.getType() : Object.class; } /** * Reads the given {@link Map} into a map of the given {@link TypeInformation}. * - * @param sourceValue must not be {@literal null}. + * @param source must not be {@literal null}. * @param targetType must not be {@literal null}. * @return the converted {@link Collection} or array, will never be {@literal null}. */ - @Nullable @SuppressWarnings({ "rawtypes", "unchecked" }) - private Object readMapInternal(Map sourceValue, TypeInformation targetType) { + private Object readMapInternal(Map source, TypeInformation targetType) { - Assert.notNull(targetType, "Target type must not be null!"); + Assert.notNull(targetType, "Target type must not be null"); TypeInformation keyType = targetType.getComponentType(); TypeInformation valueType = targetType.getMapValueType(); Class rawKeyType = keyType != null ? keyType.getType() : null; - Map map = CollectionFactory.createMap(targetType.getType(), rawKeyType, sourceValue.size()); - if (sourceValue.isEmpty()) { + Map map = CollectionFactory.createMap(resolveMapType(targetType), rawKeyType, source.size()); + + if (source.isEmpty()) { return map; } - for (Entry entry : sourceValue.entrySet()) { + for (Entry entry : source.entrySet()) { Object key = entry.getKey(); - if (rawKeyType != null && !rawKeyType.isAssignableFrom(key.getClass())) { + if (key != null && rawKeyType != null && !rawKeyType.isAssignableFrom(key.getClass())) { key = convertReadValue(key, keyType); } Object value = entry.getValue(); + map.put(key, convertReadValue(value, valueType)); } return map; } + private Class resolveMapType(TypeInformation typeInformation) { + + Class mapType = typeInformation.getType(); + + return Map.class.isAssignableFrom(mapType) ? mapType : Map.class; + } + private TypeCodec getCodec(CassandraPersistentProperty property) { return CodecRegistry.DEFAULT_INSTANCE.codecFor(mappingContext.getDataType(property)); } @@ -1041,7 +1084,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter */ @Override public boolean hasProperty(CassandraPersistentProperty property) { - return parent.hasProperty(property); + return this.parent.hasProperty(property); } /* (non-Javadoc) @@ -1051,7 +1094,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter @Override @SuppressWarnings("unchecked") public T getPropertyValue(CassandraPersistentProperty property) { - return (T) getReadValue(parent, property); + return (T) getReadValue(this.parent, property); } } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/UpdateMapper.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/UpdateMapper.java index 195f59a43..0e4b3c920 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/UpdateMapper.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/UpdateMapper.java @@ -127,8 +127,8 @@ public class UpdateMapper extends QueryMapper { Assert.state(op.getValue() != null, () -> String.format("SetAtKeyOp for %s attempts to set null", field.getProperty())); - Optional> typeInformation = field.getProperty() - .map(PersistentProperty::getTypeInformation); + Optional> typeInformation = + field.getProperty().map(PersistentProperty::getTypeInformation); Optional> keyType = typeInformation.map(TypeInformation::getComponentType); Optional> valueType = typeInformation.map(TypeInformation::getMapValueType); @@ -162,8 +162,8 @@ public class UpdateMapper extends QueryMapper { if (collection.isEmpty()) { - DataType.Name dataType = field.getProperty().map(property -> getMappingContext().getDataType(property)) - .map(DataType::getName).orElse(Name.LIST); + DataType.Name dataType = field.getProperty().map(property -> + getMappingContext().getDataType(property)).map(DataType::getName).orElse(Name.LIST); if (dataType == Name.SET) { return new SetOp(field.getMappedKey(), Collections.emptySet()); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraMappingContext.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraMappingContext.java index ddb2b030c..dbb2b388d 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraMappingContext.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraMappingContext.java @@ -45,7 +45,6 @@ import org.springframework.data.cassandra.core.mapping.UserTypeUtil.FrozenLitera import org.springframework.data.convert.CustomConversions; import org.springframework.data.convert.CustomConversions.StoreConversions; import org.springframework.data.mapping.MappingException; -import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.mapping.context.AbstractMappingContext; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.model.Property; @@ -78,32 +77,34 @@ public class CassandraMappingContext extends AbstractMappingContext, CassandraPersistentProperty> implements ApplicationContextAware, BeanClassLoaderAware { - private CassandraPersistentEntityMetadataVerifier verifier = new CompositeCassandraPersistentEntityMetadataVerifier(); + private @Nullable ApplicationContext applicationContext; - private CustomConversions customConversions = new CustomConversions( - StoreConversions.of(CassandraSimpleTypeHolder.HOLDER), Collections.emptyList()); + private @Nullable ClassLoader beanClassLoader; + + private CassandraPersistentEntityMetadataVerifier verifier = + new CompositeCassandraPersistentEntityMetadataVerifier(); + + private CustomConversions customConversions = + new CustomConversions(StoreConversions.of(CassandraSimpleTypeHolder.HOLDER), Collections.emptyList()); private Mapping mapping = new Mapping(); private @Nullable UserTypeResolver userTypeResolver; - private @Nullable ApplicationContext applicationContext; - - private @Nullable ClassLoader beanClassLoader; - // caches private final Map>> entitySetsByTableName = new HashMap<>(); - private final Set> userDefinedTypes = new HashSet<>(); + private final Set> tableEntities = new HashSet<>(); + private final Set> userDefinedTypes = new HashSet<>(); /** * Create a new {@link CassandraMappingContext}. */ public CassandraMappingContext() { - setCustomConversions(new CustomConversions( - StoreConversions.of(CassandraSimpleTypeHolder.HOLDER), Collections.emptyList())); + StoreConversions storeConversions = StoreConversions.of(CassandraSimpleTypeHolder.HOLDER); + setCustomConversions(new CustomConversions(storeConversions, Collections.emptyList())); setSimpleTypeHolder(CassandraSimpleTypeHolder.HOLDER); } @@ -143,7 +144,7 @@ public class CassandraMappingContext } catch (ClassNotFoundException cause) { throw new IllegalStateException( - String.format("Unknown persistent entity name [%s]", entityClassName), cause); + String.format("Unknown persistent entity type name [%s]", entityClassName), cause); } } @@ -463,13 +464,11 @@ public class CassandraMappingContext CreateUserTypeSpecification specification = CreateUserTypeSpecification.createType(entity.getTableName()); - entity.doWithProperties((PropertyHandler) property -> { - - // Use frozen literal to not resolve types from Cassandra. - // At this stage, they might be not created yet. + 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.getColumnName(), - getDataTypeWithUserTypeFactory(property, DataTypeProvider.FrozenLiteral)); - }); + getDataTypeWithUserTypeFactory(property, DataTypeProvider.FrozenLiteral)); + } if (specification.getFields().isEmpty()) { throw new MappingException(String.format("No fields in user type [%s]", entity.getType())); @@ -491,16 +490,8 @@ public class CassandraMappingContext public DataType getDataType(Class type) { return this.customConversions.getCustomWriteTarget(type) - .map(CassandraSimpleTypeHolder::getDataTypeFor) - .orElseGet(() -> getDataTypeFor(type)); - } - - @Nullable - private DataType getDataType(@NonNull Class type, DataTypeProvider dataTypeProvider) { - - BasicCassandraPersistentEntity entity = getPersistentEntity(type); - - return entity != null && entity.isUserDefinedType() ? dataTypeProvider.getDataType(entity) : getDataType(type); + .map(CassandraSimpleTypeHolder::getDataTypeFor) + .orElseGet(() -> getDataTypeFor(type)); } /** @@ -556,10 +547,11 @@ public class CassandraMappingContext return getDataTypeWithUserTypeFactory(property.getTypeInformation(), dataTypeProvider, property::getDataType); } - private DataType getDataTypeWithUserTypeFactory(TypeInformation typeInformation, DataTypeProvider dataTypeProvider, - Supplier fallback) { + private DataType getDataTypeWithUserTypeFactory(TypeInformation typeInformation, + DataTypeProvider dataTypeProvider, Supplier fallback) { - BasicCassandraPersistentEntity persistentEntity = getPersistentEntity(typeInformation.getRequiredActualType()); + BasicCassandraPersistentEntity persistentEntity = + getPersistentEntity(typeInformation.getRequiredActualType()); if (persistentEntity != null && persistentEntity.isUserDefinedType()) { @@ -570,17 +562,16 @@ public class CassandraMappingContext } } - Optional customWriteTarget = customConversions.getCustomWriteTarget(typeInformation.getType()) + Optional customWriteTarget = this.customConversions + .getCustomWriteTarget(typeInformation.getType()) .map(CassandraSimpleTypeHolder::getDataTypeFor); - DataType dataType = customWriteTarget.orElseGet(() -> { - - return customConversions.getCustomWriteTarget(typeInformation.getRequiredActualType().getType()) // - .filter(it -> !typeInformation.isMap()) // + DataType dataType = customWriteTarget.orElseGet(() -> + this.customConversions.getCustomWriteTarget(typeInformation.getRequiredActualType().getType()) + .filter(it -> !typeInformation.isMap()) .map(it -> { if (typeInformation.isCollectionLike()) { - if (List.class.isAssignableFrom(typeInformation.getType())) { return DataType.list(getDataTypeFor(it)); } @@ -591,16 +582,14 @@ public class CassandraMappingContext } return getDataTypeFor(it); - }).orElse(null); - }); - if (dataType != null) { - return dataType; - } + }).orElse(null)); - return typeInformation.isMap() ? getMapDataType(typeInformation, dataTypeProvider) : fallback.get(); + return dataType != null ? dataType + : typeInformation.isMap() ? getMapDataType(typeInformation, dataTypeProvider) : fallback.get(); } + @SuppressWarnings("all") private DataType getMapDataType(TypeInformation typeInformation, DataTypeProvider dataTypeProvider) { TypeInformation keyTypeInformation = typeInformation.getComponentType(); @@ -609,16 +598,18 @@ public class CassandraMappingContext DataType keyType = getDataTypeWithUserTypeFactory(keyTypeInformation, dataTypeProvider, () -> { DataType type = getDataTypeFor(keyTypeInformation.getType()); + if (type != null) { return type; } - throw new MappingException("Cannot resolve key type for " + typeInformation + "."); + throw new MappingException(String.format("Cannot resolve key type for [%s]", typeInformation)); }); DataType valueType = getDataTypeWithUserTypeFactory(valueTypeInformation, dataTypeProvider, () -> { DataType type = getDataTypeFor(valueTypeInformation.getType()); + if (type != null) { return type; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraSimpleTypeHolder.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraSimpleTypeHolder.java index d902ce670..70cf6f62b 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraSimpleTypeHolder.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/mapping/CassandraSimpleTypeHolder.java @@ -90,21 +90,6 @@ public class CassandraSimpleTypeHolder extends SimpleTypeHolder { super(CASSANDRA_SIMPLE_TYPES, true); } - /** - * @return the map between {@link Name} and {@link DataType}. - */ - private static Map nameToDataType() { - - Map nameToDataType = new HashMap<>(16); - - DataType.allPrimitiveTypes().forEach(dataType -> nameToDataType.put(dataType.getName(), dataType)); - - nameToDataType.put(Name.VARCHAR, DataType.varchar()); - nameToDataType.put(Name.TEXT, DataType.text()); - - return nameToDataType; - } - /** * @return the map between {@link Class} and {@link DataType}. * @param codecRegistry the Cassandra codec registry. @@ -139,6 +124,21 @@ public class CassandraSimpleTypeHolder extends SimpleTypeHolder { return classToDataType; } + /** + * @return the map between {@link Name} and {@link DataType}. + */ + private static Map nameToDataType() { + + Map nameToDataType = new HashMap<>(16); + + DataType.allPrimitiveTypes().forEach(dataType -> nameToDataType.put(dataType.getName(), dataType)); + + nameToDataType.put(Name.VARCHAR, DataType.varchar()); + nameToDataType.put(Name.TEXT, DataType.text()); + + return nameToDataType; + } + /** * Returns a {@link Set} containing all Cassandra primitive types. * @@ -147,29 +147,32 @@ public class CassandraSimpleTypeHolder extends SimpleTypeHolder { */ private static Set> getCassandraPrimitiveTypes(CodecRegistry codecRegistry) { - return DataType.allPrimitiveTypes().stream().map(codecRegistry::codecFor).map(TypeCodec::getJavaType) - .map(TypeToken::getRawType).collect(Collectors.toSet()); - } - - /** - * Returns the {@link DataType} for a {@link DataType.Name}. - * - * @param name must not be {@literal null}. - * @return the {@link DataType} for {@link DataType.Name}. - */ - public static DataType getDataTypeFor(DataType.Name name) { - return nameToDataType.get(name); + return DataType.allPrimitiveTypes().stream() + .map(codecRegistry::codecFor) + .map(TypeCodec::getJavaType) + .map(TypeToken::getRawType) + .collect(Collectors.toSet()); } /** * Returns the default {@link DataType} for a {@link Class}. This method resolves only simple types to a Cassandra * {@link DataType}. Other types are resolved to {@literal null}. * - * @param javaClass must not be {@literal null}. + * @param javaType must not be {@literal null}. * @return the {@link DataType} for {@code javaClass} if resolvable, otherwise {@literal null}. */ @Nullable - public static DataType getDataTypeFor(Class javaClass) { - return (javaClass.isEnum() ? DataType.varchar() : classToDataType.get(javaClass)); + public static DataType getDataTypeFor(Class javaType) { + return javaType.isEnum() ? DataType.varchar() : classToDataType.get(javaType); + } + + /** + * Returns the {@link DataType} for a {@link DataType.Name}. + * + * @param dataTypeName must not be {@literal null}. + * @return the {@link DataType} for {@link DataType.Name}. + */ + public static DataType getDataTypeFor(DataType.Name dataTypeName) { + return nameToDataType.get(dataTypeName); } } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/ColumnReaderUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/ColumnReaderUnitTests.java index bb3a4c80b..359a9a046 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/ColumnReaderUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/ColumnReaderUnitTests.java @@ -15,14 +15,16 @@ */ package org.springframework.data.cassandra.core.convert; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.BDDMockito.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.BDDMockito.when; 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.cassandra.core.cql.CqlIdentifier; import com.datastax.driver.core.ColumnDefinitions; @@ -52,7 +54,7 @@ public class ColumnReaderUnitTests { underTest = new ColumnReader(row); } - @Test + @Test(expected = IllegalArgumentException.class) public void throwsIllegalArgumentExceptionIfColumnDoesNotExistByName() { when(columnDefinitions.getIndexOf(NON_EXISTENT_COLUMN)).thenReturn(-1); @@ -60,34 +62,47 @@ public class ColumnReaderUnitTests { try { underTest.get(NON_EXISTENT_COLUMN); fail("Expected illegal argument exception"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).isEqualTo("Column does not exist in Cassandra table: " + NON_EXISTENT_COLUMN); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("Column [%s] does not exist in table", NON_EXISTENT_COLUMN); + assertThat(expected).hasNoCause(); + + throw expected; } } - @Test + @Test(expected = IllegalArgumentException.class) public void throwsIllegalArgumentExceptionIfColumnDoesNotExistByCqlIdentifier() { when(columnDefinitions.getIndexOf(NON_EXISTENT_COLUMN)).thenReturn(-1); try { underTest.get(CqlIdentifier.of(NON_EXISTENT_COLUMN)); - fail("Expected illegal argument exception"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).isEqualTo("Column does not exist in Cassandra table: " + NON_EXISTENT_COLUMN); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("Column [%s] does not exist in table", NON_EXISTENT_COLUMN); + assertThat(expected).hasNoCause(); + + throw expected; } } - @Test + @Test(expected = IllegalArgumentException.class) public void throwsIllegalArgumentExceptionIfColumnDoesNotExistByCqlIdentifierAndType() { when(columnDefinitions.getIndexOf(NON_EXISTENT_COLUMN)).thenReturn(-1); try { underTest.get(CqlIdentifier.of(NON_EXISTENT_COLUMN), String.class); - fail("Expected illegal argument exception"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).isEqualTo("Column does not exist in Cassandra table: " + NON_EXISTENT_COLUMN); + } + catch (IllegalArgumentException expected) { + + assertThat(expected).hasMessage("Column [%s] does not exist in table", NON_EXISTENT_COLUMN); + assertThat(expected).hasNoCause(); + + throw expected; } } } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUDTIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUDTIntegrationTests.java index 1b69e701d..24a13e3e5 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUDTIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUDTIntegrationTests.java @@ -15,11 +15,7 @@ */ package org.springframework.data.cassandra.core.convert; -import static org.assertj.core.api.Assertions.*; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.Getter; +import static org.assertj.core.api.Assertions.assertThat; import java.util.Arrays; import java.util.Collections; @@ -28,9 +24,14 @@ import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.core.convert.converter.Converter; @@ -399,11 +400,11 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring @Test // DATACASS-487 public void shouldReadUdtInMap() { - session.execute("INSERT INTO supplier (id,acceptedCurrencies) VALUES ('1'," - + "{{name:'a good one'}:[{currency:'EUR'},{currency:'USD'}]});"); + this.session.execute("INSERT INTO supplier (id, acceptedCurrencies)" + + " VALUES ('1', {{name:'a good one'}:[{currency:'EUR'},{currency:'USD'}]});"); - ResultSet resultSet = session.execute("SELECT * FROM supplier"); - Supplier supplier = converter.read(Supplier.class, resultSet.one()); + ResultSet resultSet = this.session.execute("SELECT * FROM supplier"); + Supplier supplier = this.converter.read(Supplier.class, resultSet.one()); assertThat(supplier.getAcceptedCurrencies()).isNotEmpty(); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUDTUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUDTUnitTests.java index ef9f77628..133b364af 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUDTUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUDTUnitTests.java @@ -15,12 +15,9 @@ */ package org.springframework.data.cassandra.core.convert; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; -import static org.springframework.data.cassandra.test.util.RowMockUtil.*; - -import lombok.AllArgsConstructor; -import lombok.Data; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; +import static org.springframework.data.cassandra.test.util.RowMockUtil.column; import java.util.Arrays; import java.util.Collections; @@ -28,6 +25,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import lombok.AllArgsConstructor; +import lombok.Data; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -35,6 +35,7 @@ import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; + import org.springframework.data.cassandra.core.cql.CqlIdentifier; import org.springframework.data.cassandra.core.mapping.CassandraMappingContext; import org.springframework.data.cassandra.core.mapping.UserDefinedType; @@ -55,7 +56,6 @@ import com.datastax.driver.core.querybuilder.QueryBuilder; * @author Mark Paluch */ @RunWith(MockitoJUnitRunner.Silent.class) // there are some unused stubbings in RowMockUtil but they're used in other - // tests public class MappingCassandraConverterUDTUnitTests { @Rule public final ExpectedException expectedException = ExpectedException.none(); @@ -90,16 +90,17 @@ public class MappingCassandraConverterUDTUnitTests { UDTValue value2 = currency.newValue().setString("currency", "USD"); Map> map = new HashMap<>(); + map.put(key, Arrays.asList(value1, value2)); - rowMock = RowMockUtil - .newRowMock(column("acceptedCurrencies", map, DataType.map(manufacturer, DataType.list(currency)))); + rowMock = RowMockUtil.newRowMock(column("acceptedCurrencies", map, DataType.map(manufacturer, DataType.list(currency)))); Supplier supplier = mappingCassandraConverter.read(Supplier.class, rowMock); assertThat(supplier.getAcceptedCurrencies()).isNotEmpty(); List currencies = supplier.getAcceptedCurrencies().get(new Manufacturer("a good one")); + assertThat(currencies).contains(new Currency("EUR"), new Currency("USD")); } @@ -110,7 +111,9 @@ public class MappingCassandraConverterUDTUnitTests { Arrays.asList(new Currency("EUR"), new Currency("USD"))); Supplier supplier = new Supplier(currencies); + Insert insert = QueryBuilder.insertInto("table"); + mappingCassandraConverter.write(supplier, insert); assertThat(insert.toString()).contains("VALUES ({{name:'a good one'}:[{currency:'EUR'},{currency:'USD'}]}"); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUnitTests.java index 75a9316c3..025e8cd7e 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUnitTests.java @@ -15,14 +15,10 @@ */ package org.springframework.data.cassandra.core.convert; -import static org.assertj.core.api.Assertions.*; -import static org.junit.Assume.*; -import static org.springframework.data.cassandra.core.mapping.BasicMapId.*; -import static org.springframework.data.cassandra.test.util.RowMockUtil.*; - -import lombok.AllArgsConstructor; -import lombok.RequiredArgsConstructor; -import lombok.Value; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assume.assumeTrue; +import static org.springframework.data.cassandra.core.mapping.BasicMapId.id; +import static org.springframework.data.cassandra.test.util.RowMockUtil.column; import java.io.Serializable; import java.math.BigDecimal; @@ -33,12 +29,26 @@ import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZoneOffset; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; +import lombok.Value; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; + import org.springframework.core.SpringVersion; import org.springframework.core.convert.ConverterNotFoundException; import org.springframework.data.cassandra.core.cql.PrimaryKeyType; @@ -95,10 +105,10 @@ public class MappingCassandraConverterUnitTests { @Before public void setUp() throws Exception { - mappingContext = new CassandraMappingContext(); + this.mappingContext = new CassandraMappingContext(); - mappingCassandraConverter = new MappingCassandraConverter(mappingContext); - mappingCassandraConverter.afterPropertiesSet(); + this.mappingCassandraConverter = new MappingCassandraConverter(mappingContext); + this.mappingCassandraConverter.afterPropertiesSet(); } @Test // DATACASS-260 @@ -907,15 +917,18 @@ public class MappingCassandraConverterUnitTests { LocalDate date1 = LocalDate.fromYearMonthDay(2018, 1, 1); LocalDate date2 = LocalDate.fromYearMonthDay(2019, 1, 1); + Map> times = Collections.singletonMap("Europe/Paris", Arrays.asList(date1, date2)); + rowMock = RowMockUtil.newRowMock( RowMockUtil.column("times", times, DataType.map(DataType.varchar(), DataType.list(DataType.date())))); - TypeWithConvertedMap converted = mappingCassandraConverter.read(TypeWithConvertedMap.class, rowMock); + TypeWithConvertedMap converted = this.mappingCassandraConverter.read(TypeWithConvertedMap.class, rowMock); assertThat(converted.times).containsKeys(ZoneId.of("Europe/Paris")); List convertedTimes = converted.times.get(ZoneId.of("Europe/Paris")); + assertThat(convertedTimes).hasSize(2).hasOnlyElementsOfType(java.time.LocalDate.class); } @@ -926,13 +939,15 @@ public class MappingCassandraConverterUnitTests { java.time.LocalDate date2 = java.time.LocalDate.of(2019, 1, 1); TypeWithConvertedMap typeWithConvertedMap = new TypeWithConvertedMap(); + typeWithConvertedMap.times = Collections.singletonMap(ZoneId.of("Europe/Paris"), Arrays.asList(date1, date2)); Insert insert = QueryBuilder.insertInto("table"); - mappingCassandraConverter.write(typeWithConvertedMap, insert); + this.mappingCassandraConverter.write(typeWithConvertedMap, insert); List values = getValues(insert); + assertThat(values).hasSize(1); assertThat(values.get(0)).isInstanceOf(Map.class); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/QueryMapperUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/QueryMapperUnitTests.java index aaf7d3f2f..a1e84327e 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/QueryMapperUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/QueryMapperUnitTests.java @@ -15,11 +15,9 @@ */ package org.springframework.data.cassandra.core.convert; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; - -import lombok.AllArgsConstructor; +import static org.mockito.Mockito.when; import java.util.Collection; import java.util.Collections; @@ -28,11 +26,14 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import lombok.AllArgsConstructor; + 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.Id; import org.springframework.data.cassandra.core.cql.CqlIdentifier; import org.springframework.data.cassandra.core.mapping.CassandraMappingContext; @@ -348,5 +349,4 @@ public class QueryMapperUnitTests { enum State { Active, Inactive; } - } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/UpdateMapperUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/UpdateMapperUnitTests.java index 77267ca93..8cbb438af 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/UpdateMapperUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/UpdateMapperUnitTests.java @@ -15,11 +15,8 @@ */ package org.springframework.data.cassandra.core.convert; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import lombok.AllArgsConstructor; -import lombok.Data; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; import java.util.Collections; import java.util.Currency; @@ -27,11 +24,15 @@ import java.util.List; import java.util.Map; import java.util.Set; +import lombok.AllArgsConstructor; +import lombok.Data; + 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.Id; import org.springframework.data.cassandra.core.cql.CqlIdentifier; import org.springframework.data.cassandra.core.mapping.CassandraMappingContext; @@ -96,6 +97,7 @@ public class UpdateMapperUnitTests { public void shouldReplaceUdtMap() { Manufacturer manufacturer = new Manufacturer("foobar"); + Map map = Collections.singletonMap(manufacturer, currency); Update update = updateMapper.getMappedObject(Update.empty().set("manufacturers", map), persistentEntity); @@ -126,6 +128,7 @@ public class UpdateMapperUnitTests { public void shouldCreateSetAtUdtKeyUpdate() { Manufacturer manufacturer = new Manufacturer("foobar"); + Update update = updateMapper.getMappedObject(Update.empty().set("manufacturers").atKey(manufacturer).to(currency), persistentEntity); @@ -146,6 +149,7 @@ public class UpdateMapperUnitTests { public void shouldAddUdtToMap() { Manufacturer manufacturer = new Manufacturer("foobar"); + Update update = updateMapper.getMappedObject(Update.empty().addTo("manufacturers").entry(manufacturer, currency), persistentEntity); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/mapping/CassandraMappingContextUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/mapping/CassandraMappingContextUnitTests.java index dffc191c2..09d58c1fb 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/mapping/CassandraMappingContextUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/mapping/CassandraMappingContextUnitTests.java @@ -250,16 +250,19 @@ public class CassandraMappingContextUnitTests { public void shouldCreateTableForMappedAndConvertedColumn() { UserType mappedudt = UserTypeBuilder.forName("mappedudt").withField("foo", DataType.ascii()).build(); - mappingContext.setUserTypeResolver(typeName -> mappedudt); - mappingContext.setCustomConversions( - new CassandraCustomConversions(Collections.singletonList(HumanToStringConverter.INSTANCE))); - CassandraPersistentEntity persistentEntity = mappingContext - .getRequiredPersistentEntity(WithMapOfMixedTypes.class); + this.mappingContext.setUserTypeResolver(typeName -> mappedudt); + this.mappingContext.setCustomConversions(new CassandraCustomConversions( + Collections.singletonList(HumanToStringConverter.INSTANCE))); - CreateTableSpecification tableSpecification = mappingContext.getCreateTableSpecificationFor(persistentEntity); + 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().toString()).isEqualTo("map, list>");