Skip to content

Commit 3a93a83

Browse files
committed
Create a new conversion context for projection properties.
We now create a new conversion context to ensure that we use the correct property type to avoid type retention when mapping complex objects within a projection. Closes #1240
1 parent 3043890 commit 3a93a83

File tree

2 files changed

+69
-4
lines changed

2 files changed

+69
-4
lines changed

spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverter.java

+15-4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import org.apache.commons.logging.Log;
2929
import org.apache.commons.logging.LogFactory;
30+
3031
import org.springframework.beans.BeansException;
3132
import org.springframework.beans.factory.BeanClassLoaderAware;
3233
import org.springframework.context.ApplicationContext;
@@ -39,7 +40,16 @@
3940
import org.springframework.data.cassandra.core.mapping.*;
4041
import org.springframework.data.cassandra.core.mapping.Embedded.OnEmpty;
4142
import org.springframework.data.convert.CustomConversions;
42-
import org.springframework.data.mapping.*;
43+
import org.springframework.data.mapping.AccessOptions;
44+
import org.springframework.data.mapping.InstanceCreatorMetadata;
45+
import org.springframework.data.mapping.MappingException;
46+
import org.springframework.data.mapping.Parameter;
47+
import org.springframework.data.mapping.PersistentEntity;
48+
import org.springframework.data.mapping.PersistentProperty;
49+
import org.springframework.data.mapping.PersistentPropertyAccessor;
50+
import org.springframework.data.mapping.PersistentPropertyPath;
51+
import org.springframework.data.mapping.PersistentPropertyPathAccessor;
52+
import org.springframework.data.mapping.PreferredConstructor;
4353
import org.springframework.data.mapping.context.MappingContext;
4454
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
4555
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
@@ -1022,8 +1032,7 @@ protected Object getPotentiallyConvertedSimpleRead(Object value, TypeInformation
10221032
@SuppressWarnings({ "rawtypes", "unchecked" })
10231033
private Object getPotentiallyConvertedSimpleRead(@Nullable Object value, @Nullable Class<?> target) {
10241034

1025-
if (value == null || target == null
1026-
|| ClassUtils.isAssignableValue(target, value)) {
1035+
if (value == null || target == null || ClassUtils.isAssignableValue(target, value)) {
10271036
return value;
10281037
}
10291038

@@ -1537,7 +1546,9 @@ ConversionContext forProperty(String name) {
15371546

15381547
EntityProjection<?, ?> property = projection.findProperty(name);
15391548
if (property == null) {
1540-
return super.forProperty(name);
1549+
return new ConversionContext(conversions, MappingCassandraConverter.this::doReadRow,
1550+
MappingCassandraConverter.this::doReadTupleValue, MappingCassandraConverter.this::doReadUdtValue,
1551+
collectionConverter, mapConverter, elementConverter);
15411552
}
15421553

15431554
return new ProjectingConversionContext(conversions, rowConverter, tupleConverter, udtConverter,

spring-data-cassandra/src/test/java/org/springframework/data/cassandra/core/convert/MappingCassandraConverterUnitTests.java

+54
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,15 @@
5959
import org.springframework.data.cassandra.domain.TypeWithMapId;
6060
import org.springframework.data.cassandra.domain.User;
6161
import org.springframework.data.cassandra.domain.UserToken;
62+
import org.springframework.data.cassandra.support.UserDefinedTypeBuilder;
6263
import org.springframework.data.cassandra.test.util.RowMockUtil;
6364
import org.springframework.data.projection.EntityProjection;
6465
import org.springframework.data.projection.EntityProjectionIntrospector;
6566
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
6667

6768
import com.datastax.oss.driver.api.core.CqlIdentifier;
6869
import com.datastax.oss.driver.api.core.cql.Row;
70+
import com.datastax.oss.driver.api.core.data.UdtValue;
6971
import com.datastax.oss.driver.api.core.type.DataTypes;
7072
import com.datastax.oss.driver.internal.core.data.DefaultTupleValue;
7173
import com.datastax.oss.driver.internal.core.type.DefaultTupleType;
@@ -1378,6 +1380,29 @@ void shouldApplyCustomConverterToMapLikeType() {
13781380
assertThat(target.theJson.get("hello")).isEqualTo("world");
13791381
}
13801382

1383+
@Test // GH-1240
1384+
void shouldReadOpenProjectionWithNestedObject() {
1385+
1386+
com.datastax.oss.driver.api.core.type.UserDefinedType authorType = UserDefinedTypeBuilder.forName("author")
1387+
.withField("firstName", DataTypes.TEXT).withField("lastName", DataTypes.TEXT).build();
1388+
1389+
UdtValue udtValue = authorType.newValue().setString("firstName", "Walter").setString("lastName", "White");
1390+
1391+
Row source = RowMockUtil.newRowMock(column("id", "id-1", DataTypes.TEXT), column("name", "my-book", DataTypes.INT),
1392+
column("author", udtValue, authorType));
1393+
1394+
EntityProjectionIntrospector introspector = EntityProjectionIntrospector.create(
1395+
mappingCassandraConverter.getProjectionFactory(),
1396+
EntityProjectionIntrospector.ProjectionPredicate.typeHierarchy()
1397+
.and((target, underlyingType) -> !mappingCassandraConverter.getCustomConversions().isSimpleType(target)),
1398+
mappingContext);
1399+
1400+
BookProjection projection = mappingCassandraConverter
1401+
.project(introspector.introspect(BookProjection.class, Book.class), source);
1402+
1403+
assertThat(projection.getName()).isEqualTo("my-book by Walter White");
1404+
}
1405+
13811406
static class TypeWithJsonObject {
13821407

13831408
JSONObject theJson;
@@ -1406,4 +1431,33 @@ public String convert(JSONObject source) {
14061431
}
14071432

14081433
}
1434+
1435+
interface BookProjection {
1436+
1437+
@Value("#{target.name + ' by ' + target.author.firstName + ' ' + target.author.lastName}")
1438+
String getName();
1439+
}
1440+
1441+
@Data
1442+
static class Book {
1443+
1444+
@Id String id;
1445+
1446+
String name;
1447+
1448+
Author author = new Author();
1449+
1450+
}
1451+
1452+
@Data
1453+
@UserDefinedType
1454+
static class Author {
1455+
1456+
@Id String id;
1457+
1458+
String firstName;
1459+
1460+
String lastName;
1461+
1462+
}
14091463
}

0 commit comments

Comments
 (0)