Skip to content

Commit f64d5f8

Browse files
committed
GH-2305 - Add association support for DTO projections.
Closes #2305
1 parent f23071b commit f64d5f8

File tree

3 files changed

+53
-8
lines changed

3 files changed

+53
-8
lines changed

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

+14-8
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
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;
3637
import org.springframework.data.mapping.SimplePropertyHandler;
3738
import org.springframework.data.mapping.model.ParameterValueProvider;
3839
import org.springframework.data.util.ClassTypeInformation;
@@ -151,17 +152,22 @@ public Object getParameterValue(Parameter parameter) {
151152
});
152153

153154
PersistentPropertyAccessor<Object> dtoAccessor = targetEntity.getPropertyAccessor(dto);
154-
targetEntity.doWithProperties((SimplePropertyHandler) property -> {
155+
targetEntity.doWithAll((PropertyHandler) property ->
156+
setPropertyOnDtoObject(entityInstanceAndSource, sourceEntity, sourceAccessor, constructor, dtoAccessor, property));
155157

156-
if (constructor.isConstructorParameter(property)) {
157-
return;
158-
}
158+
return dto;
159+
}
159160

160-
Object propertyValue = getPropertyValueFor(property, sourceEntity, sourceAccessor, entityInstanceAndSource);
161-
dtoAccessor.setProperty(property, propertyValue);
162-
});
161+
private void setPropertyOnDtoObject(EntityInstanceWithSource entityInstanceAndSource, PersistentEntity<?, ?> sourceEntity,
162+
PersistentPropertyAccessor<Object> sourceAccessor, PreferredConstructor<?, ?> constructor,
163+
PersistentPropertyAccessor<Object> dtoAccessor, PersistentProperty<?> property) {
163164

164-
return dto;
165+
if (constructor.isConstructorParameter(property)) {
166+
return;
167+
}
168+
169+
Object propertyValue = getPropertyValueFor(property, sourceEntity, sourceAccessor, entityInstanceAndSource);
170+
dtoAccessor.setProperty(property, propertyValue);
165171
}
166172

167173
@Nullable

src/test/java/org/springframework/data/neo4j/integration/imperative/RepositoryIT.java

+30
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,33 @@ void shouldFindOneToOneWithWildcardReturn(@Autowired OneToOneRepository reposito
12881288
List<OneToOneSource> oneToOnes = repository.findAllWithCustomQueryReturnStar();
12891289
assertOneToOneScenario(oneToOnes);
12901290
}
1291+
1292+
private void createOneToOneScenarioForNullValues() {
1293+
doWithSession(session -> {
1294+
try (Transaction tx = session.beginTransaction()) {
1295+
tx.run("CREATE (s:OneToOneSource {name: 's1'}) -[:OWNS]->(t:OneToOneTarget {name: 't1'})");
1296+
tx.run("CREATE (s:OneToOneSource {name: 's2'})");
1297+
tx.commit();
1298+
}
1299+
return null;
1300+
}
1301+
);
1302+
}
1303+
1304+
private void assertOneToOneScenarioWithNulls(List<OneToOneSource.OneToOneSourceProjection> oneToOnes) {
1305+
assertThat(oneToOnes).hasSize(2);
1306+
assertThat(oneToOnes).extracting(OneToOneSource.OneToOneSourceProjection::getName).containsExactlyInAnyOrder("s1", "s2");
1307+
assertThat(oneToOnes).filteredOn(s -> s.getTarget() != null)
1308+
.extracting(s -> s.getTarget().getName()).containsExactly("t1");
1309+
}
1310+
1311+
@Test // GH-2305
1312+
void shouldFindOneToOneWithNullValues(@Autowired OneToOneRepository repository) {
1313+
createOneToOneScenarioForNullValues();
1314+
1315+
List<OneToOneSource.OneToOneSourceProjection> oneToOnes = repository.findAllWithNullValues();
1316+
assertOneToOneScenarioWithNulls(oneToOnes);
1317+
}
12911318
}
12921319

12931320
@Nested
@@ -4118,6 +4145,9 @@ interface OneToOneRepository extends Neo4jRepository<OneToOneSource, String> {
41184145

41194146
@Query("MATCH (p1:#{#staticLabels})-[r:OWNS]-(p2) return *")
41204147
List<OneToOneSource> findAllWithCustomQueryReturnStar();
4148+
4149+
@Query("MATCH (p1:#{#staticLabels}) OPTIONAL MATCH (p1)-[r:OWNS]->(p2:OneToOneTarget) return p1, r, p2")
4150+
List<OneToOneSource.OneToOneSourceProjection> findAllWithNullValues();
41214151
}
41224152

41234153
interface RelationshipRepository extends Neo4jRepository<PersonWithRelationship, Long> {

src/test/java/org/springframework/data/neo4j/integration/shared/common/OneToOneSource.java

+9
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,13 @@ public class OneToOneSource {
3535

3636
@Relationship("OWNS")
3737
private OneToOneTarget target;
38+
39+
/**
40+
* Simple DTO projection for OneToOneSource
41+
*/
42+
@Data
43+
public static class OneToOneSourceProjection {
44+
String name;
45+
OneToOneTarget target;
46+
}
3847
}

0 commit comments

Comments
 (0)