Skip to content

Commit b930e2f

Browse files
GH-2365 - Apply custom converters to additional fields of DTO projections, too.
1 parent fa9d64c commit b930e2f

File tree

3 files changed

+68
-10
lines changed

3 files changed

+68
-10
lines changed

src/main/java/org/springframework/data/neo4j/repository/query/DtoInstantiatingConverter.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@
3030
import org.springframework.data.mapping.PersistentPropertyAccessor;
3131
import org.springframework.data.mapping.PreferredConstructor;
3232
import org.springframework.data.mapping.PreferredConstructor.Parameter;
33-
import org.springframework.data.mapping.SimplePropertyHandler;
33+
import org.springframework.data.mapping.PropertyHandler;
3434
import org.springframework.data.mapping.model.ParameterValueProvider;
3535
import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext;
36+
import org.springframework.data.neo4j.core.mapping.Neo4jPersistentEntity;
37+
import org.springframework.data.neo4j.core.mapping.Neo4jPersistentProperty;
3638
import org.springframework.data.util.ClassTypeInformation;
3739
import org.springframework.util.Assert;
3840

@@ -76,10 +78,10 @@ public Object convert(EntityInstanceWithSource entityInstanceAndSource) {
7678
return entityInstance;
7779
}
7880

79-
PersistentEntity<?, ?> sourceEntity = context.getRequiredPersistentEntity(entityInstance.getClass());
81+
Neo4jPersistentEntity<?> sourceEntity = context.getRequiredPersistentEntity(entityInstance.getClass());
8082
PersistentPropertyAccessor sourceAccessor = sourceEntity.getPropertyAccessor(entityInstance);
8183

82-
PersistentEntity<?, ?> targetEntity = context.addPersistentEntity(ClassTypeInformation.from(targetType)).get();
84+
Neo4jPersistentEntity<?> targetEntity = context.addPersistentEntity(ClassTypeInformation.from(targetType)).get();
8385
PreferredConstructor<?, ? extends PersistentProperty<?>> constructor = targetEntity
8486
.getPersistenceConstructor();
8587

