Skip to content

Commit 8ab3e41

Browse files
committed
GH-2639 - Fix relationship detection in converter.
In an inheritance situation, SDN might not search in the result for the right name of the list of relationships from the generic query. Closes #2639
1 parent c7f0c5e commit 8ab3e41

File tree

13 files changed

+624
-22
lines changed

13 files changed

+624
-22
lines changed

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

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -299,10 +299,10 @@ private static MapAccessor mergeRootNodeWithRecord(Node node, MapAccessor record
299299
private <ET> ET map(MapAccessor queryResult, MapAccessor allValues, Neo4jPersistentEntity<ET> nodeDescription) {
300300
Collection<Relationship> relationshipsFromResult = extractRelationships(allValues);
301301
Collection<Node> nodesFromResult = extractNodes(allValues);
302-
return map(queryResult, nodeDescription, null, null, relationshipsFromResult, nodesFromResult);
302+
return map(queryResult, nodeDescription, nodeDescription, null, null, relationshipsFromResult, nodesFromResult);
303303
}
304304

305-
private <ET> ET map(MapAccessor queryResult, Neo4jPersistentEntity<ET> nodeDescription,
305+
private <ET> ET map(MapAccessor queryResult, Neo4jPersistentEntity<ET> nodeDescription, NodeDescription<?> genericTargetNodeDescription,
306306
@Nullable Object lastMappedEntity, @Nullable RelationshipDescription relationshipDescription, Collection<Relationship> relationshipsFromResult, Collection<Node> nodesFromResult) {
307307

308308
// if the given result does not contain an identifier to the mapped object cannot get temporarily saved
@@ -327,7 +327,7 @@ private <ET> ET map(MapAccessor queryResult, Neo4jPersistentEntity<ET> nodeDescr
327327
Neo4jPersistentEntity<ET> concreteNodeDescription = (Neo4jPersistentEntity<ET>) nodeDescriptionAndLabels
328328
.getNodeDescription();
329329

330-
ET instance = instantiate(concreteNodeDescription, queryResult,
330+
ET instance = instantiate(concreteNodeDescription, genericTargetNodeDescription, queryResult,
331331
nodeDescriptionAndLabels.getDynamicLabels(), lastMappedEntity, relationshipsFromResult, nodesFromResult);
332332

333333
knownObjects.removeFromInCreation(internalId);
@@ -475,7 +475,7 @@ private boolean containsOnePlainNode(MapAccessor queryResult) {
475475
.filter(value -> value.hasType(nodeType)).count() == 1L;
476476
}
477477

478-
private <ET> ET instantiate(Neo4jPersistentEntity<ET> nodeDescription, MapAccessor values,
478+
private <ET> ET instantiate(Neo4jPersistentEntity<ET> nodeDescription, NodeDescription<?> genericNodeDescription, MapAccessor values,
479479
Collection<String> surplusLabels, @Nullable Object lastMappedEntity,
480480
Collection<Relationship> relationshipsFromResult, Collection<Node> nodesFromResult) {
481481

@@ -496,12 +496,12 @@ public <T> T getParameterValue(Parameter<T, Neo4jPersistentProperty> parameter)
496496
// If we cannot find any value it does not mean that there isn't any.
497497
// The result set might contain associations not named CONCRETE_TYPE_TARGET but ABSTRACT_TYPE_TARGET.
498498
// For this we bubble up the hierarchy of NodeDescriptions.
499-
result = createInstanceOfRelationships(matchingProperty, values, relationshipDescription, nodeDescription, relationshipsFromResult, nodesFromResult)
499+
result = createInstanceOfRelationships(matchingProperty, values, relationshipDescription, nodeDescription, genericNodeDescription, relationshipsFromResult, nodesFromResult)
500500
.orElseGet(() -> {
501501
NodeDescription<?> parentNodeDescription = nodeDescription.getParentNodeDescription();
502502
T resultValue = null;
503503
while (parentNodeDescription != null) {
504-
Optional<Object> value = createInstanceOfRelationships(matchingProperty, values, relationshipDescription, parentNodeDescription, relationshipsFromResult, nodesFromResult);
504+
Optional<Object> value = createInstanceOfRelationships(matchingProperty, values, relationshipDescription, parentNodeDescription, parentNodeDescription, relationshipsFromResult, nodesFromResult);
505505
if (value.isPresent()) {
506506
resultValue = (T) value.get();
507507
break;
@@ -594,7 +594,7 @@ private AssociationHandler<Neo4jPersistentProperty> populateFrom(MapAccessor que
594594
&& propertyValueNotNull;
595595

596596
if (populatedCollection) {
597-
createInstanceOfRelationships(persistentProperty, queryResult, (RelationshipDescription) association, baseDescription, relationshipsFromResult, nodesFromResult, false)
597+
createInstanceOfRelationships(persistentProperty, queryResult, (RelationshipDescription) association, baseDescription, baseDescription, relationshipsFromResult, nodesFromResult, false)
598598
.ifPresent(value -> {
599599
Collection<?> providedCollection = (Collection<?>) value;
600600
Collection<?> existingValue = (Collection<?>) propertyValue;
@@ -617,7 +617,7 @@ private AssociationHandler<Neo4jPersistentProperty> populateFrom(MapAccessor que
617617
return;
618618
}
619619

620-
createInstanceOfRelationships(persistentProperty, queryResult, (RelationshipDescription) association, baseDescription, relationshipsFromResult, nodesFromResult)
620+
createInstanceOfRelationships(persistentProperty, queryResult, (RelationshipDescription) association, baseDescription, baseDescription, relationshipsFromResult, nodesFromResult)
621621
.ifPresent(value -> propertyAccessor.setProperty(persistentProperty, value));
622622
};
623623
}
@@ -641,13 +641,13 @@ private void mergeCollections(RelationshipDescription relationshipDescription, C
641641
}
642642

643643
private Optional<Object> createInstanceOfRelationships(Neo4jPersistentProperty persistentProperty, MapAccessor values,
644-
RelationshipDescription relationshipDescription, NodeDescription<?> baseDescription, Collection<Relationship> relationshipsFromResult,
644+
RelationshipDescription relationshipDescription, NodeDescription<?> baseDescription, NodeDescription<?> genericNodeDescription, Collection<Relationship> relationshipsFromResult,
645645
Collection<Node> nodesFromResult) {
646-
return createInstanceOfRelationships(persistentProperty, values, relationshipDescription, baseDescription, relationshipsFromResult, nodesFromResult, true);
646+
return createInstanceOfRelationships(persistentProperty, values, relationshipDescription, baseDescription, genericNodeDescription, relationshipsFromResult, nodesFromResult, true);
647647
}
648648

649649
private Optional<Object> createInstanceOfRelationships(Neo4jPersistentProperty persistentProperty, MapAccessor values,
650-
RelationshipDescription relationshipDescription, NodeDescription<?> baseDescription, Collection<Relationship> relationshipsFromResult,
650+
RelationshipDescription relationshipDescription, NodeDescription<?> baseDescription, NodeDescription<?> genericNodeDescription, Collection<Relationship> relationshipsFromResult,
651651
Collection<Node> nodesFromResult, boolean fetchMore) {
652652

653653
String typeOfRelationship = relationshipDescription.getType();
@@ -681,7 +681,7 @@ private Optional<Object> createInstanceOfRelationships(Neo4jPersistentProperty p
681681
mappedObjectHandler = (type, mappedObject) -> value.add(mappedObject);
682682
}
683683

684-
String collectionName = relationshipDescription.generateRelatedNodesCollectionName(baseDescription);
684+
String collectionName = relationshipDescription.generateRelatedNodesCollectionName(genericNodeDescription);
685685

686686
Value list = values.get(collectionName);
687687

@@ -728,23 +728,24 @@ private Optional<Object> createInstanceOfRelationships(Neo4jPersistentProperty p
728728
if (fetchMore) {
729729
mappedObject = sourceNodeId != null && sourceNodeId.equals(targetNodeId)
730730
? knownObjects.getObject("N" + sourceNodeId)
731-
: map(possibleValueNode, concreteTargetNodeDescription, null, null, relationshipsFromResult, nodesFromResult);
731+
: map(possibleValueNode, concreteTargetNodeDescription, genericNodeDescription, null, null, relationshipsFromResult, nodesFromResult);
732732
} else {
733733
Object objectFromStore = knownObjects.getObject("N" + targetNodeId);
734734
mappedObject = objectFromStore != null
735735
? objectFromStore
736-
: map(possibleValueNode, concreteTargetNodeDescription, null, null, relationshipsFromResult, nodesFromResult);
736+
: map(possibleValueNode, concreteTargetNodeDescription, genericNodeDescription, null, null, relationshipsFromResult, nodesFromResult);
737737
}
738738

739739
if (relationshipDescription.hasRelationshipProperties()) {
740740
Object relationshipProperties;
741+
Neo4jPersistentEntity<?> relationshipPropertiesEntity = (Neo4jPersistentEntity<?>) relationshipDescription.getRelationshipPropertiesEntity();
741742
if (fetchMore) {
742-
relationshipProperties = map(possibleRelationship, (Neo4jPersistentEntity<?>) relationshipDescription.getRelationshipPropertiesEntity(), mappedObject, relationshipDescription, relationshipsFromResult, nodesFromResult);
743+
relationshipProperties = map(possibleRelationship, relationshipPropertiesEntity, relationshipPropertiesEntity, mappedObject, relationshipDescription, relationshipsFromResult, nodesFromResult);
743744
} else {
744745
Object objectFromStore = knownObjects.getObject(getInternalId(possibleRelationship, relationshipDescription.getDirection().name()));
745746
relationshipProperties = objectFromStore != null
746747
? objectFromStore
747-
: map(possibleRelationship, (Neo4jPersistentEntity<?>) relationshipDescription.getRelationshipPropertiesEntity(), mappedObject, relationshipDescription, relationshipsFromResult, nodesFromResult);
748+
: map(possibleRelationship, relationshipPropertiesEntity, relationshipPropertiesEntity, mappedObject, relationshipDescription, relationshipsFromResult, nodesFromResult);
748749
}
749750
relationshipsAndProperties.add(relationshipProperties);
750751
mappedObjectHandler.accept(possibleRelationship.type(), relationshipProperties);
@@ -764,29 +765,30 @@ private Optional<Object> createInstanceOfRelationships(Neo4jPersistentProperty p
764765

765766
Object valueEntry;
766767
if (fetchMore) {
767-
valueEntry = map(relatedEntity, concreteTargetNodeDescription, null, null, relationshipsFromResult, nodesFromResult);
768+
valueEntry = map(relatedEntity, concreteTargetNodeDescription, genericNodeDescription, null, null, relationshipsFromResult, nodesFromResult);
768769
} else {
769770
Object objectFromStore = knownObjects.getObject(getInternalId(relatedEntity, null));
770771
valueEntry = objectFromStore != null
771772
? objectFromStore
772-
: map(relatedEntity, concreteTargetNodeDescription, null, null, relationshipsFromResult, nodesFromResult);
773+
: map(relatedEntity, concreteTargetNodeDescription, genericNodeDescription, null, null, relationshipsFromResult, nodesFromResult);
773774
}
774775

775776
if (relationshipDescription.hasRelationshipProperties()) {
776-
String sourceLabel = relationshipDescription.getSource().getMostAbstractParentLabel(baseDescription);
777+
String sourceLabel = relationshipDescription.getSource().getMostAbstractParentLabel(genericNodeDescription);
777778
String relationshipSymbolicName = sourceLabel
778779
+ RelationshipDescription.NAME_OF_RELATIONSHIP + targetLabel;
779780
Relationship relatedEntityRelationship = relatedEntity.get(relationshipSymbolicName)
780781
.asRelationship();
781782

782783
Object relationshipProperties;
784+
Neo4jPersistentEntity<?> relationshipPropertiesEntity = (Neo4jPersistentEntity<?>) relationshipDescription.getRelationshipPropertiesEntity();
783785
if (fetchMore) {
784-
relationshipProperties = map(relatedEntityRelationship, (Neo4jPersistentEntity<?>) relationshipDescription.getRelationshipPropertiesEntity(), valueEntry, relationshipDescription, relationshipsFromResult, nodesFromResult);
786+
relationshipProperties = map(relatedEntityRelationship, relationshipPropertiesEntity, relationshipPropertiesEntity, valueEntry, relationshipDescription, relationshipsFromResult, nodesFromResult);
785787
} else {
786788
Object objectFromStore = knownObjects.getObject(getInternalId(relatedEntityRelationship, relationshipDescription.getDirection().name()));
787789
relationshipProperties = objectFromStore != null
788790
? objectFromStore
789-
: map(relatedEntityRelationship, (Neo4jPersistentEntity<?>) relationshipDescription.getRelationshipPropertiesEntity(), valueEntry, relationshipDescription, relationshipsFromResult, nodesFromResult);
791+
: map(relatedEntityRelationship, relationshipPropertiesEntity, relationshipPropertiesEntity, valueEntry, relationshipDescription, relationshipsFromResult, nodesFromResult);
790792
}
791793

792794
relationshipsAndProperties.add(relationshipProperties);
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2011-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.issues.gh2639;
17+
18+
import org.springframework.data.neo4j.core.schema.GeneratedValue;
19+
import org.springframework.data.neo4j.core.schema.Id;
20+
import org.springframework.data.neo4j.core.schema.Node;
21+
import org.springframework.data.neo4j.core.schema.Relationship;
22+
23+
import java.util.List;
24+
import java.util.StringJoiner;
25+
26+
/**
27+
* Root node
28+
*/
29+
@Node
30+
public class Company {
31+
32+
@Id
33+
@GeneratedValue
34+
private Long id;
35+
36+
private final String name;
37+
@Relationship(type = "EMPLOYEE")
38+
private final List<Person> employees;
39+
40+
41+
public Company(String name, List<Person> employees) {
42+
this.name = name;
43+
this.employees = employees;
44+
}
45+
46+
public void addEmployee(Person person) {
47+
employees.add(person);
48+
}
49+
50+
public List<Person> getEmployees() {
51+
return employees;
52+
}
53+
54+
@Override
55+
public String toString() {
56+
return new StringJoiner(", ", Company.class.getSimpleName() + "[", "]")
57+
.add("id=" + id)
58+
.add("name='" + name + "'")
59+
.add("employees=" + employees)
60+
.toString();
61+
}
62+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2011-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.issues.gh2639;
17+
18+
import org.springframework.data.neo4j.repository.Neo4jRepository;
19+
20+
/**
21+
* Fetch the root node by name.
22+
*/
23+
public interface CompanyRepository extends Neo4jRepository<Company, Long> {
24+
25+
Company findByName(String name);
26+
27+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2011-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.issues.gh2639;
17+
18+
import org.springframework.data.neo4j.core.schema.Node;
19+
20+
import java.util.List;
21+
import java.util.StringJoiner;
22+
23+
/**
24+
* Developer holds the specific relationship we are trying to map
25+
* in this test case.
26+
*/
27+
@Node
28+
public class Developer extends Person {
29+
30+
private final List<LanguageRelationship> programmingLanguages;
31+
private final String name;
32+
33+
public Developer(String name, List<LanguageRelationship> programmingLanguages) {
34+
this.name = name;
35+
this.programmingLanguages = programmingLanguages;
36+
}
37+
38+
public List<LanguageRelationship> getProgrammingLanguages() {
39+
return programmingLanguages;
40+
}
41+
42+
public String getName() {
43+
return name;
44+
}
45+
46+
@Override
47+
public String toString() {
48+
return new StringJoiner(", ", Developer.class.getSimpleName() + "[", "]")
49+
.add("name='" + name + "'")
50+
.add("programmingLanguages=" + programmingLanguages)
51+
.toString();
52+
}
53+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2011-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.issues.gh2639;
17+
18+
import org.springframework.data.neo4j.core.schema.Node;
19+
20+
import java.util.Objects;
21+
22+
/**
23+
* @author Gerrit Meier
24+
*/
25+
@Node
26+
public class Enterprise extends Inventor {
27+
28+
final String someEnterpriseProperty;
29+
30+
public Enterprise(String name, String someEnterpriseProperty) {
31+
super(name);
32+
this.someEnterpriseProperty = someEnterpriseProperty;
33+
}
34+
35+
@Override
36+
public boolean equals(Object o) {
37+
if (this == o) {
38+
return true;
39+
}
40+
if (o == null || getClass() != o.getClass()) {
41+
return false;
42+
}
43+
Enterprise that = (Enterprise) o;
44+
return someEnterpriseProperty.equals(that.someEnterpriseProperty);
45+
}
46+
47+
@Override
48+
public int hashCode() {
49+
return Objects.hash(someEnterpriseProperty);
50+
}
51+
}

0 commit comments

Comments
 (0)