Skip to content

Commit 53cb12c

Browse files
GH-2365 - Apply custom converters to additional fields of DTO projections, too.
1 parent 48cbaca commit 53cb12c

File tree

3 files changed

+66
-11
lines changed

3 files changed

+66
-11
lines changed

src/main/java/org/springframework/data/neo4j/core/mapping/DtoInstantiatingConverter.java

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import org.springframework.data.mapping.PersistentPropertyAccessor;
3434
import org.springframework.data.mapping.PreferredConstructor;
3535
import org.springframework.data.mapping.PreferredConstructor.Parameter;
36-
import org.springframework.data.mapping.PropertyHandler;
3736
import org.springframework.data.mapping.SimplePropertyHandler;
3837
import org.springframework.data.mapping.model.ParameterValueProvider;
3938
import org.springframework.data.util.ClassTypeInformation;
@@ -129,10 +128,10 @@ public Object convert(EntityInstanceWithSource entityInstanceAndSource) {
129128
return entityInstance;
130129
}
131130

132-
PersistentEntity<?, ?> sourceEntity = context.getRequiredPersistentEntity(entityInstance.getClass());
131+
Neo4jPersistentEntity<?> sourceEntity = context.getRequiredPersistentEntity(entityInstance.getClass());
133132
PersistentPropertyAccessor<Object> sourceAccessor = sourceEntity.getPropertyAccessor(entityInstance);
134133

135-
PersistentEntity<?, ?> targetEntity = context.addPersistentEntity(ClassTypeInformation.from(targetType)).get();
134+
Neo4jPersistentEntity<?> targetEntity = context.addPersistentEntity(ClassTypeInformation.from(targetType)).get();
136135
PreferredConstructor<?, ? extends PersistentProperty<?>> constructor = targetEntity
137136
.getPersistenceConstructor();
138137

