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 31ceb66b1..9a91b85d7 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 @@ -1,5 +1,5 @@ /* - * Copyright 2013-2016 the original author or authors + * Copyright 2013-2017 the original author or authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,23 +17,13 @@ package org.springframework.data.cassandra.convert; import static org.springframework.data.cassandra.repository.support.BasicMapId.*; +import lombok.AllArgsConstructor; + import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; - -import com.datastax.driver.core.CodecRegistry; -import com.datastax.driver.core.DataType; -import com.datastax.driver.core.Row; -import com.datastax.driver.core.TypeCodec; -import com.datastax.driver.core.UDTValue; -import com.datastax.driver.core.UserType; -import com.datastax.driver.core.querybuilder.Clause; -import com.datastax.driver.core.querybuilder.Delete; -import com.datastax.driver.core.querybuilder.Insert; -import com.datastax.driver.core.querybuilder.QueryBuilder; -import com.datastax.driver.core.querybuilder.Select; -import com.datastax.driver.core.querybuilder.Update; +import java.util.Map.Entry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,6 +56,19 @@ import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; +import com.datastax.driver.core.CodecRegistry; +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.TypeCodec; +import com.datastax.driver.core.UDTValue; +import com.datastax.driver.core.UserType; +import com.datastax.driver.core.querybuilder.Clause; +import com.datastax.driver.core.querybuilder.Delete; +import com.datastax.driver.core.querybuilder.Insert; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.core.querybuilder.Select; +import com.datastax.driver.core.querybuilder.Update; + /** * {@link CassandraConverter} that uses a {@link MappingContext} to do sophisticated mapping of domain objects to * {@link Row}. @@ -136,8 +139,8 @@ public class MappingCassandraConverter extends AbstractCassandraConverter return getConversionService().convert(row, type); } - CassandraPersistentEntity persistentEntity = (CassandraPersistentEntity) - getMappingContext().getPersistentEntity(typeInfo); + CassandraPersistentEntity persistentEntity = (CassandraPersistentEntity) getMappingContext() + .getPersistentEntity(typeInfo); if (persistentEntity == null) { throw new MappingException(String.format("No mapping metadata found for %s", rawType.getName())); @@ -160,8 +163,8 @@ public class MappingCassandraConverter extends AbstractCassandraConverter DefaultSpELExpressionEvaluator expressionEvaluator = new DefaultSpELExpressionEvaluator(row, spELContext); BasicCassandraRowValueProvider rowValueProvider = new BasicCassandraRowValueProvider(row, expressionEvaluator); - CassandraPersistentEntityParameterValueProvider parameterProvider = - new CassandraPersistentEntityParameterValueProvider(entity, rowValueProvider, null); + CassandraPersistentEntityParameterValueProvider parameterProvider = new CassandraPersistentEntityParameterValueProvider( + entity, new MappingAndConvertingValueProvider(rowValueProvider), null); EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity); S instance = instantiator.createInstance(entity, parameterProvider); @@ -175,11 +178,11 @@ 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); - CassandraPersistentEntityParameterValueProvider parameterProvider = - new CassandraPersistentEntityParameterValueProvider(entity, valueProvider, null); + CassandraPersistentEntityParameterValueProvider parameterProvider = new CassandraPersistentEntityParameterValueProvider( + entity, new MappingAndConvertingValueProvider(valueProvider), null); EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity); S instance = instantiator.createInstance(entity, parameterProvider); @@ -833,4 +836,40 @@ public class MappingCassandraConverter extends AbstractCassandraConverter private TypeCodec getCodec(CassandraPersistentProperty property) { return CodecRegistry.DEFAULT_INSTANCE.codecFor(mappingContext.getDataType(property)); } + + /** + * {@link CassandraRowValueProvider} that delegates reads to {@link CassandraValueProvider} applying mapping and + * custom conversion from {@link MappingCassandraConverter}. + * + * @author Mark Paluch + * @since 1.5.1 + */ + @AllArgsConstructor + class MappingAndConvertingValueProvider implements CassandraValueProvider { + + private final CassandraValueProvider parent; + + /* (non-Javadoc) + * @see org.springframework.data.cassandra.convert.CassandraValueProvider#hasProperty(org.springframework.data.cassandra.mapping.CassandraPersistentProperty) + */ + @Override + public boolean hasProperty(CassandraPersistentProperty property) { + return parent.hasProperty(property); + } + + /* (non-Javadoc) + * @see org.springframework.data.mapping.model.PropertyValueProvider#getPropertyValue(org.springframework.data.mapping.PersistentProperty) + */ + @Override + public Object getPropertyValue(CassandraPersistentProperty property) { + + Object readValue = getReadValue(parent, property); + + if(readValue == null || property.getType().isAssignableFrom(readValue.getClass())){ + return readValue; + } + + return conversionService.convert(readValue, property.getType()); + } + } } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/convert/MappingCassandraConverterUDTIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/convert/MappingCassandraConverterUDTIntegrationTests.java index c6975877f..ca353f554 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/convert/MappingCassandraConverterUDTIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/convert/MappingCassandraConverterUDTIntegrationTests.java @@ -17,7 +17,9 @@ package org.springframework.data.cassandra.convert; import static org.assertj.core.api.Assertions.*; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.Getter; import java.util.Arrays; import java.util.Collections; @@ -291,11 +293,10 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring assertThat(addressBook.getOtherCurrencies()).hasSize(1).contains(Currency.getInstance("EUR")); } - @Test // DATACASS-172 + @Test // DATACASS-172, DATACASS-400 public void shouldWriteUdtWithCustomConversion() { - Bank bank = new Bank(); - bank.setCurrency(Currency.getInstance("EUR")); + Bank bank = new Bank(null, Currency.getInstance("EUR"), null); Insert insert = QueryBuilder.insertInto("bank"); converter.write(bank, insert); @@ -316,12 +317,10 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring assertThat(update.toString()).isEqualTo("UPDATE money WHERE currency={currency:'EUR'};"); } - @Test // DATACASS-172 + @Test // DATACASS-172, DATACASS-400 public void shouldWriteUdtUpdateAssignmentsWithCustomConversion() { - MoneyTransfer money = new MoneyTransfer(); - money.setId("1"); - money.setCurrency(Currency.getInstance("EUR")); + MoneyTransfer money = new MoneyTransfer("1", Currency.getInstance("EUR")); Update update = QueryBuilder.update("money"); converter.write(money, update); @@ -353,11 +352,10 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring assertThat(delete.toString()).isEqualTo("DELETE FROM money WHERE currency={currency:'EUR'};"); } - @Test // DATACASS-172 + @Test // DATACASS-172, DATACASS-400 public void shouldWriteUdtListWithCustomConversion() { - Bank bank = new Bank(); - bank.setOtherCurrencies(Collections.singletonList(Currency.getInstance("EUR"))); + Bank bank = new Bank(null, null, Collections.singletonList(Currency.getInstance("EUR"))); Insert insert = QueryBuilder.insertInto("bank"); converter.write(bank, insert); @@ -379,20 +377,14 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring assertThat(car.getEngine().getManufacturer().getName()).isEqualTo("a good one"); } - @Test // DATACASS-172 + @Test // DATACASS-172, DATACASS-400 public void shouldWriteNestedUdt() { session.execute("INSERT INTO car (id, engine) VALUES ('1', {manufacturer: {name:'a good one'}});"); - Manufacturer manufacturer = new Manufacturer(); - manufacturer.setName("a good one"); + Engine engine = new Engine(new Manufacturer("a good one")); - Engine engine = new Engine(); - engine.setManufacturer(manufacturer); - - Car car = new Car(); - car.setId("1"); - car.setEngine(engine); + Car car = new Car("1", engine); Insert insert = QueryBuilder.insertInto("car"); converter.write(car, insert); @@ -402,7 +394,8 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring } @Table - @Data + @Getter + @AllArgsConstructor private static class Bank { @Id String id; @@ -416,8 +409,9 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring @Id private Currency currency; } - @Data @Table + @AllArgsConstructor + @Getter public static class MoneyTransfer { @Id String id; @@ -426,7 +420,8 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring } @Table - @Data + @Getter + @AllArgsConstructor private static class Car { @Id String id; @@ -434,13 +429,15 @@ public class MappingCassandraConverterUDTIntegrationTests extends AbstractSpring } @UserDefinedType - @Data + @Getter + @AllArgsConstructor private static class Engine { Manufacturer manufacturer; } @UserDefinedType - @Data + @Getter + @AllArgsConstructor private static class Manufacturer { String name; } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/convert/MappingCassandraConverterUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/convert/MappingCassandraConverterUnitTests.java index f3687619f..e9bb49045 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/convert/MappingCassandraConverterUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/convert/MappingCassandraConverterUnitTests.java @@ -21,6 +21,8 @@ import static org.mockito.Mockito.*; import static org.springframework.data.cassandra.RowMockUtil.*; import static org.springframework.data.cassandra.repository.support.BasicMapId.*; +import lombok.AllArgsConstructor; + import java.io.Serializable; import java.math.BigDecimal; import java.math.BigInteger; @@ -482,11 +484,11 @@ public class MappingCassandraConverterUnitTests { assertThat(result.localDate.getDayOfMonth()).isEqualTo(4); } - @Test // DATACASS-296 + @Test // DATACASS-296, DATACASS-400 public void shouldCreateInsertWithLocalDateUsingCassandraDateCorrectly() { - TypeWithLocalDateMappedToDate typeWithLocalDate = new TypeWithLocalDateMappedToDate(); - typeWithLocalDate.localDate = java.time.LocalDate.of(2010, 7, 4); + TypeWithLocalDateMappedToDate typeWithLocalDate = new TypeWithLocalDateMappedToDate(null, + java.time.LocalDate.of(2010, 7, 4)); Insert insert = QueryBuilder.insertInto("table"); @@ -498,8 +500,8 @@ public class MappingCassandraConverterUnitTests { @Test // DATACASS-296 public void shouldCreateUpdateWithLocalDateUsingCassandraDateCorrectly() { - TypeWithLocalDateMappedToDate typeWithLocalDate = new TypeWithLocalDateMappedToDate(); - typeWithLocalDate.localDate = java.time.LocalDate.of(2010, 7, 4); + TypeWithLocalDateMappedToDate typeWithLocalDate = new TypeWithLocalDateMappedToDate(null, + java.time.LocalDate.of(2010, 7, 4)); Update update = QueryBuilder.update("table"); @@ -1042,6 +1044,7 @@ public class MappingCassandraConverterUnitTests { * Uses Cassandra's {@link Name#DATE} which maps by default to {@link LocalDate} */ @Table + @AllArgsConstructor public static class TypeWithLocalDateMappedToDate { @PrimaryKey private String id;