From 20eb0f47fa1be7b51b485e1a4a4d2b3e121027f7 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 19 Jul 2022 11:42:25 +0200 Subject: [PATCH] Fix DTO projection instantiation. We now correctly instantiate DTO projection classes by using the actual constructor argument type. Previously, we did not update the conversion context to fetch the correct type but used the type of the DTO projection class instead of the constructor argument. Closes #1292 --- .../convert/MappingCassandraConverter.java | 4 +- .../MappingCassandraConverterUnitTests.java | 41 +++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java index a820a7fa4..ea15e2c03 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java @@ -1427,11 +1427,11 @@ public class MappingCassandraConverter extends AbstractCassandraConverter name, entity.getType())); } - return (T) getReadValue(context, provider, property); + return (T) getReadValue(context.forProperty(property.getName()), provider, property); } @Nullable - private CassandraPersistentProperty getPersistentProperty(String name, TypeInformation typeInformation, + private CassandraPersistentProperty getPersistentProperty(String name, TypeInformation typeInformation, MergedAnnotations annotations) { CassandraPersistentProperty property = entity.getPersistentProperty(name); diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUnitTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUnitTests.java index 4ac68c4b7..394738602 100755 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUnitTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUnitTests.java @@ -964,6 +964,33 @@ public class MappingCassandraConverterUnitTests { assertThat(result.getTuple().getOne()).isEqualTo("One"); } + @Test // GH-1202 + void shouldCreateDtoProjectionsThroughConstructor() { + + DefaultTupleValue value = new DefaultTupleValue( + new DefaultTupleType(Arrays.asList(DataTypes.ASCII, DataTypes.ASCII, DataTypes.ASCII))); + + value.setString(0, "Zero"); + value.setString(1, "One"); + value.setString(2, "Two"); + + EntityProjectionIntrospector introspector = EntityProjectionIntrospector.create( + new SpelAwareProxyProjectionFactory(), EntityProjectionIntrospector.ProjectionPredicate.typeHierarchy(), + this.mappingContext); + + rowMock = RowMockUtil.newRowMock(RowMockUtil.column("firstname", "Heisenberg", DataTypes.ASCII), + RowMockUtil.column("tuple", value, value.getType())); + + EntityProjection projection = introspector + .introspect(TupleAndNameProjection.class, WithMappedTuple.class); + + TupleAndNameProjection result = this.mappingCassandraConverter.project(projection, rowMock); + + assertThat(result.getName().getFirstname()).isEqualTo("Heisenberg"); + assertThat(result.getTuple().zero).isEqualTo("Zero"); + assertThat(result.getTuple().one).isEqualTo("One"); + } + private static List getValues(Map statement) { return new ArrayList<>(statement.values()); } @@ -1204,9 +1231,15 @@ public class MappingCassandraConverterUnitTests { private static class WithMappedTuple { String firstname; + @Embedded.Nullable Name name; TupleWithElementAnnotationInConstructor tuple; } + @lombok.Value + private static class Name { + String firstname; + } + @Data private static class WithMappedTupleDtoProjection { @@ -1214,6 +1247,14 @@ public class MappingCassandraConverterUnitTests { TupleProjection tuple; } + @lombok.Value + private static class TupleAndNameProjection { + + @Embedded.Nullable Name name; + + TupleWithElementAnnotationInConstructor tuple; + } + private static interface TupleProjection { String getZero();