diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/CassandraConverter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/CassandraConverter.java index 34dbd42db..0dd6fb92d 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/CassandraConverter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/CassandraConverter.java @@ -34,6 +34,13 @@ import org.springframework.data.util.TypeInformation; public interface CassandraConverter extends EntityConverter, CassandraPersistentProperty, Object, Object> { + /** + * Returns the {@link CustomConversions} registered in the {@link CassandraConverter}. + * + * @return the {@link CustomConversions}. + */ + CustomConversions getCustomConversions(); + /* (non-Javadoc) * @see org.springframework.data.convert.EntityConverter#getMappingContext() */ @@ -57,6 +64,25 @@ public interface CassandraConverter */ Object getId(Object object, CassandraPersistentEntity entity); + /** + * Converts the given object into a value Cassandra will be able to store natively in a column. + * + * @param obj {@link Object} to convert; must not be {@literal null}. + * @return the result of the conversion. + * @since 2.0 + */ + Optional convertToColumnType(Optional obj); + + /** + * Converts the given object into a value Cassandra will be able to store natively in a column. + * + * @param obj {@link Object} to convert; must not be {@literal null}. + * @param typeInformation {@link TypeInformation} used to describe the object type; may be {@literal null}. + * @return the result of the conversion. + * @since 1.5 + */ + Optional convertToColumnType(Optional obj, TypeInformation typeInformation); + /** * Converts and writes a {@code source} object into a {@code sink} using the given {@link CassandraPersistentEntity}. * @@ -66,29 +92,4 @@ public interface CassandraConverter */ void write(Object source, Object sink, CassandraPersistentEntity entity); - /** - * Converts the given object into one Cassandra will be able to store natively in a column. - * - * @param obj {@link Object} to convert, must not be {@literal null}. - * @return the result of the conversion. - * @since 2.0 - */ - Optional convertToCassandraColumn(Optional obj); - - /** - * Converts the given object into one Cassandra will be able to store natively in a column. - * - * @param obj {@link Object} to convert, must not be {@literal null}. - * @param typeInformation {@link TypeInformation} used to describe the object type; may be {@literal null}. - * @return the result of the conversion. - * @since 1.5 - */ - Optional convertToCassandraColumn(Optional obj, TypeInformation typeInformation); - - /** - * Returns the {@link CustomConversions} registered in the {@link CassandraConverter}. - * - * @return the {@link CustomConversions}. - */ - CustomConversions getCustomConversions(); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/CassandraConverters.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/CassandraConverters.java index 01736b6e7..c3678fe99 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/CassandraConverters.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/CassandraConverters.java @@ -69,6 +69,7 @@ abstract class CassandraConverters { @ReadingConverter public enum RowToBooleanConverter implements Converter { + INSTANCE; @Override @@ -84,6 +85,7 @@ abstract class CassandraConverters { */ @ReadingConverter public enum RowToDateConverter implements Converter { + INSTANCE; @Override @@ -99,6 +101,7 @@ abstract class CassandraConverters { */ @ReadingConverter public enum RowToInetAddressConverter implements Converter { + INSTANCE; @Override @@ -124,11 +127,11 @@ abstract class CassandraConverters { */ @ReadingConverter public enum RowToNumberConverterFactory implements ConverterFactory { + INSTANCE; @Override public Converter getConverter(Class targetType) { - Assert.notNull(targetType, "Target type must not be null"); return new RowToNumber<>(targetType); } @@ -158,6 +161,7 @@ abstract class CassandraConverters { */ @ReadingConverter public enum RowToStringConverter implements Converter { + INSTANCE; @Override @@ -173,6 +177,7 @@ abstract class CassandraConverters { */ @ReadingConverter public enum RowToUuidConverter implements Converter { + INSTANCE; @Override @@ -188,6 +193,7 @@ abstract class CassandraConverters { */ @ReadingConverter public enum RowToCassandraLocalDateConverter implements Converter { + INSTANCE; @Override diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/CassandraCustomConversions.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/CassandraCustomConversions.java index a4b19e180..0548d362c 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/CassandraCustomConversions.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/CassandraCustomConversions.java @@ -38,7 +38,6 @@ public class CassandraCustomConversions extends org.springframework.data.convert private static final List STORE_CONVERTERS; static { - List converters = new ArrayList<>(); converters.addAll(CassandraConverters.getConvertersToRegister()); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/MappingCassandraConverter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/MappingCassandraConverter.java index b0cc0eaea..2b91b92f6 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/MappingCassandraConverter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/MappingCassandraConverter.java @@ -15,9 +15,8 @@ */ package org.springframework.data.cassandra.convert; -import static org.springframework.data.cassandra.repository.support.BasicMapId.*; - -import lombok.AllArgsConstructor; +import static org.springframework.data.cassandra.repository.support.BasicMapId.Entry; +import static org.springframework.data.cassandra.repository.support.BasicMapId.id; import java.util.ArrayList; import java.util.Collection; @@ -25,8 +24,8 @@ import java.util.Collections; import java.util.List; import java.util.Optional; -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 +55,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; @@ -118,6 +120,15 @@ public class MappingCassandraConverter extends AbstractCassandraConverter this.spELContext = new SpELContext(RowReaderPropertyAccessor.INSTANCE); } + /* (non-Javadoc) + * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) + */ + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + this.spELContext = new SpELContext(this.spELContext, applicationContext); + } + @SuppressWarnings("unchecked") public R readRow(Class type, Row row) { @@ -139,32 +150,24 @@ 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); } - /* (non-Javadoc) - * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) - */ - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = applicationContext; - this.spELContext = new SpELContext(this.spELContext, applicationContext); - } - - protected S readEntityFromRow(final CassandraPersistentEntity entity, final Row row) { + protected S readEntityFromRow(CassandraPersistentEntity entity, Row row) { DefaultSpELExpressionEvaluator expressionEvaluator = new DefaultSpELExpressionEvaluator(row, spELContext); + BasicCassandraRowValueProvider rowValueProvider = new BasicCassandraRowValueProvider(row, expressionEvaluator); - PersistentEntityParameterValueProvider parameterProvider = new PersistentEntityParameterValueProvider<>( - entity, new MappingAndConvertingValueProvider(rowValueProvider), Optional.empty()); + PersistentEntityParameterValueProvider parameterValueProvider = + new PersistentEntityParameterValueProvider<>(entity, + new MappingAndConvertingValueProvider(rowValueProvider), Optional.empty()); EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity); - S instance = instantiator.createInstance(entity, parameterProvider); + S instance = instantiator.createInstance(entity, parameterValueProvider); readPropertiesFromRow(entity, rowValueProvider, getConvertingAccessor(instance, entity)); @@ -175,14 +178,15 @@ 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 parameterProvider = new PersistentEntityParameterValueProvider( - entity, new MappingAndConvertingValueProvider(valueProvider), Optional.empty()); + PersistentEntityParameterValueProvider parameterValueProvider = + new PersistentEntityParameterValueProvider<>(entity, + new MappingAndConvertingValueProvider(valueProvider), Optional.empty()); EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity); - S instance = instantiator.createInstance(entity, parameterProvider); + S instance = instantiator.createInstance(entity, parameterValueProvider); readProperties(entity, valueProvider, getConvertingAccessor(instance, entity)); @@ -195,11 +199,11 @@ public class MappingCassandraConverter extends AbstractCassandraConverter readProperties(entity, row, propertyAccessor); } - protected void readProperties(final CassandraPersistentEntity entity, final CassandraValueProvider valueProvider, - final PersistentPropertyAccessor propertyAccessor) { + protected void readProperties(CassandraPersistentEntity entity, CassandraValueProvider valueProvider, + PersistentPropertyAccessor propertyAccessor) { - entity.getPersistentProperties().forEach( - property -> MappingCassandraConverter.this.readProperty(entity, property, valueProvider, propertyAccessor)); + entity.getPersistentProperties().forEach(property -> + MappingCassandraConverter.this.readProperty(entity, property, valueProvider, propertyAccessor)); } protected void readProperty(CassandraPersistentEntity entity, CassandraPersistentProperty property, @@ -259,30 +263,30 @@ public class MappingCassandraConverter extends AbstractCassandraConverter } /* (non-Javadoc) - * @see org.springframework.data.cassandra.convert.CassandraConverter#convertToCassandraColumn(java.util.Optional) + * @see org.springframework.data.cassandra.convert.CassandraConverter#convertToColumnType(java.util.Optional) */ @Override @SuppressWarnings("unchecked") - public Optional convertToCassandraColumn(Optional obj) { + public Optional convertToColumnType(Optional obj) { - return convertToCassandraColumn(obj, - obj.map(Object::getClass) // - .map(ClassTypeInformation::from) // - .orElse((ClassTypeInformation) ClassTypeInformation.OBJECT)); + return convertToColumnType(obj, + obj.map(Object::getClass) + .map(ClassTypeInformation::from) + .orElse((ClassTypeInformation) ClassTypeInformation.OBJECT)); } /* (non-Javadoc) - * @see org.springframework.data.cassandra.convert.CassandraConverter#convertToCassandraColumn(java.util.Optional, org.springframework.data.util.TypeInformation) + * @see org.springframework.data.cassandra.convert.CassandraConverter#convertToColumnType(java.util.Optional, org.springframework.data.util.TypeInformation) */ @Override - public Optional convertToCassandraColumn(Optional obj, TypeInformation typeInformation) { + public Optional convertToColumnType(Optional obj, TypeInformation typeInformation) { - Assert.notNull(typeInformation, "TypeInformation must not be null!"); + Assert.notNull(typeInformation, "TypeInformation must not be null"); - return obj.flatMap(t -> { + return obj.flatMap(object -> { - if (t.getClass().isArray()) { - return Optional.of(t); + if (object.getClass().isArray()) { + return Optional.of(object); } return getWriteValue(obj, typeInformation); @@ -654,7 +658,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter * Retrieve the value from {@code value} applying the given {@link TypeInformation} and perform optionally a * conversion of collection element types. * - * @param value the value, may be {@literal null}. + * @param optional the value, may be {@literal null}. * @param typeInformation the type information. * @return the return value, may be {@literal null}. */ @@ -667,26 +671,28 @@ public class MappingCassandraConverter extends AbstractCassandraConverter I value = optional.get(); - if (getCustomConversions().isSimpleType(value.getClass())) { - // Doesn't need conversion - return getPotentiallyConvertedSimpleValue(optional, (Class) typeInformation.getType()); + Class requestedTargetType = Optional.ofNullable(typeInformation) + .map(typeInfo -> (Class) typeInfo.getType()) + .orElse(null); + + if (getCustomConversions().hasCustomWriteTarget(value.getClass(), requestedTargetType)) { + return Optional.ofNullable((O) getConversionService().convert(value, + getCustomConversions().getCustomWriteTarget(value.getClass(), requestedTargetType) + .orElse(requestedTargetType))); } if (getCustomConversions().hasCustomWriteTarget(value.getClass())) { - - return getCustomConversions().getCustomWriteTarget(value.getClass()).map(it -> { - - return getConversionService().convert(value, (Class) it); - }); + return Optional.ofNullable((O) getConversionService().convert(value, + getCustomConversions().getCustomWriteTarget(value.getClass()).get())); } if (getCustomConversions().isSimpleType(value.getClass())) { - // Doesn't need conversion - return getPotentiallyConvertedSimpleValue(optional, - typeInformation != null ? (Class) typeInformation.getType() : null); + return getPotentiallyConvertedSimpleValue(optional, requestedTargetType); } - TypeInformation type = (typeInformation != null ? typeInformation : ClassTypeInformation.from(value.getClass())); + TypeInformation type = Optional.ofNullable(typeInformation) + .orElseGet(() -> ClassTypeInformation.from((Class) value.getClass())); + TypeInformation actualType = type.getActualType(); if (value instanceof Collection) { @@ -694,14 +700,15 @@ public class MappingCassandraConverter extends AbstractCassandraConverter Collection original = (Collection) value; Collection converted = CollectionFactory.createCollection(getCollectionType(type), original.size()); - original.stream() // - .map(element -> convertToCassandraColumn(Optional.ofNullable(element), actualType).orElse(null)) + original.stream() + .map(element -> convertToColumnType(Optional.ofNullable(element), actualType).orElse(null)) .forEach(converted::add); return Optional.of((O) converted); } - Optional> optionalUdt = getMappingContext().getPersistentEntity(actualType.getType()) + Optional> optionalUdt = getMappingContext() + .getPersistentEntity(actualType.getType()) .filter(CassandraPersistentEntity::isUserDefinedType); if (optionalUdt.isPresent()) { @@ -721,8 +728,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter } /** - * Checks whether we have a custom conversion registered for the given value into an arbitrary simple Cassandra type. - * Returns the converted value if so. If not, we perform special enum handling or simply return the value as is. + * Performs special enum handling or simply returns the value as is. * * @param optionalValue may be {@literal null}. * @param requestedTargetType must not be {@literal null}. @@ -736,16 +742,8 @@ public class MappingCassandraConverter extends AbstractCassandraConverter Object value = optionalValue.get(); - if (getCustomConversions().hasCustomWriteTarget(value.getClass())) { - - return getCustomConversions().getCustomWriteTarget(value.getClass()).map(it -> { - - return getConversionService().convert(value, (Class) it); - }); - } - - // Cassandra has no default enum handling - convert it either to string - // or - if requested - to a different type + // Cassandra has no default enum handling - convert it to either a String + // or, if requested, to a different type if (Enum.class.isAssignableFrom(value.getClass())) { if (requestedTargetType != null && !requestedTargetType.isEnum() && getConversionService().canConvert(value.getClass(), requestedTargetType)) { @@ -764,9 +762,9 @@ public class MappingCassandraConverter extends AbstractCassandraConverter * Checks whether we have a custom conversion for the given simple object. Converts the given value if so, applies * {@link Enum} handling or returns the value as is. * - * @param value + * @param value simple value to convert into a value of type {@code target}. * @param target must not be {@literal null}. - * @return + * @return the converted value. */ @SuppressWarnings({ "rawtypes", "unchecked" }) private Object getPotentiallyConvertedSimpleRead(Object value, Class target) { @@ -775,15 +773,15 @@ public class MappingCassandraConverter extends AbstractCassandraConverter return value; } - if (conversions.hasCustomReadTarget(value.getClass(), target)) { - return conversionService.convert(value, target); + if (getCustomConversions().hasCustomReadTarget(value.getClass(), target)) { + return getConversionService().convert(value, target); } if (Enum.class.isAssignableFrom(target)) { return Enum.valueOf((Class) target, value.toString()); } - return conversionService.convert(value, target); + return getConversionService().convert(value, target); } private Class getCollectionType(TypeInformation type) { @@ -825,7 +823,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter return Optional.empty(); } - if (conversions.hasCustomWriteTarget(property.getActualType()) && property.isCollectionLike()) { + if (getCustomConversions().hasCustomWriteTarget(property.getActualType()) && property.isCollectionLike()) { if (obj.filter(it -> it instanceof Collection).isPresent()) { @@ -865,7 +863,6 @@ public class MappingCassandraConverter extends AbstractCassandraConverter * * @param targetType must not be {@literal null}. * @param sourceValue must not be {@literal null}. - * @param path must not be {@literal null}. * @return the converted {@link Collection} or array, will never be {@literal null}. */ @SuppressWarnings({ "rawtypes", "unchecked" }) diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/QueryMapper.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/QueryMapper.java index 7df2b7300..3648e0524 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/QueryMapper.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/QueryMapper.java @@ -33,6 +33,7 @@ import org.springframework.data.cassandra.core.query.Criteria; import org.springframework.data.cassandra.core.query.CriteriaDefinition; import org.springframework.data.cassandra.core.query.CriteriaDefinition.Predicate; import org.springframework.data.cassandra.core.query.Filter; +import org.springframework.data.cassandra.mapping.CassandraMappingContext; import org.springframework.data.cassandra.mapping.CassandraPersistentEntity; import org.springframework.data.cassandra.mapping.CassandraPersistentProperty; import org.springframework.data.domain.Sort; @@ -51,13 +52,26 @@ import org.springframework.util.Assert; * Map {@link org.springframework.data.cassandra.core.query.Query} to CQL-specific data types. * * @author Mark Paluch + * @see org.springframework.data.cassandra.core.query.ColumnName + * @see org.springframework.data.cassandra.core.query.Columns + * @see org.springframework.data.cassandra.core.query.Criteria + * @see org.springframework.data.cassandra.core.query.Filter + * @see org.springframework.data.cassandra.mapping.CassandraMappingContext + * @see org.springframework.data.cassandra.mapping.CassandraPersistentEntity + * @see org.springframework.data.cassandra.mapping.CassandraPersistentProperty + * @see org.springframework.data.domain.Sort + * @see org.springframework.data.mapping.PersistentProperty + * @see org.springframework.data.mapping.PropertyPath + * @see org.springframework.data.mapping.context.MappingContext + * @see org.springframework.data.mapping.context.PersistentPropertyPath + * @see org.springframework.data.util.TypeInformation * @since 2.0 */ public class QueryMapper { private final CassandraConverter converter; - private final MappingContext, CassandraPersistentProperty> mappingContext; + private final CassandraMappingContext mappingContext; /** * Creates a new {@link QueryMapper} with the given {@link CassandraConverter}. @@ -73,35 +87,25 @@ public class QueryMapper { } /** - * Map a {@link Filter} with a {@link CassandraPersistentEntity type hint}. Filter mapping translates property names - * to column names and maps {@link Predicate} values to simple Cassandra values. + * Returns the configured {@link CassandraConverter} used to convert object values into + * Cassandra column typed values. * - * @param filter must not be {@literal null}. - * @param entity must not be {@literal null}. - * @return the mapped {@link Filter}. + * @return the configured {@link CassandraConverter}. + * @see org.springframework.data.cassandra.convert.CassandraConverter */ - public Filter getMappedObject(Filter filter, CassandraPersistentEntity entity) { + protected CassandraConverter getConverter() { + return this.converter; + } - Assert.notNull(filter, "Filter must not be null"); - Assert.notNull(entity, "CassandraPersistentEntity must not be null"); - - List result = new ArrayList<>(); - - for (CriteriaDefinition criteriaDefinition : filter) { - - Field field = createPropertyField(entity, criteriaDefinition.getColumnName()); - - Predicate predicate = criteriaDefinition.getPredicate(); - - Optional value = Optional.ofNullable(predicate.getValue()); - TypeInformation typeInformation = getTypeInformation(field, value); - Optional mappedValue = converter.convertToCassandraColumn(value, typeInformation); - - Predicate mappedPredicate = new Predicate(predicate.getOperator(), mappedValue.orElse(null)); - result.add(Criteria.of(field.getMappedKey(), mappedPredicate)); - } - - return Filter.from(result); + /** + * Returns the configured {@link CassandraMappingContext} containing mapping meta-data (persistent entities + * and properties) used to store (map) objects to Cassandra tables (rows/columns). + * + * @return the configured {@link CassandraMappingContext}. + * @see org.springframework.data.cassandra.mapping.CassandraMappingContext + */ + protected CassandraMappingContext getMappingContext() { + return this.mappingContext; } /** @@ -113,8 +117,41 @@ public class QueryMapper { public List getColumns(CassandraPersistentEntity entity) { return entity.getPersistentProperties() // - .flatMap(p -> p.getColumnNames().stream()).map(ColumnSelector::from) // - .collect(Collectors.toList()); + .flatMap(p -> p.getColumnNames().stream()).map(ColumnSelector::from) // + .collect(Collectors.toList()); + } + + /** + * Map a {@link Filter} with a {@link CassandraPersistentEntity type hint}. Filter mapping translates property names + * to column names and maps {@link Predicate} values to simple Cassandra values. + * + * @param filter must not be {@literal null}. + * @param entity must not be {@literal null}. + * @return the mapped {@link Filter}. + */ + public Filter getMappedObject(Filter filter, CassandraPersistentEntity entity) { + + Assert.notNull(filter, "Filter must not be null"); + Assert.notNull(entity, "Entity must not be null"); + + List result = new ArrayList<>(); + + for (CriteriaDefinition criteriaDefinition : filter) { + + Field field = createPropertyField(entity, criteriaDefinition.getColumnName()); + + Predicate predicate = criteriaDefinition.getPredicate(); + + Optional value = Optional.ofNullable(predicate.getValue()); + TypeInformation typeInformation = getTypeInformation(field, value); + Optional mappedValue = getConverter().convertToColumnType(value, typeInformation); + + Predicate mappedPredicate = new Predicate(predicate.getOperator(), mappedValue.orElse(null)); + + result.add(Criteria.of(field.getMappedKey(), mappedPredicate)); + } + + return Filter.from(result); } /** @@ -171,8 +208,8 @@ public class QueryMapper { ColumnSelector mappedColumnSelector = ColumnSelector.from(cqlIdentifier); - return columnSelector.getAlias() // - .map(mappedColumnSelector::as) // + return columnSelector.getAlias() + .map(mappedColumnSelector::as) .orElse(mappedColumnSelector); } @@ -180,23 +217,23 @@ public class QueryMapper { FunctionCall functionCall = (FunctionCall) selector; - List mappedParameters = functionCall.getParameters() // - .stream() // - .map(o -> { - - if (o instanceof Selector) { - return getMappedSelector((Selector) o, cqlIdentifier); + List mappedParameters = functionCall.getParameters() + .stream() + .map(obj -> { + if (obj instanceof Selector) { + return getMappedSelector((Selector) obj, cqlIdentifier); } - return o; + return obj; }) // .collect(Collectors.toList()); - FunctionCall mappedCall = FunctionCall.from(functionCall.getExpression(), mappedParameters.toArray()); + FunctionCall mappedFunctionCall = + FunctionCall.from(functionCall.getExpression(), mappedParameters.toArray()); return functionCall.getAlias() // - .map(mappedCall::as) // - .orElse(mappedCall); + .map(mappedFunctionCall::as) // + .orElse(mappedFunctionCall); } throw new IllegalArgumentException(String.format("Selector [%s] not supported", selector)); @@ -232,11 +269,8 @@ public class QueryMapper { columns.getSelector(column) // .filter(selector -> selector instanceof ColumnSelector) // - .ifPresent(columnExpression -> { - - getCqlIdentifier(column, field) // - .map(CqlIdentifier::toCql) // - .ifPresent(columnNames::add); + .ifPresent(columnSelector -> { + getCqlIdentifier(column, field).map(CqlIdentifier::toCql).ifPresent(columnNames::add); }); } @@ -271,20 +305,21 @@ public class QueryMapper { for (Order order : sort) { ColumnName columnName = ColumnName.from(order.getProperty()); + Field field = createPropertyField(entity, columnName); Order mappedOrder = getCqlIdentifier(columnName, field) .map(cqlIdentifier -> new Order(order.getDirection(), cqlIdentifier.toCql())).orElse(order); + mappedOrders.add(mappedOrder); } - return new Sort(mappedOrders); + return Sort.by(mappedOrders); } private Optional getCqlIdentifier(ColumnName column, Field field) { try { - if (field.getProperty().isPresent()) { return field.getProperty().map(CassandraPersistentProperty::getColumnName); } @@ -306,19 +341,19 @@ public class QueryMapper { * @return */ protected Field createPropertyField(CassandraPersistentEntity entity, ColumnName key) { - return entity == null ? new Field(key) : new MetadataBackedField(key, entity, mappingContext); + return Optional.ofNullable(entity).map(e -> new MetadataBackedField(key, e, getMappingContext())) + .orElseGet(() -> new Field(key)); } @SuppressWarnings("unchecked") TypeInformation getTypeInformation(Field field, Optional value) { - return field.getProperty().map(CassandraPersistentProperty::getTypeInformation).orElseGet(() -> { - - return value.map(Object::getClass) // - .map(ClassTypeInformation::from) // - .orElse((ClassTypeInformation) ClassTypeInformation.OBJECT); - - }); + return field.getProperty().map(CassandraPersistentProperty::getTypeInformation) + .orElseGet(() -> + value.map(Object::getClass) + .map(ClassTypeInformation::from) + .orElse((ClassTypeInformation) ClassTypeInformation.OBJECT) + ); } /** @@ -336,7 +371,6 @@ public class QueryMapper { * @param name must not be {@literal null} or empty. */ public Field(ColumnName name) { - Assert.notNull(name, "Name must not be null!"); this.name = name; } @@ -345,7 +379,7 @@ public class QueryMapper { * Returns a new {@link Field} with the given name. * * @param name must not be {@literal null} or empty. - * @return + * @return a new {@link Field} with the given name. */ public Field with(ColumnName name) { return new Field(name); @@ -386,16 +420,17 @@ public class QueryMapper { private final Optional optionalProperty; /** - * Creates a new {@link MetadataBackedField} with the given name, {@link MongoPersistentEntity} and - * {@link MappingContext}. + * Creates a new {@link MetadataBackedField} with the given name, {@link CassandraPersistentEntity} + * and {@link MappingContext}. * * @param name must not be {@literal null} or empty. * @param entity must not be {@literal null}. - * @param context must not be {@literal null}. + * @param mappingContext must not be {@literal null}. */ public MetadataBackedField(ColumnName name, CassandraPersistentEntity entity, - MappingContext, CassandraPersistentProperty> context) { - this(name, entity, context, null); + MappingContext, CassandraPersistentProperty> mappingContext) { + + this(name, entity, mappingContext, null); } /** @@ -404,19 +439,19 @@ public class QueryMapper { * * @param name must not be {@literal null} or empty. * @param entity must not be {@literal null}. - * @param context must not be {@literal null}. + * @param mappingContext must not be {@literal null}. * @param property may be {@literal null}. */ public MetadataBackedField(ColumnName name, CassandraPersistentEntity entity, - MappingContext, CassandraPersistentProperty> context, + MappingContext, CassandraPersistentProperty> mappingContext, CassandraPersistentProperty property) { super(name); - Assert.notNull(entity, "MongoPersistentEntity must not be null!"); + Assert.notNull(entity, "CassandraPersistentEntity must not be null"); this.entity = entity; - this.mappingContext = context; + this.mappingContext = mappingContext; this.path = getPath(name.toCql()); this.property = path.map(PersistentPropertyPath::getLeafProperty).orElse(property); this.optionalProperty = Optional.ofNullable(this.property); @@ -431,11 +466,13 @@ public class QueryMapper { private Optional> getPath(String pathExpression) { try { - PropertyPath path = PropertyPath.from(pathExpression.replaceAll("\\.\\d", ""), entity.getTypeInformation()); - PersistentPropertyPath propertyPath = mappingContext - .getPersistentPropertyPath(path); + PropertyPath propertyPath = PropertyPath.from(pathExpression.replaceAll("\\.\\d", ""), + entity.getTypeInformation()); - return Optional.of(propertyPath); + PersistentPropertyPath persistentPropertyPath = + mappingContext.getPersistentPropertyPath(propertyPath); + + return Optional.of(persistentPropertyPath); } catch (PropertyReferenceException e) { return Optional.empty(); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/UpdateMapper.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/UpdateMapper.java index adf1ea554..fc798568c 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/UpdateMapper.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/convert/UpdateMapper.java @@ -35,7 +35,6 @@ import org.springframework.data.cassandra.core.query.Update.RemoveOp; import org.springframework.data.cassandra.core.query.Update.SetAtIndexOp; import org.springframework.data.cassandra.core.query.Update.SetAtKeyOp; import org.springframework.data.cassandra.core.query.Update.SetOp; -import org.springframework.data.cassandra.mapping.CassandraMappingContext; import org.springframework.data.cassandra.mapping.CassandraPersistentEntity; import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.util.TypeInformation; @@ -48,24 +47,22 @@ import com.datastax.driver.core.DataType.Name; * Map {@link org.springframework.data.cassandra.core.query.Update} to CQL-specific data types. * * @author Mark Paluch + * @see org.springframework.data.cassandra.core.query.Filter + * @see org.springframework.data.cassandra.core.query.Update + * @see org.springframework.data.cassandra.mapping.CassandraPersistentEntity + * @see org.springframework.data.mapping.PersistentProperty + * @see org.springframework.data.util.TypeInformation + * @since 2.0 */ public class UpdateMapper extends QueryMapper { - private final CassandraConverter converter; - - private final CassandraMappingContext mappingContext; - /** * Creates a new {@link UpdateMapper} with the given {@link CassandraConverter}. * * @param converter must not be {@literal null}. */ public UpdateMapper(CassandraConverter converter) { - super(converter); - - this.converter = converter; - this.mappingContext = converter.getMappingContext(); } /** @@ -116,30 +113,33 @@ public class UpdateMapper extends QueryMapper { return getMappedUpdateOperation(field, (AddToMapOp) assignmentOp); } - throw new IllegalArgumentException(String.format("UpdateOp %s not supported", assignmentOp)); + throw new IllegalArgumentException(String.format("UpdateOp [%s] not supported", assignmentOp)); } private AssignmentOp getMappedUpdateOperation(Field field, SetOp updateOp) { - Optional value = Optional.ofNullable(updateOp.getValue()); + Object rawValue = updateOp.getValue(); + + Optional value = Optional.ofNullable(rawValue); if (updateOp instanceof SetAtKeyOp) { SetAtKeyOp op = (SetAtKeyOp) updateOp; - Optional> typeInformation = field.getProperty() - .map(PersistentProperty::getTypeInformation); + Optional> typeInformation = + field.getProperty().map(PersistentProperty::getTypeInformation); + Optional> keyType = typeInformation.map(TypeInformation::getActualType); Optional> valueType = typeInformation.flatMap(TypeInformation::getMapValueType); - Optional k = Optional.ofNullable(op.getKey()); - Optional v = Optional.ofNullable(op.getValue()); + Optional key = Optional.ofNullable(op.getKey()); + Optional val = Optional.ofNullable(op.getValue()); - Optional mappedKey = keyType.map(it -> converter.convertToCassandraColumn(k, it)) - .orElseGet(() -> converter.convertToCassandraColumn(k)); + Optional mappedKey = keyType.map(typeInfo -> getConverter().convertToColumnType(key, typeInfo)) + .orElseGet(() -> getConverter().convertToColumnType(key)); - Optional mappedValue = valueType.map(it -> converter.convertToCassandraColumn(v, it)) - .orElseGet(() -> converter.convertToCassandraColumn(v)); + Optional mappedValue = valueType.map(typeInfo -> getConverter().convertToColumnType(val, typeInfo)) + .orElseGet(() -> getConverter().convertToColumnType(val)); return new SetAtKeyOp(field.getMappedKey(), mappedKey.orElse(null), mappedValue.orElse(null)); } @@ -150,20 +150,21 @@ public class UpdateMapper extends QueryMapper { SetAtIndexOp op = (SetAtIndexOp) updateOp; - Optional mappedValue = converter.convertToCassandraColumn(Optional.ofNullable(op.getValue()), - typeInformation); + Optional mappedValue = getConverter().convertToColumnType( + Optional.ofNullable(op.getValue()), typeInformation); + return new SetAtIndexOp(field.getMappedKey(), op.getIndex(), mappedValue.orElse(null)); } - if (updateOp.getValue() instanceof Collection && typeInformation.isCollectionLike()) { + if (rawValue instanceof Collection && typeInformation.isCollectionLike()) { - Collection collection = (Collection) updateOp.getValue(); + Collection collection = (Collection) rawValue; if (collection.isEmpty()) { - DataType.Name dataType = field.getProperty() // - .map(mappingContext::getDataType) // - .map(DataType::getName) // + DataType.Name dataType = field.getProperty() + .map(property -> getMappingContext().getDataType(property)) + .map(DataType::getName) .orElse(Name.LIST); if (dataType == Name.SET) { @@ -174,7 +175,8 @@ public class UpdateMapper extends QueryMapper { } } - Optional mappedValue = converter.convertToCassandraColumn(value, typeInformation); + Optional mappedValue = getConverter().convertToColumnType(value, typeInformation); + return new SetOp(field.getMappedKey(), mappedValue.orElse(null)); } @@ -182,8 +184,8 @@ public class UpdateMapper extends QueryMapper { Optional value = Optional.ofNullable(updateOp.getValue()); TypeInformation typeInformation = getTypeInformation(field, value); + Optional mappedValue = getConverter().convertToColumnType(value, typeInformation); - Optional mappedValue = converter.convertToCassandraColumn(value, typeInformation); return new RemoveOp(field.getMappedKey(), mappedValue.orElse(null)); } @@ -192,22 +194,20 @@ public class UpdateMapper extends QueryMapper { Optional> value = Optional.ofNullable(updateOp.getValue()); TypeInformation typeInformation = getTypeInformation(field, value); - - Collection mappedValue = (Collection) converter.convertToCassandraColumn(value, typeInformation) + Collection mappedValue = (Collection) getConverter().convertToColumnType(value, typeInformation) .orElse(null); if (field.getProperty().isPresent()) { - DataType dataType = mappingContext.getDataType(field.getProperty().get()); - if (dataType.getName() == Name.SET && !(mappedValue instanceof Set)) { + DataType dataType = getMappingContext().getDataType(field.getProperty().get()); + if (dataType.getName() == Name.SET && !(mappedValue instanceof Set)) { Collection collection = new HashSet<>(); collection.addAll(mappedValue); mappedValue = collection; } if (dataType.getName() == Name.LIST && !(mappedValue instanceof List)) { - Collection collection = new ArrayList<>(); collection.addAll(mappedValue); mappedValue = collection; @@ -219,8 +219,9 @@ public class UpdateMapper extends QueryMapper { private AssignmentOp getMappedUpdateOperation(Field field, AddToMapOp updateOp) { - Optional> typeInformation = field.getProperty() - .map(PersistentProperty::getTypeInformation); + Optional> typeInformation = + field.getProperty().map(PersistentProperty::getTypeInformation); + Optional> keyType = typeInformation.map(TypeInformation::getActualType); Optional> valueType = typeInformation.flatMap(TypeInformation::getMapValueType); @@ -231,11 +232,11 @@ public class UpdateMapper extends QueryMapper { Optional key = Optional.ofNullable(k); Optional value = Optional.ofNullable(v); - Optional mappedKey = keyType.map(it -> converter.convertToCassandraColumn(key, it)) - .orElseGet(() -> converter.convertToCassandraColumn(key)); + Optional mappedKey = keyType.map(typeInfo -> getConverter().convertToColumnType(key, typeInfo)) + .orElseGet(() -> getConverter().convertToColumnType(key)); - Optional mappedValue = valueType.map(it -> converter.convertToCassandraColumn(value, it)) - .orElseGet(() -> converter.convertToCassandraColumn(value)); + Optional mappedValue = valueType.map(typeInfo -> getConverter().convertToColumnType(value, typeInfo)) + .orElseGet(() -> getConverter().convertToColumnType(value)); result.put(mappedKey.orElse(null), mappedValue.orElse(null)); }); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraTemplate.java index 5929d557e..46d3fd93b 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/AsyncCassandraTemplate.java @@ -69,6 +69,14 @@ import com.datastax.driver.core.querybuilder.Update; * * @author Mark Paluch * @author John Blum + * @see org.springframework.cassandra.core.AsyncCqlOperations + * @see org.springframework.data.cassandra.core.AsyncCassandraOperations + * @see com.datastax.driver.core.querybuilder.Delete + * @see com.datastax.driver.core.querybuilder.Insert + * @see com.datastax.driver.core.querybuilder.QueryBuilder + * @see com.datastax.driver.core.querybuilder.Select + * @see com.datastax.driver.core.querybuilder.Truncate + * @see com.datastax.driver.core.querybuilder.Update * @since 2.0 */ public class AsyncCassandraTemplate implements AsyncCassandraOperations { @@ -321,8 +329,8 @@ public class AsyncCassandraTemplate implements AsyncCassandraOperations { Assert.notNull(query, "Query must not be null"); Assert.notNull(entityClass, "Entity type must not be null"); - return selectOne(getStatementFactory().select(query, getMappingContext().getRequiredPersistentEntity(entityClass)), - entityClass); + return selectOne(getStatementFactory().select(query, + getMappingContext().getRequiredPersistentEntity(entityClass)), entityClass); } /* (non-Javadoc) @@ -511,8 +519,8 @@ public class AsyncCassandraTemplate implements AsyncCassandraOperations { Assert.notNull(entityClass, "Entity type must not be null"); - Truncate truncate = QueryBuilder.truncate( - getMappingContext().getRequiredPersistentEntity(entityClass).getTableName().toCql()); + Truncate truncate = + QueryBuilder.truncate(getMappingContext().getRequiredPersistentEntity(entityClass).getTableName().toCql()); return new MappingListenableFutureAdapter<>(getAsyncCqlOperations().execute(truncate), aBoolean -> null); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java index b3a3addf6..c6bfb6e5d 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java @@ -142,6 +142,15 @@ public class CassandraTemplate implements CassandraOperations { this.statementFactory = new StatementFactory(new QueryMapper(converter), new UpdateMapper(converter)); } + /* (non-Javadoc) + * @see org.springframework.data.cassandra.core.CassandraOperations#getConverter() + */ + @Override + public CassandraConverter getConverter() { + return this.converter; + } + + /* (non-Javadoc) */ private static MappingCassandraConverter newConverter() { MappingCassandraConverter converter = new MappingCassandraConverter(); @@ -151,6 +160,36 @@ public class CassandraTemplate implements CassandraOperations { return converter; } + /* + * (non-Javadoc) + * @see org.springframework.data.cassandra.core.CassandraOperations#CqlOperations() + */ + @Override + public CqlOperations getCqlOperations() { + return this.cqlOperations; + } + + /** + * Returns the {@link CassandraMappingContext} used by this template to access mapping meta-data used to + * store (map) object to Cassandra tables. + * + * @return the {@link CassandraMappingContext} used by this template. + * @see org.springframework.data.cassandra.mapping.CassandraMappingContext + */ + protected CassandraMappingContext getMappingContext() { + return this.mappingContext; + } + + /** + * Returns the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements. + * + * @return the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements. + * @see org.springframework.data.cassandra.core.StatementFactory + */ + protected StatementFactory getStatementFactory() { + return this.statementFactory; + } + // ------------------------------------------------------------------------- // Methods dealing with static CQL // ------------------------------------------------------------------------- @@ -206,7 +245,7 @@ public class CassandraTemplate implements CassandraOperations { Assert.notNull(statement, "Statement must not be null"); Assert.notNull(entityClass, "Entity type must not be null"); - return cqlOperations.query(statement, (row, rowNum) -> converter.read(entityClass, row)); + return getCqlOperations().query(statement, (row, rowNum) -> getConverter().read(entityClass, row)); } /* (non-Javadoc) @@ -218,8 +257,8 @@ public class CassandraTemplate implements CassandraOperations { Assert.notNull(statement, "Statement must not be null"); Assert.notNull(entityClass, "Entity type must not be null"); - return StreamSupport.stream(cqlOperations.queryForResultSet(statement).spliterator(), false) - .map(row -> converter.read(entityClass, row)); + return StreamSupport.stream(getCqlOperations().queryForResultSet(statement).spliterator(), false) + .map(row -> getConverter().read(entityClass, row)); } /* @@ -228,10 +267,7 @@ public class CassandraTemplate implements CassandraOperations { */ @Override public T selectOne(Statement statement, Class entityClass) { - - List result = select(statement, entityClass); - - return result.stream().findFirst().orElse(null); + return select(statement, entityClass).stream().findFirst().orElse(null); } // ------------------------------------------------------------------------- @@ -247,7 +283,8 @@ public class CassandraTemplate implements CassandraOperations { Assert.notNull(query, "Query must not be null"); Assert.notNull(entityClass, "Entity type must not be null"); - return select(statementFactory.select(query, mappingContext.getRequiredPersistentEntity(entityClass)), entityClass); + return select(getStatementFactory().select(query, + getMappingContext().getRequiredPersistentEntity(entityClass)), entityClass); } /* (non-Javadoc) @@ -259,7 +296,8 @@ public class CassandraTemplate implements CassandraOperations { Assert.notNull(query, "Query must not be null"); Assert.notNull(entityClass, "Entity type must not be null"); - return stream(statementFactory.select(query, mappingContext.getRequiredPersistentEntity(entityClass)), entityClass); + return stream(getStatementFactory().select(query, + getMappingContext().getRequiredPersistentEntity(entityClass)), entityClass); } /* (non-Javadoc) @@ -284,8 +322,8 @@ public class CassandraTemplate implements CassandraOperations { Assert.notNull(update, "Update must not be null"); Assert.notNull(entityClass, "Entity type must not be null"); - return cqlOperations - .execute(statementFactory.update(query, update, mappingContext.getRequiredPersistentEntity(entityClass))); + return getCqlOperations().execute(getStatementFactory().update(query, update, + getMappingContext().getRequiredPersistentEntity(entityClass))); } /* (non-Javadoc) @@ -297,8 +335,8 @@ public class CassandraTemplate implements CassandraOperations { Assert.notNull(query, "Query must not be null"); Assert.notNull(entityClass, "Entity type must not be null"); - return cqlOperations - .execute(statementFactory.delete(query, mappingContext.getRequiredPersistentEntity(entityClass))); + return getCqlOperations().execute(getStatementFactory().delete(query, + getMappingContext().getRequiredPersistentEntity(entityClass))); } // ------------------------------------------------------------------------- @@ -315,9 +353,9 @@ public class CassandraTemplate implements CassandraOperations { Assert.notNull(entityClass, "Entity type must not be null"); Select select = QueryBuilder.select().countAll() - .from(mappingContext.getRequiredPersistentEntity(entityClass).getTableName().toCql()); + .from(getMappingContext().getRequiredPersistentEntity(entityClass).getTableName().toCql()); - return cqlOperations.queryForObject(select, Long.class); + return getCqlOperations().queryForObject(select, Long.class); } /* @@ -330,13 +368,13 @@ public class CassandraTemplate implements CassandraOperations { Assert.notNull(id, "Id must not be null"); Assert.notNull(entityClass, "Entity type must not be null"); - CassandraPersistentEntity entity = mappingContext.getRequiredPersistentEntity(entityClass); + CassandraPersistentEntity entity = getMappingContext().getRequiredPersistentEntity(entityClass); Select select = QueryBuilder.select().from(entity.getTableName().toCql()); - converter.write(id, select.where(), entity); + getConverter().write(id, select.where(), entity); - return cqlOperations.queryForResultSet(select).iterator().hasNext(); + return getCqlOperations().queryForResultSet(select).iterator().hasNext(); } /* @@ -349,11 +387,11 @@ public class CassandraTemplate implements CassandraOperations { Assert.notNull(id, "Id must not be null"); Assert.notNull(entityClass, "Entity type must not be null"); - CassandraPersistentEntity entity = mappingContext.getRequiredPersistentEntity(entityClass); + CassandraPersistentEntity entity = getMappingContext().getRequiredPersistentEntity(entityClass); Select select = QueryBuilder.select().all().from(entity.getTableName().toCql()); - converter.write(id, select.where(), entity); + getConverter().write(id, select.where(), entity); return selectOne(select, entityClass); } @@ -364,15 +402,17 @@ public class CassandraTemplate implements CassandraOperations { Assert.notNull(ids, "Ids must not be null"); Assert.notNull(entityClass, "EntityClass must not be null"); - CassandraPersistentEntity entity = mappingContext.getRequiredPersistentEntity(entityClass); + CassandraPersistentEntity entity = getMappingContext().getRequiredPersistentEntity(entityClass); - CassandraPersistentProperty idProperty = entity.getIdProperty().orElseThrow(() -> new IllegalArgumentException( - String.format("Entity class [%s] has no primary key", entityClass.getName()))); + CassandraPersistentProperty idProperty = entity.getIdProperty().orElseThrow(() -> + new IllegalArgumentException(String.format("Entity class [%s] has no primary key", + entityClass.getName()))); if (idProperty.isCompositePrimaryKey()) { String typeName = idProperty.getCompositePrimaryKeyEntity().getType().getName(); - throw new IllegalArgumentException( - String.format("Entity class [%s] uses a composite primary key class [%s] which this method can't support", + + throw new IllegalArgumentException(String.format( + "Entity class [%s] uses a composite primary key class [%s] which this method can't support", entityClass.getName(), typeName)); } @@ -403,7 +443,7 @@ public class CassandraTemplate implements CassandraOperations { Insert insert = QueryUtils.createInsertQuery(getTableName(entity.getClass()).toCql(), entity, options, converter); - return cqlOperations.execute(new StatementCallback<>(insert, entity)); + return getCqlOperations().execute(new StatementCallback<>(insert, entity)); } /* @@ -426,7 +466,7 @@ public class CassandraTemplate implements CassandraOperations { Update update = QueryUtils.createUpdateQuery(getTableName(entity.getClass()).toCql(), entity, options, converter); - return cqlOperations.execute(new StatementCallback<>(update, entity)); + return getCqlOperations().execute(new StatementCallback<>(update, entity)); } /* @@ -449,7 +489,7 @@ public class CassandraTemplate implements CassandraOperations { Delete delete = QueryUtils.createDeleteQuery(getTableName(entity.getClass()).toCql(), entity, options, converter); - return cqlOperations.execute(new StatementCallback<>(delete, entity)); + return getCqlOperations().execute(new StatementCallback<>(delete, entity)); } /* @@ -462,13 +502,13 @@ public class CassandraTemplate implements CassandraOperations { Assert.notNull(id, "Id must not be null"); Assert.notNull(entityClass, "Entity type must not be null"); - CassandraPersistentEntity entity = mappingContext.getRequiredPersistentEntity(entityClass); + CassandraPersistentEntity entity = getMappingContext().getRequiredPersistentEntity(entityClass); Delete delete = QueryBuilder.delete().from(entity.getTableName().toCql()); - converter.write(id, delete.where(), entity); + getConverter().write(id, delete.where(), entity); - return cqlOperations.execute(delete); + return getCqlOperations().execute(delete); } /* @@ -480,10 +520,10 @@ public class CassandraTemplate implements CassandraOperations { Assert.notNull(entityClass, "Entity type must not be null"); - Truncate truncate = QueryBuilder - .truncate(mappingContext.getRequiredPersistentEntity(entityClass).getTableName().toCql()); + Truncate truncate = + QueryBuilder.truncate(getMappingContext().getRequiredPersistentEntity(entityClass).getTableName().toCql()); - cqlOperations.execute(truncate); + getCqlOperations().execute(truncate); } // ------------------------------------------------------------------------- @@ -491,30 +531,13 @@ public class CassandraTemplate implements CassandraOperations { // ------------------------------------------------------------------------- /* - * (non-Javadoc) - * @see org.springframework.data.cassandra.core.CassandraOperations#getConverter() - */ - @Override - public CassandraConverter getConverter() { - return converter; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.cassandra.core.CassandraOperations#CqlOperations() - */ - @Override - public CqlOperations getCqlOperations() { - return cqlOperations; - } - /* * (non-Javadoc) * @see org.springframework.data.cassandra.core.CassandraOperationsNG#getTableName(java.lang.Class) */ @Override public CqlIdentifier getTableName(Class entityClass) { - return mappingContext.getRequiredPersistentEntity(ClassUtils.getUserClass(entityClass)).getTableName(); + return getMappingContext().getRequiredPersistentEntity(ClassUtils.getUserClass(entityClass)).getTableName(); } /* diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraTemplate.java index 6d883da95..a4a75cb8c 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/ReactiveCassandraTemplate.java @@ -66,6 +66,18 @@ import com.datastax.driver.core.querybuilder.Update; * first case given to the service directly, in the second case to the prepared template. * * @author Mark Paluch + * @author John Blum + * @see org.springframework.cassandra.core.ReactiveCqlOperations + * @see org.springframework.data.cassandra.convert.CassandraConverter + * @see org.springframework.data.cassandra.convert.QueryMapper + * @see org.springframework.data.cassandra.convert.UpdateMapper + * @see org.springframework.data.cassandra.core.ReactiveCassandraOperations + * @see com.datastax.driver.core.querybuilder.Delete + * @see com.datastax.driver.core.querybuilder.Insert + * @see com.datastax.driver.core.querybuilder.QueryBuilder + * @see com.datastax.driver.core.querybuilder.Select + * @see com.datastax.driver.core.querybuilder.Truncate + * @see com.datastax.driver.core.querybuilder.Update * @since 2.0 */ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations { @@ -147,8 +159,9 @@ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations { this.statementFactory = new StatementFactory(new QueryMapper(converter), new UpdateMapper(converter)); } - /* (non-Javadoc) - * @see org.springframework.data.cassandra.core.CassandraOperations#getConverter() + /* + * (non-Javadoc) + * @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#getConverter() */ @Override public CassandraConverter getConverter() { @@ -165,15 +178,6 @@ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations { return converter; } - /* - * (non-Javadoc) - * @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#getReactiveCqlOperations() - */ - @Override - public ReactiveCqlOperations getReactiveCqlOperations() { - return cqlOperations; - } - /** * Returns the {@link CassandraMappingContext} used by this template to access mapping meta-data used to * store (map) objects to Cassandra tables. @@ -185,6 +189,15 @@ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations { return this.mappingContext; } + /* + * (non-Javadoc) + * @see org.springframework.data.cassandra.core.ReactiveCassandraOperations#getReactiveCqlOperations() + */ + @Override + public ReactiveCqlOperations getReactiveCqlOperations() { + return this.cqlOperations; + } + /** * Returns the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements. * @@ -265,7 +278,7 @@ public class ReactiveCassandraTemplate implements ReactiveCassandraOperations { Assert.notNull(entityClass, "Entity type must not be null"); return select(getStatementFactory().select(query, - getMappingContext().getRequiredPersistentEntity(entityClass)), entityClass); + getMappingContext().getRequiredPersistentEntity(entityClass)), entityClass); } /* (non-Javadoc) diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/StatementFactory.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/StatementFactory.java index 0a44d5375..e9679715d 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/StatementFactory.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/StatementFactory.java @@ -17,6 +17,7 @@ package org.springframework.data.cassandra.core; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.Set; import org.springframework.cassandra.core.QueryOptionsUtil; @@ -62,6 +63,15 @@ import com.google.common.primitives.Ints; * Statement factory to render {@link Statement} from {@link Query} and {@link Update} objects. * * @author Mark Paluch + * @author John Blum + * @see org.springframework.data.cassandra.core.query.Query + * @see org.springframework.data.cassandra.core.query.Update + * @see com.datastax.driver.core.querybuilder.Assignment + * @see com.datastax.driver.core.querybuilder.Clause + * @see com.datastax.driver.core.querybuilder.Delete + * @see com.datastax.driver.core.querybuilder.Ordering + * @see com.datastax.driver.core.querybuilder.QueryBuilder + * @see com.datastax.driver.core.querybuilder.Select * @since 2.0 */ public class StatementFactory { @@ -94,6 +104,26 @@ public class StatementFactory { this.updateMapper = updateMapper; } + /** + * Returns the {@link QueryMapper} used to map {@link Query} to CQL-specific data types. + * + * @return the {@link QueryMapper} used to map {@link Query} to CQL-specific data types. + * @see org.springframework.data.cassandra.convert.QueryMapper + */ + protected QueryMapper getQueryMapper() { + return this.queryMapper; + } + + /** + * Returns the {@link UpdateMapper} used to map {@link Update} to CQL-specific data types. + * + * @return the {@link UpdateMapper} used to map {@link Update} to CQL-specific data types. + * @see org.springframework.data.cassandra.convert.UpdateMapper + */ + protected UpdateMapper getUpdateMapper() { + return this.updateMapper; + } + /** * Create a {@literal SELECT} statement by mapping {@link Query} to {@link Select}. * @@ -104,12 +134,15 @@ public class StatementFactory { public RegularStatement select(Query query, CassandraPersistentEntity entity) { Assert.notNull(query, "Query must not be null"); - Assert.notNull(entity, "CassandraPersistentEntity must not be null"); + Assert.notNull(entity, "Entity must not be null"); - List selectors = queryMapper.getMappedSelectors(query.getColumns(), entity); + Filter filter = getQueryMapper().getMappedObject(query, entity); - Filter filter = queryMapper.getMappedObject(query, entity); - Sort sort = query.getSort() != null ? queryMapper.getMappedSort(query.getSort(), entity) : null; + List selectors = getQueryMapper().getMappedSelectors(query.getColumns(), entity); + + Sort sort = Optional.ofNullable(query.getSort()) + .map(querySort -> getQueryMapper().getMappedSort(querySort, entity)) + .orElse(null); Select select = select(selectors, entity.getTableName(), filter, sort); @@ -135,7 +168,6 @@ public class StatementFactory { if (selectors.isEmpty()) { select = QueryBuilder.select().all().from(from.toCql()); } else { - Selection selection = QueryBuilder.select(); selectors.forEach(selector -> { selector.getAlias().map(CqlIdentifier::toCql).ifPresent(getSelection(selection, selector)::as); @@ -148,10 +180,9 @@ public class StatementFactory { } if (sort != null) { - List orderings = new ArrayList<>(); - for (Order order : sort) { + for (Order order : sort) { if (order.isAscending()) { orderings.add(QueryBuilder.asc(order.getProperty())); } else { @@ -171,13 +202,13 @@ public class StatementFactory { if (selector instanceof FunctionCall) { - Object[] objects = ((FunctionCall) selector).getParameters().stream().map(o -> { + Object[] objects = ((FunctionCall) selector).getParameters().stream().map(param -> { - if (o instanceof ColumnSelector) { - return QueryBuilder.column(((ColumnSelector) o).getExpression()); + if (param instanceof ColumnSelector) { + return QueryBuilder.column(((ColumnSelector) param).getExpression()); } - return o; + return param; }).toArray(); @@ -197,15 +228,15 @@ public class StatementFactory { public RegularStatement update(Query query, Update updateObj, CassandraPersistentEntity entity) { Assert.notNull(query, "Query must not be null"); - Assert.notNull(entity, "CassandraPersistentEntity must not be null"); + Assert.notNull(entity, "Entity must not be null"); - Update mappedUpdate = updateMapper.getMappedObject(updateObj, entity); - Filter filter = queryMapper.getMappedObject(query, entity); + Filter filter = getQueryMapper().getMappedObject(query, entity); + + Update mappedUpdate = getUpdateMapper().getMappedObject(updateObj, entity); com.datastax.driver.core.querybuilder.Update update = update(entity.getTableName(), mappedUpdate, filter); query.getQueryOptions().ifPresent(queryOptions -> { - if (queryOptions instanceof WriteOptions) { QueryOptionsUtil.addWriteOptions(update, (WriteOptions) queryOptions); } else { @@ -261,24 +292,19 @@ public class StatementFactory { private static Assignment getAssignment(IncrOp incrOp) { - if (incrOp.getValue().intValue() > 0) { - return QueryBuilder.incr(incrOp.getColumnName().toCql(), Math.abs(incrOp.getValue().intValue())); - } - - return QueryBuilder.decr(incrOp.getColumnName().toCql(), Math.abs(incrOp.getValue().intValue())); + return incrOp.getValue().intValue() > 0 + ? QueryBuilder.incr(incrOp.getColumnName().toCql(), Math.abs(incrOp.getValue().intValue())) + : QueryBuilder.decr(incrOp.getColumnName().toCql(), Math.abs(incrOp.getValue().intValue())); } private static Assignment getAssignment(SetOp updateOp) { if (updateOp instanceof SetAtIndexOp) { - SetAtIndexOp op = (SetAtIndexOp) updateOp; - return QueryBuilder.setIdx(op.getColumnName().toCql(), op.getIndex(), op.getValue()); } if (updateOp instanceof SetAtKeyOp) { - SetAtKeyOp op = (SetAtKeyOp) updateOp; return QueryBuilder.put(op.getColumnName().toCql(), op.getKey(), op.getValue()); } @@ -306,11 +332,9 @@ public class StatementFactory { return QueryBuilder.addAll(updateOp.getColumnName().toCql(), (Set) updateOp.getValue()); } - if (updateOp.getMode() == Mode.PREPEND) { - return QueryBuilder.prependAll(updateOp.getColumnName().toCql(), (List) updateOp.getValue()); - } - - return QueryBuilder.appendAll(updateOp.getColumnName().toCql(), (List) updateOp.getValue()); + return Mode.PREPEND.equals(updateOp.getMode()) + ? QueryBuilder.prependAll(updateOp.getColumnName().toCql(), (List) updateOp.getValue()) + : QueryBuilder.appendAll(updateOp.getColumnName().toCql(), (List) updateOp.getValue()); } private static Assignment getAssignment(AddToMapOp updateOp) { @@ -327,10 +351,11 @@ public class StatementFactory { public RegularStatement delete(Query query, CassandraPersistentEntity entity) { Assert.notNull(query, "Query must not be null"); - Assert.notNull(entity, "CassandraPersistentEntity must not be null"); + Assert.notNull(entity, "Entity must not be null"); - List columnNames = queryMapper.getMappedColumnNames(query.getColumns(), entity); - Filter filter = queryMapper.getMappedObject(query, entity); + Filter filter = getQueryMapper().getMappedObject(query, entity); + + List columnNames = getQueryMapper().getMappedColumnNames(query.getColumns(), entity); Delete delete = delete(columnNames, entity.getTableName(), filter); @@ -404,7 +429,7 @@ public class StatementFactory { return QueryBuilder.containsKey(columnName, predicate.getValue()); } - throw new IllegalArgumentException( - String.format("Criteria %s %s %s not supported", columnName, predicate.getOperator(), predicate.getValue())); + throw new IllegalArgumentException(String.format("Criteria %s %s %s not supported", + columnName, predicate.getOperator(), predicate.getValue())); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/ColumnName.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/ColumnName.java index 83677faf0..5169e1c2c 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/ColumnName.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/ColumnName.java @@ -29,6 +29,7 @@ import org.springframework.util.Assert; * Implementing classes must provide either {@link #getColumnName()} or {@link #getCqlIdentifier()}. * * @author Mark Paluch + * @see org.springframework.cassandra.core.cql.CqlIdentifier * @since 2.0 */ public abstract class ColumnName { @@ -84,14 +85,17 @@ public abstract class ColumnName { * @see org.springframework.data.cassandra.core.query.Criteria#equals(java.lang.Object) */ @Override - public boolean equals(Object o) { + public boolean equals(Object obj) { - if (this == o) + if (this == obj) { return true; - if (!(o instanceof ColumnName)) - return false; + } - ColumnName that = (ColumnName) o; + if (!(obj instanceof ColumnName)) { + return false; + } + + ColumnName that = (ColumnName) obj; return toCql().equals(that.toCql()); } @@ -101,7 +105,9 @@ public abstract class ColumnName { */ @Override public int hashCode() { - return 31 + toCql().hashCode(); + int hashValue = 17; + hashValue = 37 * hashValue + toCql().hashCode(); + return hashValue; } /** @@ -117,14 +123,6 @@ public abstract class ColumnName { this.columnName = columnName; } - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return columnName; - } - /* (non-Javadoc) * @see org.springframework.data.cassandra.core.query.ColumnName#getColumnName() */ @@ -148,6 +146,14 @@ public abstract class ColumnName { public String toCql() { return columnName; } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return columnName; + } } /** @@ -163,14 +169,6 @@ public abstract class ColumnName { this.cqlIdentifier = cqlIdentifier; } - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return cqlIdentifier.toString(); - } - /* (non-Javadoc) * @see org.springframework.data.cassandra.core.query.ColumnName#getColumnName() */ @@ -194,5 +192,13 @@ public abstract class ColumnName { public String toCql() { return cqlIdentifier.toCql(); } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return cqlIdentifier.toString(); + } } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Columns.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Columns.java index b1eac6bc1..e1da1ad12 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Columns.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Columns.java @@ -15,8 +15,6 @@ */ package org.springframework.data.cassandra.core.query; -import lombok.EqualsAndHashCode; - import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -27,6 +25,8 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Optional; +import lombok.EqualsAndHashCode; + import org.springframework.cassandra.core.cql.CqlIdentifier; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -37,12 +37,12 @@ import org.springframework.util.StringUtils; * included using a {@link Selector}. * * @author Mark Paluch - * @since 2.0 - * @see CqlIdentifier - * @see ColumnName + * @see org.springframework.cassandra.core.cql.CqlIdentifier + * @see org.springframework.data.cassandra.core.query.ColumnName * @see Selector - * @see ColumnSelector * @see FunctionCall + * @see ColumnSelector + * @since 2.0 */ public class Columns implements Iterable { @@ -338,14 +338,14 @@ public class Columns implements Iterable { * Create a {@link ColumnSelector} given {@link CqlIdentifier}. */ public static ColumnSelector from(CqlIdentifier columnName) { - return new ColumnSelector(ColumnName.from(columnName)); + return from(ColumnName.from(columnName)); } /** * Create a {@link ColumnSelector} given a plain {@code columnName}. */ public static ColumnSelector from(String columnName) { - return new ColumnSelector(ColumnName.from(columnName)); + return from(ColumnName.from(columnName)); } /** @@ -368,14 +368,14 @@ public class Columns implements Iterable { return new ColumnSelector(columnName, alias); } - public String getExpression() { - return columnName.toCql(); - } - public Optional getAlias() { return alias; } + public String getExpression() { + return columnName.toCql(); + } + /* (non-Javadoc) * @see java.lang.Object#toString() */ @@ -454,11 +454,11 @@ public class Columns implements Iterable { @Override public String toString() { - String params = StringUtils.collectionToDelimitedString(getParameters(), ", "); + String parameters = StringUtils.collectionToDelimitedString(getParameters(), ", "); return getAlias() - .map(cqlIdentifier -> String.format("%s(%s) AS %s", getExpression(), params, cqlIdentifier.toCql())) - .orElseGet(() -> String.format("%s(%s)", getExpression(), params)); + .map(cqlIdentifier -> String.format("%s(%s) AS %s", getExpression(), parameters, cqlIdentifier.toCql())) + .orElseGet(() -> String.format("%s(%s)", getExpression(), parameters)); } } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Criteria.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Criteria.java index 64ccb82b4..a9927e9d0 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Criteria.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Criteria.java @@ -15,7 +15,7 @@ */ package org.springframework.data.cassandra.core.query; -import static org.springframework.util.ObjectUtils.*; +import static org.springframework.util.ObjectUtils.nullSafeHashCode; import java.util.Arrays; import java.util.Collection; @@ -245,7 +245,7 @@ public class Criteria implements CriteriaDefinition { return true; } - if (obj == null || !(obj instanceof Criteria)) { + if (!(obj instanceof Criteria)) { return false; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/CriteriaDefinition.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/CriteriaDefinition.java index 33439015e..b8469271a 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/CriteriaDefinition.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/CriteriaDefinition.java @@ -98,8 +98,15 @@ public interface CriteriaDefinition { */ enum Operators implements Operator { - EQ("="), GT(">"), GTE(">="), LT("<"), LTE("<="), IN("IN"), CONTAINS("CONTAINS"), CONTAINS_KEY("CONTAINS KEY"), LIKE( - "LIKE"); + EQ("="), + GT(">"), + GTE(">="), + LT("<"), + LTE("<="), + CONTAINS("CONTAINS"), + CONTAINS_KEY("CONTAINS KEY"), + IN("IN"), + LIKE("LIKE"); private final String operator; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/DefaultFilter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/DefaultFilter.java index 5e239e529..c032ddabd 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/DefaultFilter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/DefaultFilter.java @@ -22,6 +22,7 @@ import java.util.stream.StreamSupport; * Default implementation of {@link Filter}. * * @author Mark Paluch + * @see org.springframework.data.cassandra.core.query.Filter * @since 2.0 */ class DefaultFilter implements Filter { @@ -46,7 +47,6 @@ class DefaultFilter implements Filter { */ @Override public String toString() { - return StreamSupport.stream(this.spliterator(), false) // .map(SerializationUtils::serializeToCqlSafely) // .collect(Collectors.joining(" AND ")); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Query.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Query.java index 336e42d65..974b05c8c 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Query.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Query.java @@ -15,7 +15,8 @@ */ package org.springframework.data.cassandra.core.query; -import static org.springframework.util.ObjectUtils.*; +import static org.springframework.util.ObjectUtils.nullSafeEquals; +import static org.springframework.util.ObjectUtils.nullSafeHashCode; import java.util.ArrayList; import java.util.Arrays; @@ -37,6 +38,8 @@ import com.datastax.driver.core.PagingState; * {@link QueryOptions} for a CQL query. {@link Query} is created with a fluent API creating immutable objects. * * @author Mark Paluch + * @see org.springframework.data.cassandra.core.query.Filter + * @see org.springframework.data.domain.Sort * @since 2.0 */ public class Query implements Filter { @@ -119,13 +122,15 @@ public class Query implements Filter { Assert.notNull(criteriaDefinition, "Criteria must not be null"); List criteriaDefinitions = new ArrayList<>(this.criteriaDefinitions.size() + 1); + criteriaDefinitions.addAll(this.criteriaDefinitions); if (!criteriaDefinitions.contains(criteriaDefinition)) { criteriaDefinitions.add(criteriaDefinition); } - return new Query(criteriaDefinitions, columns, sort, pagingState, queryOptions, limit, allowFiltering); + return new Query(criteriaDefinitions, this.columns, this.sort, this.pagingState, + this.queryOptions, this.limit, this.allowFiltering); } /* (non-Javadoc) @@ -147,15 +152,15 @@ public class Query implements Filter { Assert.notNull(columns, "Columns must not be null"); - return new Query(criteriaDefinitions, this.columns.and(columns), sort, pagingState, queryOptions, limit, - allowFiltering); + return new Query(this.criteriaDefinitions, this.columns.and(columns), this.sort, this.pagingState, + this.queryOptions, this.limit, this.allowFiltering); } /** * @return the query {@link Columns}. */ public Columns getColumns() { - return columns; + return this.columns; } /** @@ -170,20 +175,20 @@ public class Query implements Filter { for (Order order : sort) { if (order.isIgnoreCase()) { - throw new IllegalArgumentException(String.format("Given sort contained an Order for %s with ignore case! " - + "Apache Cassandra does not support sorting ignoring case currently!", order.getProperty())); + throw new IllegalArgumentException(String.format("Given sort contained an Order for %s with ignore case; " + + "Apache Cassandra does not support sorting ignoring case currently", order.getProperty())); } } - return new Query(criteriaDefinitions, columns, this.sort.and(sort), pagingState, queryOptions, limit, - allowFiltering); + return new Query(this.criteriaDefinitions, this.columns, this.sort.and(sort), this.pagingState, + this.queryOptions, this.limit, this.allowFiltering); } /** * @return the query {@link Sort} object. */ public Sort getSort() { - return sort; + return this.sort; } /** @@ -196,14 +201,15 @@ public class Query implements Filter { Assert.notNull(pagingState, "PagingState must not be null"); - return new Query(criteriaDefinitions, columns, sort, Optional.of(pagingState), queryOptions, limit, allowFiltering); + return new Query(this.criteriaDefinitions, this.columns, this.sort, Optional.of(pagingState), + this.queryOptions, this.limit, this.allowFiltering); } /** * @return the optional {@link PagingState}. */ public Optional getPagingState() { - return pagingState; + return this.pagingState; } /** @@ -216,14 +222,15 @@ public class Query implements Filter { Assert.notNull(queryOptions, "QueryOptions must not be null"); - return new Query(criteriaDefinitions, columns, sort, pagingState, Optional.of(queryOptions), limit, allowFiltering); + return new Query(this.criteriaDefinitions, this.columns, this.sort, this.pagingState, + Optional.of(queryOptions), this.limit, this.allowFiltering); } /** * @return the optional {@link QueryOptions}. */ public Optional getQueryOptions() { - return queryOptions; + return this.queryOptions; } /** @@ -233,7 +240,8 @@ public class Query implements Filter { * @return a new {@link Query} object containing the former settings with {@code limit} applied. */ public Query limit(long limit) { - return new Query(criteriaDefinitions, columns, sort, pagingState, queryOptions, Optional.of(limit), allowFiltering); + return new Query(this.criteriaDefinitions, this.columns, this.sort, this.pagingState, + this.queryOptions, Optional.of(limit), this.allowFiltering); } /** @@ -249,15 +257,15 @@ public class Query implements Filter { * @return a new {@link Query} object containing the former settings with {@code allowFiltering} applied. */ public Query withAllowFiltering() { - - return new Query(criteriaDefinitions, columns, sort, pagingState, queryOptions, limit, true); + return new Query(this.criteriaDefinitions, this.columns, this.sort, this.pagingState, + this.queryOptions, this.limit, true); } /** * @return {@literal true} to allow filtering. */ public boolean isAllowFiltering() { - return allowFiltering; + return this.allowFiltering; } /* (non-Javadoc) diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/SerializationUtils.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/SerializationUtils.java index 41cb0b551..97456f4d8 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/SerializationUtils.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/SerializationUtils.java @@ -30,6 +30,9 @@ import com.datastax.driver.core.TypeCodec; * Utility methods for CQL serialization. * * @author Mark Paluch + * @see org.springframework.core.convert.converter.Converter + * @see com.datastax.driver.core.CodecRegistry + * @see com.datastax.driver.core.TypeCodec * @since 2.0 */ abstract class SerializationUtils { @@ -51,7 +54,8 @@ abstract class SerializationUtils { } CriteriaDefinition.Predicate predicate = criteria.getPredicate(); - return serialize(criteria.getColumnName(), criteria.getPredicate().getOperator()) + + return serialize(criteria.getColumnName(), predicate.getOperator()) .append(serializeToCqlSafely(predicate.getValue())).toString(); } @@ -61,8 +65,8 @@ abstract class SerializationUtils { * but falling back to the given object's {@link Object#toString()} method if it's not serializable. Useful for * printing raw {@link Criteria}s containing complex values before actually converting them into Mongo native types. * - * @param criteria - * @return + * @param value value to serialize to CQL. + * @return the value as a serialized CQL {@link String}. */ public static String serializeToCqlSafely(Object value) { @@ -92,6 +96,7 @@ abstract class SerializationUtils { } TypeCodec codec = CodecRegistry.DEFAULT_INSTANCE.codecFor(value); + return codec.format(value); } @@ -103,7 +108,6 @@ abstract class SerializationUtils { } private static String toString(Map source) { - return iterableToDelimitedString(source.entrySet(), "{ ", " }", s -> String.format("%s : %s", serialize(s.getKey()), serialize(s.getValue()))); } @@ -119,9 +123,9 @@ abstract class SerializationUtils { } /** - * Creates a string representation from the given {@link Iterable} prepending the prefix, applying the given - * {@link Converter} to each element before adding it to the result {@link String}, concatenating each element with - * {@literal ,} and applying the postfix. + * Creates a {@link String} representation from the given {@link Iterable} prepending the {@code prefix}, + * applying the given {@link Converter} to each element before adding it to the result {@link String}, + * concatenating each element with {@literal ,} and applying the {@code postfix}. */ private static String iterableToDelimitedString(Iterable source, String prefix, String postfix, Converter transformer) { @@ -131,6 +135,7 @@ abstract class SerializationUtils { while (iterator.hasNext()) { builder.append(transformer.convert(iterator.next())); + if (iterator.hasNext()) { builder.append(","); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Update.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Update.java index 97d861f57..32f55c423 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Update.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/query/Update.java @@ -15,7 +15,7 @@ */ package org.springframework.data.cassandra.core.query; -import static org.springframework.data.cassandra.core.query.SerializationUtils.*; +import static org.springframework.data.cassandra.core.query.SerializationUtils.serializeToCqlSafely; import java.util.Arrays; import java.util.Collection; @@ -55,15 +55,6 @@ public class Update { return new Update(Collections.emptyMap()); } - /** - * Set the {@code columnName} to {@code value}. - * - * @return a new {@link Update}. - */ - public static Update update(String columnName, Object value) { - return empty().set(columnName, value); - } - /** * Create a {@link Update} object given a list of {@link AssignmentOp}s. * @@ -81,13 +72,22 @@ public class Update { return new Update(updateOperations); } + /** + * Set the {@code columnName} to {@code value}. + * + * @return a new {@link Update}. + */ + public static Update update(String columnName, Object value) { + return empty().set(columnName, value); + } + /** * Set the {@code columnName} to {@code value}. * * @param columnName must not be {@literal null}. - * @param value - * @return a new {@link Update} object containing the merge result of the existing assignments and the current - * assignment. + * @param value value to set on column with name. + * @return a new {@link Update} object containing the merge result of the existing assignments + * and the current assignment. */ public Update set(String columnName, Object value) { return add(new SetOp(ColumnName.from(columnName), value)); @@ -180,11 +180,11 @@ public class Update { */ public Update decrement(String columnName, Number delta) { - if (delta.doubleValue() > 0) { - return add(new IncrOp(ColumnName.from(columnName), -Math.abs(delta.doubleValue()))); - } + double deltaValue = delta.doubleValue(); - return add(new IncrOp(ColumnName.from(columnName), delta.doubleValue())); + deltaValue = deltaValue > 0 ? -Math.abs(deltaValue) : deltaValue; + + return add(new IncrOp(ColumnName.from(columnName), deltaValue)); } /** @@ -312,24 +312,34 @@ public class Update { return prependAll(Collections.singleton(value)); } + /* (non-Javadoc) + * @see org.springframework.data.cassandra.core.query.Update.AddToBuilder#prependAll(java.lang.Object[]) + */ + @Override + public Update prependAll(Object... values) { + + Assert.notNull(values, "Values must not be null"); + + return prependAll(Arrays.asList(values)); + } + + /* (non-Javadoc) + * @see org.springframework.data.cassandra.core.query.Update.AddToBuilder#prependAll(java.lang.Iterable) + */ + @Override + public Update prependAll(Iterable values) { + + Assert.notNull(values, "Values must not be null"); + + return add(new AddToOp(columnName, values, Mode.PREPEND)); + } + /* (non-Javadoc) * @see org.springframework.data.cassandra.core.query.Update.AddToBuilder#append(java.lang.Object) */ @Override public Update append(Object value) { - return prependAll(Collections.singleton(value)); - } - - /* (non-Javadoc) - * @see org.springframework.data.cassandra.core.query.Update.AddToBuilder#entry(java.lang.Object, java.lang.Object) - */ - @Override - public Update entry(Object key, Object value) { - - Assert.notNull(key, "Key must not be null"); - Assert.notNull(value, "Value must not be null"); - - return addAll(Collections.singletonMap(key, value)); + return appendAll(Collections.singleton(value)); } /* (non-Javadoc) @@ -355,25 +365,15 @@ public class Update { } /* (non-Javadoc) - * @see org.springframework.data.cassandra.core.query.Update.AddToBuilder#prependAll(java.lang.Object[]) + * @see org.springframework.data.cassandra.core.query.Update.AddToBuilder#entry(java.lang.Object, java.lang.Object) */ @Override - public Update prependAll(Object... values) { + public Update entry(Object key, Object value) { - Assert.notNull(values, "Values must not be null"); + Assert.notNull(key, "Key must not be null"); + Assert.notNull(value, "Value must not be null"); - return prependAll(Arrays.asList(values)); - } - - /* (non-Javadoc) - * @see org.springframework.data.cassandra.core.query.Update.AddToBuilder#prependAll(java.lang.Iterable) - */ - @Override - public Update prependAll(Iterable values) { - - Assert.notNull(values, "Values must not be null"); - - return add(new AddToOp(columnName, values, Mode.PREPEND)); + return addAll(Collections.singletonMap(key, value)); } /* (non-Javadoc) @@ -446,7 +446,7 @@ public class Update { */ @Override public SetValueBuilder atIndex(int index) { - return value -> add(new SetAtIndexOp(columnName, index, value)); + return value -> add(new SetAtIndexOp(this.columnName, index, value)); } /* (non-Javadoc) @@ -454,10 +454,7 @@ public class Update { */ @Override public SetValueBuilder atKey(Object key) { - - Assert.notNull(key, "Key must not be null"); - - return value -> add(new SetAtKeyOp(columnName, key, value)); + return value -> add(new SetAtKeyOp(this.columnName, key, value)); } } @@ -511,11 +508,9 @@ public class Update { @Override public String toString() { - if (mode == Mode.PREPEND) { - return String.format("%s = %s + %s", getColumnName(), serializeToCqlSafely(value), getColumnName()); - } - - return String.format("%s = %s + %s", getColumnName(), getColumnName(), serializeToCqlSafely(value)); + return Mode.PREPEND.equals(getMode()) + ? String.format("%s = %s + %s", getColumnName(), serializeToCqlSafely(value), getColumnName()) + : String.format("%s = %s + %s", getColumnName(), getColumnName(), serializeToCqlSafely(value)); } public enum Mode { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraMappingContext.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraMappingContext.java index 42792a0cc..7dcc18f8a 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraMappingContext.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraMappingContext.java @@ -15,9 +15,9 @@ */ package org.springframework.data.cassandra.mapping; -import static org.springframework.cassandra.core.cql.CqlIdentifier.*; -import static org.springframework.cassandra.core.keyspace.CreateTableSpecification.*; -import static org.springframework.data.cassandra.mapping.CassandraSimpleTypeHolder.*; +import static org.springframework.cassandra.core.cql.CqlIdentifier.cqlId; +import static org.springframework.cassandra.core.keyspace.CreateTableSpecification.createTable; +import static org.springframework.data.cassandra.mapping.CassandraSimpleTypeHolder.getDataTypeFor; import java.util.Collection; import java.util.Collections; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntity.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntity.java index b6481ca0d..82e6c8ebb 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntity.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentEntity.java @@ -15,7 +15,7 @@ */ package org.springframework.data.cassandra.mapping; -import static org.springframework.cassandra.core.cql.CqlIdentifier.*; +import static org.springframework.cassandra.core.cql.CqlIdentifier.cqlId; import java.util.ArrayList; import java.util.Comparator; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentProperty.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentProperty.java index cf46f13ad..32dcb2333 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentProperty.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/BasicCassandraPersistentProperty.java @@ -15,7 +15,7 @@ */ package org.springframework.data.cassandra.mapping; -import static org.springframework.cassandra.core.cql.CqlIdentifier.*; +import static org.springframework.cassandra.core.cql.CqlIdentifier.cqlId; import java.util.ArrayList; import java.util.Collections; diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraSimpleTypeHolder.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraSimpleTypeHolder.java index d5eef01a9..982e80932 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraSimpleTypeHolder.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/mapping/CassandraSimpleTypeHolder.java @@ -19,6 +19,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -50,10 +51,10 @@ public class CassandraSimpleTypeHolder extends SimpleTypeHolder { private static final Map nameToDataType; static { - CodecRegistry codecRegistry = CodecRegistry.DEFAULT_INSTANCE; Map, Class> primitiveWrappers = new HashMap<>(8); + primitiveWrappers.put(Boolean.class, boolean.class); primitiveWrappers.put(Byte.class, byte.class); primitiveWrappers.put(Character.class, char.class); @@ -64,11 +65,12 @@ public class CassandraSimpleTypeHolder extends SimpleTypeHolder { primitiveWrappers.put(Short.class, short.class); Set> simpleTypes = getCassandraPrimitiveTypes(codecRegistry); + simpleTypes.add(Number.class); simpleTypes.add(Row.class); simpleTypes.add(UDTValue.class); - classToDataType = Collections.unmodifiableMap(classToDataType(primitiveWrappers, codecRegistry)); + classToDataType = Collections.unmodifiableMap(classToDataType(codecRegistry, primitiveWrappers)); nameToDataType = Collections.unmodifiableMap(nameToDataType()); CASSANDRA_SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes); } @@ -82,40 +84,37 @@ public class CassandraSimpleTypeHolder extends SimpleTypeHolder { Map nameToDataType = new HashMap<>(16); - for (DataType dataType : DataType.allPrimitiveTypes()) { + DataType.allPrimitiveTypes().forEach(dataType -> { nameToDataType.put(dataType.getName(), dataType); - } + }); return nameToDataType; } /** * @return the map between {@link Class} and {@link DataType}. - * @param primitiveWrappers map of primitive to wrapper type * @param codecRegistry the Cassandra codec registry + * @param primitiveWrappers map of primitive to wrapper type */ - private static Map, DataType> classToDataType(Map, Class> primitiveWrappers, - CodecRegistry codecRegistry) { + private static Map, DataType> classToDataType(CodecRegistry codecRegistry, + Map, Class> primitiveWrappers) { Map, DataType> classToDataType = new HashMap<>(16); - for (DataType dataType : DataType.allPrimitiveTypes()) { + DataType.allPrimitiveTypes().forEach(dataType -> { + Class javaType = codecRegistry.codecFor(dataType).getJavaType().getRawType(); - Class javaClass = codecRegistry.codecFor(dataType).getJavaType().getRawType(); - classToDataType.put(javaClass, dataType); + classToDataType.put(javaType, dataType); - Class primitiveJavaClass = primitiveWrappers.get(javaClass); - if (primitiveJavaClass != null) { - classToDataType.put(primitiveJavaClass, dataType); - } - } + Optional.ofNullable(primitiveWrappers.get(javaType)) + .ifPresent(primitiveType -> classToDataType.put(primitiveType, dataType)); + }); // override String to text datatype as String is used multiple times classToDataType.put(String.class, DataType.text()); - // map Long to bigint as counter columns (last type aver multiple overrides) - // are a special use case so map it to a more common type by - // default + // map Long to bigint as counter columns (last type aver multiple overrides) are a special use case + // so map it to a more common type by default classToDataType.put(Long.class, DataType.bigint()); classToDataType.put(long.class, DataType.bigint()); @@ -130,10 +129,10 @@ public class CassandraSimpleTypeHolder extends SimpleTypeHolder { */ private static Set> getCassandraPrimitiveTypes(CodecRegistry codecRegistry) { - return DataType.allPrimitiveTypes().stream() // - .map(codecRegistry::codecFor) // - .map(TypeCodec::getJavaType) // - .map(TypeToken::getRawType) // + return DataType.allPrimitiveTypes().stream() + .map(codecRegistry::codecFor) + .map(TypeCodec::getJavaType) + .map(TypeToken::getRawType) .collect(Collectors.toSet()); } @@ -165,15 +164,19 @@ public class CassandraSimpleTypeHolder extends SimpleTypeHolder { public static DataType.Name[] getDataTypeNamesFrom(List> arguments) { DataType.Name[] array = new DataType.Name[arguments.size()]; + for (int i = 0; i != array.length; i++) { TypeInformation typeInfo = arguments.get(i); DataType dataType = getDataTypeFor(typeInfo.getType()); + if (dataType == null) { throw new InvalidDataAccessApiUsageException( - String.format("Did not find appropriate primitive DataType for type '%s'", typeInfo.getType())); + String.format("Did not find appropriate primitive DataType for type '%s'", typeInfo.getType())); } + array[i] = dataType.getName(); } + return array; } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/AbstractCassandraQuery.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/AbstractCassandraQuery.java index 16fe12b63..00427ddd5 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/AbstractCassandraQuery.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/AbstractCassandraQuery.java @@ -136,9 +136,8 @@ public abstract class AbstractCassandraQuery implements RepositoryQuery { Statement statement = createQuery(parameterAccessor); - CassandraQueryExecution queryExecution = getExecution( - new ResultProcessingConverter(resultProcessor, getOperations().getConverter().getMappingContext(), - getEntityInstantiators())); + CassandraQueryExecution queryExecution = getExecution(new ResultProcessingConverter( + resultProcessor, getOperations().getConverter().getMappingContext(), getEntityInstantiators())); CassandraReturnedType returnedType = new CassandraReturnedType(resultProcessor.getReturnedType(), getOperations().getConverter().getCustomConversions()); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/AbstractReactiveCassandraQuery.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/AbstractReactiveCassandraQuery.java index 8f1587815..ddd75bd45 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/AbstractReactiveCassandraQuery.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/AbstractReactiveCassandraQuery.java @@ -18,7 +18,6 @@ package org.springframework.data.cassandra.repository.query; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import org.reactivestreams.Publisher; import org.springframework.core.convert.converter.Converter; import org.springframework.data.cassandra.core.CassandraOperations; import org.springframework.data.cassandra.core.ReactiveCassandraOperations; @@ -32,6 +31,8 @@ import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.util.Assert; +import org.reactivestreams.Publisher; + import com.datastax.driver.core.Statement; /** @@ -65,13 +66,23 @@ public abstract class AbstractReactiveCassandraQuery implements RepositoryQuery this.instantiators = new EntityInstantiators(); } + /* (non-Javadoc) */ + protected EntityInstantiators getEntityInstantiators() { + return this.instantiators; + } + /* * (non-Javadoc) * @see org.springframework.data.repository.query.RepositoryQuery#getQueryMethod() */ @Override - public CassandraQueryMethod getQueryMethod() { - return method; + public ReactiveCassandraQueryMethod getQueryMethod() { + return this.method; + } + + /* (non-Javadoc) */ + protected ReactiveCassandraOperations getReactiveCassandraOperations() { + return this.operations; } /* @@ -81,14 +92,14 @@ public abstract class AbstractReactiveCassandraQuery implements RepositoryQuery @Override public Object execute(Object[] parameters) { - return (method.hasReactiveWrapperParameter() ? executeDeferred(parameters) - : execute(new ReactiveCassandraParameterAccessor(method, parameters))); + return (getQueryMethod().hasReactiveWrapperParameter() ? executeDeferred(parameters) + : execute(new ReactiveCassandraParameterAccessor(getQueryMethod(), parameters))); } @SuppressWarnings("unchecked") private Object executeDeferred(Object[] parameters) { - ReactiveCassandraParameterAccessor accessor = new ReactiveCassandraParameterAccessor(method, parameters); + ReactiveCassandraParameterAccessor accessor = new ReactiveCassandraParameterAccessor(getQueryMethod(), parameters); return (getQueryMethod().isCollectionQuery() ? Flux.defer(() -> (Publisher) execute(accessor)) : Mono.defer(() -> (Mono) execute(accessor))); @@ -96,18 +107,20 @@ public abstract class AbstractReactiveCassandraQuery implements RepositoryQuery private Object execute(CassandraParameterAccessor parameterAccessor) { - CassandraParameterAccessor convertingParameterAccessor = new ConvertingParameterAccessor(operations.getConverter(), - parameterAccessor); + CassandraParameterAccessor convertingParameterAccessor = + new ConvertingParameterAccessor(getReactiveCassandraOperations().getConverter(), parameterAccessor); Statement statement = createQuery(convertingParameterAccessor); - ResultProcessor resultProcessor = method.getResultProcessor().withDynamicProjection(convertingParameterAccessor); + ResultProcessor resultProcessor = + getQueryMethod().getResultProcessor().withDynamicProjection(convertingParameterAccessor); - ReactiveCassandraQueryExecution queryExecution = getExecution( - new ResultProcessingConverter(resultProcessor, operations.getConverter().getMappingContext(), instantiators)); + ReactiveCassandraQueryExecution queryExecution = getExecution(new ResultProcessingConverter( + resultProcessor, getReactiveCassandraOperations().getConverter().getMappingContext(), + getEntityInstantiators())); CassandraReturnedType returnedType = new CassandraReturnedType(resultProcessor.getReturnedType(), - operations.getConverter().getCustomConversions()); + getReactiveCassandraOperations().getConverter().getCustomConversions()); Class resultType = (returnedType.isProjecting() ? returnedType.getDomainType() : returnedType.getReturnedType()); @@ -132,6 +145,7 @@ public abstract class AbstractReactiveCassandraQuery implements RepositoryQuery /* (non-Javadoc) */ private ReactiveCassandraQueryExecution getExecutionToWrap() { - return (method.isCollectionQuery() ? new CollectionExecution(operations) : new SingleEntityExecution(operations)); + return (getQueryMethod().isCollectionQuery() ? new CollectionExecution(getReactiveCassandraOperations()) + : new SingleEntityExecution(getReactiveCassandraOperations())); } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraQueryCreator.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraQueryCreator.java index d2526fd27..76d257e46 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraQueryCreator.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/CassandraQueryCreator.java @@ -20,8 +20,6 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.cassandra.core.query.Criteria; import org.springframework.data.cassandra.core.query.CriteriaDefinition; @@ -38,6 +36,9 @@ import org.springframework.data.repository.query.parser.Part.Type; import org.springframework.data.repository.query.parser.PartTree; import org.springframework.util.Assert; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.datastax.driver.core.querybuilder.Clause; /** @@ -73,14 +74,34 @@ class CassandraQueryCreator extends AbstractQueryCreator iterator) { - PersistentPropertyPath path = mappingContext - .getPersistentPropertyPath(part.getProperty()); + PersistentPropertyPath path = + getMappingContext().getPersistentPropertyPath(part.getProperty()); CassandraPersistentProperty property = path.getLeafProperty(); @@ -94,10 +115,10 @@ class CassandraQueryCreator extends AbstractQueryCreator iterator) { if (base == null) { - return queryBuilder.and(create(part, iterator)); + return getQueryBuilder().and(create(part, iterator)); } - queryBuilder.and(base); + getQueryBuilder().and(base); return create(part, iterator); } @@ -120,10 +141,10 @@ class CassandraQueryCreator extends AbstractQueryCreator converter.convertToCassandraColumn(bindableValue, findTypeInformation(index, v, Optional.empty()))) + v -> converter.convertToColumnType(bindableValue, findTypeInformation(index, v, Optional.empty()))) .orElse(null); } @@ -151,7 +151,7 @@ class ConvertingParameterAccessor implements CassandraParameterAccessor { private Object potentiallyConvert(int index, Optional bindableValue, CassandraPersistentProperty property) { return bindableValue.flatMap( - v -> converter.convertToCassandraColumn(bindableValue, findTypeInformation(index, v, Optional.of(property)))) + v -> converter.convertToColumnType(bindableValue, findTypeInformation(index, v, Optional.of(property)))) .orElse(null); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/PartTreeCassandraQuery.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/PartTreeCassandraQuery.java index 4eba9d2e6..ba37f56a8 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/PartTreeCassandraQuery.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/PartTreeCassandraQuery.java @@ -37,10 +37,10 @@ import com.datastax.driver.core.Statement; */ public class PartTreeCassandraQuery extends AbstractCassandraQuery { - private final PartTree tree; - private final CassandraMappingContext mappingContext; + private final PartTree tree; + private final StatementFactory statementFactory; /** @@ -70,9 +70,15 @@ public class PartTreeCassandraQuery extends AbstractCassandraQuery { } /** +<<<<<<< 85e5d32f4c6db62b63513c14864f7625d7813060 * Returns the {@link StatementFactory} used by this query to construct and run Cassandra CQL statements. * * @return the {@link StatementFactory} used by this query to construct and run Cassandra CQL statements. +======= + * Returns the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements. + * + * @return the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements. +>>>>>>> DATACASS-343 - Polish. * @see org.springframework.data.cassandra.core.StatementFactory */ protected StatementFactory getStatementFactory() { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ReactivePartTreeCassandraQuery.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ReactivePartTreeCassandraQuery.java index d0b5ccaa0..81f277faf 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ReactivePartTreeCassandraQuery.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ReactivePartTreeCassandraQuery.java @@ -35,10 +35,10 @@ import com.datastax.driver.core.Statement; */ public class ReactivePartTreeCassandraQuery extends AbstractReactiveCassandraQuery { - private final PartTree tree; - private final CassandraMappingContext mappingContext; + private final PartTree tree; + private final StatementFactory statementFactory; /** @@ -70,9 +70,15 @@ public class ReactivePartTreeCassandraQuery extends AbstractReactiveCassandraQue } /** +<<<<<<< 85e5d32f4c6db62b63513c14864f7625d7813060 * Returns the {@link StatementFactory} used by this query to construct and run Cassandra CQL statements. * * @return the {@link StatementFactory} used by this query to construct and run Cassandra CQL statements. +======= + * Returns the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements. + * + * @return the {@link StatementFactory} used by this template to construct and run Cassandra CQL statements. +>>>>>>> DATACASS-343 - Polish. * @see org.springframework.data.cassandra.core.StatementFactory */ protected StatementFactory getStatementFactory() { diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ReactiveStringBasedCassandraQuery.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ReactiveStringBasedCassandraQuery.java index f30ee6cf7..be75a48bc 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ReactiveStringBasedCassandraQuery.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/ReactiveStringBasedCassandraQuery.java @@ -15,14 +15,15 @@ */ package org.springframework.data.cassandra.repository.query; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.data.cassandra.core.ReactiveCassandraOperations; import org.springframework.data.repository.query.EvaluationContextProvider; import org.springframework.data.repository.query.QueryCreationException; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.util.Assert; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.datastax.driver.core.SimpleStatement; /** @@ -81,6 +82,11 @@ public class ReactiveStringBasedCassandraQuery extends AbstractReactiveCassandra new ExpressionEvaluatingParameterBinder(expressionParser, evaluationContextProvider)); } + /* (non-Javadoc) */ + protected StringBasedQuery getStringBasedQuery() { + return this.stringBasedQuery; + } + /* (non-Javadoc) * @see org.springframework.data.cassandra.repository.query.AbstractCassandraQuery#createQuery(org.springframework.data.cassandra.repository.query.CassandraParameterAccessor) */ @@ -88,7 +94,7 @@ public class ReactiveStringBasedCassandraQuery extends AbstractReactiveCassandra public SimpleStatement createQuery(CassandraParameterAccessor parameterAccessor) { try { - SimpleStatement boundQuery = stringBasedQuery.bindQuery(parameterAccessor, getQueryMethod()); + SimpleStatement boundQuery = getStringBasedQuery().bindQuery(parameterAccessor, getQueryMethod()); if (LOG.isDebugEnabled()) { LOG.debug(String.format("Created query [%s].", boundQuery)); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/StringBasedCassandraQuery.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/StringBasedCassandraQuery.java index 43051c000..52804ec2c 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/StringBasedCassandraQuery.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/StringBasedCassandraQuery.java @@ -15,13 +15,14 @@ */ package org.springframework.data.cassandra.repository.query; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.data.cassandra.core.CassandraOperations; import org.springframework.data.repository.query.EvaluationContextProvider; import org.springframework.data.repository.query.QueryCreationException; import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.datastax.driver.core.SimpleStatement; /** @@ -72,10 +73,16 @@ public class StringBasedCassandraQuery extends AbstractCassandraQuery { SpelExpressionParser expressionParser, EvaluationContextProvider evaluationContextProvider) { super(queryMethod, operations); + this.stringBasedQuery = new StringBasedQuery(query, new ExpressionEvaluatingParameterBinder(expressionParser, evaluationContextProvider)); } + /* (non-Javadoc) */ + protected StringBasedQuery getStringBasedQuery() { + return this.stringBasedQuery; + } + /* (non-Javadoc) * @see org.springframework.data.cassandra.repository.query.AbstractCassandraQuery#createQuery(org.springframework.data.cassandra.repository.query.CassandraParameterAccessor) */ @@ -83,8 +90,7 @@ public class StringBasedCassandraQuery extends AbstractCassandraQuery { public SimpleStatement createQuery(CassandraParameterAccessor parameterAccessor) { try { - - SimpleStatement boundQuery = stringBasedQuery.bindQuery(parameterAccessor, getQueryMethod()); + SimpleStatement boundQuery = getStringBasedQuery().bindQuery(parameterAccessor, getQueryMethod()); if (LOG.isDebugEnabled()) { LOG.debug(String.format("Created query [%s].", boundQuery)); diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/StringBasedQuery.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/StringBasedQuery.java index 635799236..616ae7781 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/StringBasedQuery.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/repository/query/StringBasedQuery.java @@ -49,7 +49,6 @@ class StringBasedQuery { * * @param query must not be empty. * @param parameterBinder must not be {@literal null}. - * @param codecRegistry must not be {@literal null}. */ public StringBasedQuery(String query, ExpressionEvaluatingParameterBinder parameterBinder) { @@ -63,6 +62,16 @@ class StringBasedQuery { } + /* (non-Javadoc) */ + protected ExpressionEvaluatingParameterBinder getParameterBinder() { + return this.parameterBinder; + } + + /* (non-Javadoc) */ + protected String getQuery() { + return this.query; + } + /** * Bind the query to actual parameters using {@link CassandraParameterAccessor}, * @@ -75,10 +84,10 @@ class StringBasedQuery { Assert.notNull(parameterAccessor, "CassandraParameterAccessor must not be null"); Assert.notNull(queryMethod, "CassandraQueryMethod must not be null"); - List arguments = parameterBinder.bind(parameterAccessor, - new BindingContext(queryMethod, queryParameterBindings)); + List arguments = getParameterBinder().bind(parameterAccessor, + new BindingContext(queryMethod, this.queryParameterBindings)); - return ParameterBinder.INSTANCE.bind(query, arguments); + return ParameterBinder.INSTANCE.bind(getQuery(), arguments); } /** diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/convert/QueryMapperUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/convert/QueryMapperUnitTests.java index 83b6b9fcd..256cad965 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/convert/QueryMapperUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/convert/QueryMapperUnitTests.java @@ -25,6 +25,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Currency; import java.util.List; +import java.util.Locale; import java.util.stream.Collectors; import org.junit.Before; @@ -77,7 +78,8 @@ public class QueryMapperUnitTests { @Before public void before() throws Exception { - CustomConversions customConversions = new CustomConversions(Collections.singletonList(CurrencyConverter.INSTANCE)); + CassandraCustomConversions customConversions = + new CassandraCustomConversions(Collections.singletonList(CurrencyConverter.INSTANCE)); mappingContext.setCustomConversions(customConversions); mappingContext.setUserTypeResolver(userTypeResolver); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/convert/UpdateMapperUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/convert/UpdateMapperUnitTests.java index 08f1295c4..3b333b06e 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/convert/UpdateMapperUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/convert/UpdateMapperUnitTests.java @@ -55,7 +55,8 @@ public class UpdateMapperUnitTests { @Before public void before() throws Exception { - CustomConversions customConversions = new CustomConversions(Collections.singletonList(CurrencyConverter.INSTANCE)); + CassandraCustomConversions customConversions = + new CassandraCustomConversions(Collections.singletonList(CurrencyConverter.INSTANCE)); mappingContext.setCustomConversions(customConversions); mappingContext.setUserTypeResolver(userTypeResolver);