@@ -141,7 +140,7 @@ public Object convert(EntityInstanceWithSource entityInstanceAndSource) {
141140
.createInstance(targetEntity, new ParameterValueProvider() {
142141
@Override
143142
public Object getParameterValue(Parameter parameter) {
144-
PersistentProperty<?> targetProperty = targetEntity.getPersistentProperty(parameter.getName());
143+
Neo4jPersistentProperty targetProperty = targetEntity.getPersistentProperty(parameter.getName());
145144
if (targetProperty == null) {
146145
throw new MappingException("Cannot map constructor parameter " + parameter.getName()
147146
+ " to a property of class " + targetType);
@@ -152,15 +151,15 @@ public Object getParameterValue(Parameter parameter) {
152151
});
153152

154153
PersistentPropertyAccessor<Object> dtoAccessor = targetEntity.getPropertyAccessor(dto);
155-
targetEntity.doWithAll((PropertyHandler) property ->
154+
targetEntity.doWithAll(property ->
156155
setPropertyOnDtoObject(entityInstanceAndSource, sourceEntity, sourceAccessor, constructor, dtoAccessor, property));
157156

158157
return dto;
159158
}
160159

161160
private void setPropertyOnDtoObject(EntityInstanceWithSource entityInstanceAndSource, PersistentEntity<?, ?> sourceEntity,
162161
PersistentPropertyAccessor<Object> sourceAccessor, PreferredConstructor<?, ?> constructor,
163-
PersistentPropertyAccessor<Object> dtoAccessor, PersistentProperty<?> property) {
162+
PersistentPropertyAccessor<Object> dtoAccessor, Neo4jPersistentProperty property) {
164163

165164
if (constructor.isConstructorParameter(property)) {
166165
return;
@@ -171,7 +170,7 @@ private void setPropertyOnDtoObject(EntityInstanceWithSource entityInstanceAndSo
171170
}
172171

173172
@Nullable
174-
Object getPropertyValueFor(PersistentProperty<?> targetProperty, PersistentEntity<?, ?> sourceEntity,
173+
Object getPropertyValueFor(Neo4jPersistentProperty targetProperty, PersistentEntity<?, ?> sourceEntity,
175174
PersistentPropertyAccessor sourceAccessor, EntityInstanceWithSource entityInstanceAndSource) {
176175

177176
TypeSystem typeSystem = entityInstanceAndSource.getTypeSystem();
@@ -212,7 +211,7 @@ Object getPropertyValueFor(PersistentProperty<?> targetProperty, PersistentEntit
212211
singleValue = p -> context.getEntityConverter().read(actualType, p);
213212
} else {
214213
ClassTypeInformation<?> actualTargetType = ClassTypeInformation.from(actualType);
215-
singleValue = p -> context.getConversionService().readValue(p, actualTargetType, null);
214+
singleValue = p -> context.getConversionService().readValue(p, actualTargetType, targetProperty.getOptionalReadingConverter());
216215
}
217216

218217
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.core.DatabaseSelectionProvider;
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.core.transaction.Neo4jBookmarkManager;
4648
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
4749
import org.springframework.data.neo4j.integration.shared.conversion.PersonWithCustomId;
@@ -90,7 +92,8 @@ void setupData() {
9092
try (Session session = driver.session(bookmarkCapture.createSessionConfig())) {
9193
session.writeTransaction(transaction -> {
9294
transaction.run("MATCH (n) detach delete n").consume();
93-
transaction.run("CREATE (:CustomTypes{customType:'XYZ'})").consume();
95+
transaction.run("CREATE (:CustomTypes{customType:'XYZ', dateAsLong: 1630311077418})").consume();
96+
transaction.run("CREATE (:CustomTypes{customType:'ABC'})").consume();
9497
return null;
9598
});
9699
bookmarkCapture.seedWith(session.lastBookmark());
@@ -124,7 +127,7 @@ void deleteAllByCustomId() {
124127
.limit(2)
125128
.collect(Collectors.toList());
126129
try (
127-
Session session = driver.session(bookmarkCapture.createSessionConfig());
130+
Session session = driver.session(bookmarkCapture.createSessionConfig())
128131
) {
129132
ids.forEach(id -> session.writeTransaction(createPersonWithCustomId(id)));
130133
bookmarkCapture.seedWith(session.lastBookmark());
@@ -143,7 +146,9 @@ void deleteAllByCustomId() {
143146
@Test
144147
void findByConvertedCustomType(@Autowired EntityWithCustomTypePropertyRepository repository) {
145148

146-
assertThat(repository.findByCustomType(ThingWithCustomTypes.CustomType.of("XYZ"))).isNotNull();
149+
ThingWithCustomTypes xyz = repository.findByCustomType(ThingWithCustomTypes.CustomType.of("XYZ"));
150+
assertThat(xyz).isNotNull();
151+
assertThat(xyz.getDateAsLong()).isNotNull();
147152
}
148153

149154
@Test
@@ -152,6 +157,31 @@ void findByConvertedCustomTypeWithCustomQuery(@Autowired EntityWithCustomTypePro
152157
assertThat(repository.findByCustomTypeCustomQuery(ThingWithCustomTypes.CustomType.of("XYZ"))).isNotNull();
153158
}
154159

160+
@Test // GH-2365
161+
void customConverterShouldBeApplied(@Autowired EntityWithCustomTypePropertyRepository repository) {
162+
163+
ThingWithCustomTypes xyz = repository.defaultAttributeWithCoalesce(ThingWithCustomTypes.CustomType.of("XYZ"));
164+
assertThat(xyz).isNotNull();
165+
assertThat(xyz.getDateAsLong()).isInSameDayAs("2021-08-30");
166+
}
167+
168+
@Test // GH-2365
169+
void customConverterShouldBeAppliedWithCoalesce(@Autowired EntityWithCustomTypePropertyRepository repository) {
170+
171+
ThingWithCustomTypes abc = repository.defaultAttributeWithCoalesce(ThingWithCustomTypes.CustomType.of("ABC"));
172+
assertThat(abc).isNotNull();
173+
assertThat(abc.getDateAsLong()).isInSameDayAs("2021-09-21");
174+
}
175+
176+
@Test // GH-2365
177+
void converterAndProjection(@Autowired EntityWithCustomTypePropertyRepository repository) {
178+
179+
ThingWithCustomTypesProjection projection = repository.converterOnProjection(ThingWithCustomTypes.CustomType.of("XYZ"));
180+
assertThat(projection).isNotNull();
181+
assertThat(projection.dateAsLong).isInSameDayAs("2021-08-30");
182+
assertThat(projection.modified).isInSameDayAs("2021-09-21");
183+
}
184+
155185
@Test
156186
void findByConvertedCustomTypeWithSpELPropertyAccessQuery(
157187
@Autowired EntityWithCustomTypePropertyRepository repository) {
@@ -172,13 +202,27 @@ void findByConvertedDifferentTypeWithSpELObjectQuery(@Autowired EntityWithCustom
172202
assertThat(repository.findByDifferentTypeCustomQuery(ThingWithCustomTypes.DifferentType.of("XYZ"))).isNotNull();
173203
}
174204

205+
static class ThingWithCustomTypesProjection {
206+
207+
Date dateAsLong;
208+
209+
@DateLong
210+
Date modified;
211+
}
212+
175213
interface EntityWithCustomTypePropertyRepository extends Neo4jRepository<ThingWithCustomTypes, Long> {
176214

177215
ThingWithCustomTypes findByCustomType(ThingWithCustomTypes.CustomType customType);
178216

179217
@Query("MATCH (c:CustomTypes) WHERE c.customType = $customType return c")
180218
ThingWithCustomTypes findByCustomTypeCustomQuery(@Param("customType") ThingWithCustomTypes.CustomType customType);
181219

220+
@Query("MATCH (c:CustomTypes) WHERE c.customType = $customType return c, 1632200400000 as modified")
221+
ThingWithCustomTypesProjection converterOnProjection(@Param("customType") ThingWithCustomTypes.CustomType customType);
222+
223+
@Query("MATCH (thingWithCustomTypes:`CustomTypes`) WHERE thingWithCustomTypes.customType = $0 RETURN thingWithCustomTypes{.customType, dateAsLong: COALESCE(thingWithCustomTypes.dateAsLong, 1632200400000), .dateAsString, .id, __nodeLabels__: labels(thingWithCustomTypes), __internalNeo4jId__: id(thingWithCustomTypes)}")
224+
ThingWithCustomTypes defaultAttributeWithCoalesce(@Param("customType") ThingWithCustomTypes.CustomType customType);
225+
182226
@Query("MATCH (c:CustomTypes) WHERE c.customType = $differentType return c")
183227
ThingWithCustomTypes findByDifferentTypeCustomQuery(
184228
@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)