@@ -88,7 +90,7 @@ public Object convert(EntityInstanceWithSource entityInstanceAndSource) {
8890
.createInstance(targetEntity, new ParameterValueProvider() {
8991
@Override
9092
public Object getParameterValue(Parameter parameter) {
91-
PersistentProperty<?> targetProperty = targetEntity.getPersistentProperty(parameter.getName());
93+
Neo4jPersistentProperty targetProperty = targetEntity.getPersistentProperty(parameter.getName());
9294
if (targetProperty == null) {
9395
throw new MappingException("Cannot map constructor parameter " + parameter.getName()
9496
+ " to a property of class " + targetType);
@@ -99,7 +101,7 @@ public Object getParameterValue(Parameter parameter) {
99101
});
100102

101103
PersistentPropertyAccessor dtoAccessor = targetEntity.getPropertyAccessor(dto);
102-
targetEntity.doWithProperties((SimplePropertyHandler) property -> {
104+
targetEntity.doWithProperties((PropertyHandler<Neo4jPersistentProperty>) property -> {
103105

104106
if (constructor.isConstructorParameter(property)) {
105107
return;
@@ -112,7 +114,7 @@ public Object getParameterValue(Parameter parameter) {
112114
return dto;
113115
}
114116

115-
Object getPropertyValueFor(PersistentProperty<?> targetProperty, PersistentEntity<?, ?> sourceEntity,
117+
Object getPropertyValueFor(Neo4jPersistentProperty targetProperty, PersistentEntity<?, ?> sourceEntity,
116118
PersistentPropertyAccessor sourceAccessor, EntityInstanceWithSource entityInstanceAndSource) {
117119

118120
TypeSystem typeSystem = entityInstanceAndSource.getTypeSystem();
@@ -154,7 +156,7 @@ Object getPropertyValueFor(PersistentProperty<?> targetProperty, PersistentEntit
154156
singleValue = p -> context.getEntityConverter().read(actualType, p);
155157
} else {
156158
ClassTypeInformation<?> actualTargetType = ClassTypeInformation.from(actualType);
157-
singleValue = p -> context.getConversionService().readValue(p, actualTargetType, null);
159+
singleValue = p -> context.getConversionService().readValue(p, actualTargetType, targetProperty.getOptionalReadingConverter());
158160
}
159161

160162
if (targetProperty.isCollectionLike()) {

src/test/java/org/springframework/data/neo4j/integration/conversion_imperative/CustomTypesIT.java

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import java.util.Collection;
2121
import java.util.Collections;
22+
import java.util.Date;
2223
import java.util.HashSet;
2324
import java.util.List;
2425
import java.util.Set;
@@ -42,6 +43,7 @@
4243
import org.springframework.data.neo4j.config.AbstractNeo4jConfig;
4344
import org.springframework.data.neo4j.core.Neo4jOperations;
4445
import org.springframework.data.neo4j.core.convert.Neo4jConversions;
46+
import org.springframework.data.neo4j.core.support.DateLong;
4547
import org.springframework.data.neo4j.integration.shared.conversion.PersonWithCustomId;
4648
import org.springframework.data.neo4j.integration.shared.conversion.ThingWithCustomTypes;
4749
import org.springframework.data.neo4j.repository.Neo4jRepository;
@@ -87,7 +89,8 @@ void setupData() {
8789
try (Session session = driver.session()) {
8890
session.writeTransaction(transaction -> {
8991
transaction.run("MATCH (n) detach delete n").consume();
90-
transaction.run("CREATE (:CustomTypes{customType:'XYZ'})").consume();
92+
transaction.run("CREATE (:CustomTypes{customType:'XYZ', dateAsLong: 1630311077418})").consume();
93+
transaction.run("CREATE (:CustomTypes{customType:'ABC'})").consume();
9194
return null;
9295
});
9396
}
@@ -118,7 +121,7 @@ void deleteAllByCustomId() {
118121
.limit(2)
119122
.collect(Collectors.toList());
120123
try (
121-
Session session = driver.session(getSessionConfig());
124+
Session session = driver.session(getSessionConfig())
122125
) {
123126
ids.forEach(id -> session.writeTransaction(createPersonWithCustomId(id)));
124127
}
@@ -135,7 +138,9 @@ void deleteAllByCustomId() {
135138
@Test
136139
void findByConvertedCustomType(@Autowired EntityWithCustomTypePropertyRepository repository) {
137140

138-
assertThat(repository.findByCustomType(ThingWithCustomTypes.CustomType.of("XYZ"))).isNotNull();
141+
ThingWithCustomTypes xyz = repository.findByCustomType(ThingWithCustomTypes.CustomType.of("XYZ"));
142+
assertThat(xyz).isNotNull();
143+
assertThat(xyz.getDateAsLong()).isNotNull();
139144
}
140145

141146
@Test
@@ -144,6 +149,31 @@ void findByConvertedCustomTypeWithCustomQuery(@Autowired EntityWithCustomTypePro
144149
assertThat(repository.findByCustomTypeCustomQuery(ThingWithCustomTypes.CustomType.of("XYZ"))).isNotNull();
145150
}
146151

152+
@Test // GH-2365
153+
void customConverterShouldBeApplied(@Autowired EntityWithCustomTypePropertyRepository repository) {
154+
155+
ThingWithCustomTypes xyz = repository.defaultAttributeWithCoalesce(ThingWithCustomTypes.CustomType.of("XYZ"));
156+
assertThat(xyz).isNotNull();
157+
assertThat(xyz.getDateAsLong()).isInSameDayAs("2021-08-30");
158+
}
159+
160+
@Test // GH-2365
161+
void customConverterShouldBeAppliedWithCoalesce(@Autowired EntityWithCustomTypePropertyRepository repository) {
162+
163+
ThingWithCustomTypes abc = repository.defaultAttributeWithCoalesce(ThingWithCustomTypes.CustomType.of("ABC"));
164+
assertThat(abc).isNotNull();
165+
assertThat(abc.getDateAsLong()).isInSameDayAs("2021-09-21");
166+
}
167+
168+
@Test // GH-2365
169+
void converterAndProjection(@Autowired EntityWithCustomTypePropertyRepository repository) {
170+
171+
ThingWithCustomTypesProjection projection = repository.converterOnProjection(ThingWithCustomTypes.CustomType.of("XYZ"));
172+
assertThat(projection).isNotNull();
173+
assertThat(projection.dateAsLong).isInSameDayAs("2021-08-30");
174+
assertThat(projection.modified).isInSameDayAs("2021-09-21");
175+
}
176+
147177
@Test
148178
void findByConvertedCustomTypeWithSpELPropertyAccessQuery(
149179
@Autowired EntityWithCustomTypePropertyRepository repository) {
@@ -164,13 +194,27 @@ void findByConvertedDifferentTypeWithSpELObjectQuery(@Autowired EntityWithCustom
164194
assertThat(repository.findByDifferentTypeCustomQuery(ThingWithCustomTypes.DifferentType.of("XYZ"))).isNotNull();
165195
}
166196

197+
static class ThingWithCustomTypesProjection {
198+
199+
Date dateAsLong;
200+
201+
@DateLong
202+
Date modified;
203+
}
204+
167205
interface EntityWithCustomTypePropertyRepository extends Neo4jRepository<ThingWithCustomTypes, Long> {
168206

169207
ThingWithCustomTypes findByCustomType(ThingWithCustomTypes.CustomType customType);
170208

171209
@Query("MATCH (c:CustomTypes) WHERE c.customType = $customType return c")
172210
ThingWithCustomTypes findByCustomTypeCustomQuery(@Param("customType") ThingWithCustomTypes.CustomType customType);
173211

212+
@Query("MATCH (c:CustomTypes) WHERE c.customType = $customType return c, 1632200400000 as modified")
213+
ThingWithCustomTypesProjection converterOnProjection(@Param("customType") ThingWithCustomTypes.CustomType customType);
214+
215+
@Query("MATCH (thingWithCustomTypes:`CustomTypes`) WHERE thingWithCustomTypes.customType = $0 RETURN thingWithCustomTypes{.customType, dateAsLong: COALESCE(thingWithCustomTypes.dateAsLong, 1632200400000), .dateAsString, .id, __nodeLabels__: labels(thingWithCustomTypes), __internalNeo4jId__: id(thingWithCustomTypes)}")
216+
ThingWithCustomTypes defaultAttributeWithCoalesce(@Param("customType") ThingWithCustomTypes.CustomType customType);
217+
174218
@Query("MATCH (c:CustomTypes) WHERE c.customType = $differentType return c")
175219
ThingWithCustomTypes findByDifferentTypeCustomQuery(
176220
@Param("differentType") ThingWithCustomTypes.DifferentType differentType);

src/test/java/org/springframework/data/neo4j/integration/shared/conversion/ThingWithCustomTypes.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,18 @@ public void setDateAsLong(Date dateAsLong) {
6767
this.dateAsLong = dateAsLong;
6868
}
6969

70+
public Long getId() {
71+
return id;
72+
}
73+
74+
public Date getDateAsLong() {
75+
return dateAsLong;
76+
}
77+
78+
public Date getDateAsString() {
79+
return dateAsString;
80+
}
81+
7082
/**
7183
* Custom type to convert
7284
*/

0 commit comments

Comments
 (0)