From e26f6a109b06c978b9fa29979bc072956f016f34 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 27 Feb 2017 12:51:20 +0100 Subject: [PATCH] DATACASS-400 - Use custom conversion and UDT mapping with entity instantiator. We now apply custom conversion and UDT mapping to the value provider that is used with entity instantiators. This allows entities to be instantiated with mapped UDTs and using custom conversion with properties that are provided using a constructor. Previously, columns were not converted which caused a parameter mismatch so entities couldn't be instantiated. --- .../convert/MappingCassandraConverter.java | 57 ++++++++++++++++--- ...CassandraConverterUDTIntegrationTests.java | 45 +++++++-------- .../MappingCassandraConverterUnitTests.java | 13 +++-- 3 files changed, 77 insertions(+), 38 deletions(-) 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 7f4331d0b..53e948bd5 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,10 +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 java.util.Map.Entry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -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); @@ -835,4 +838,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;