Skip to content

Commit 1337e3c

Browse files
committed
GH-3036 - Use correct type for mapping.
If there is a class hierarchy scenario, SDN will try to use the best matching child class. In some cases the parent, non-abstract class is already the best fit and should be used. Closes #3036 Signed-off-by: Gerrit Meier <meistermeier@gmail.com>
1 parent 2185cec commit 1337e3c

File tree

6 files changed

+168
-3
lines changed

6 files changed

+168
-3
lines changed

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ public NodeDescriptionAndLabels deriveConcreteNodeDescription(NodeDescription<?>
104104

105105
private NodeDescriptionAndLabels computeConcreteNodeDescription(NodeDescription<?> entityDescription, @Nullable List<String> labels) {
106106

107-
boolean isConcreteClassThatFulfillsEverything = !Modifier.isAbstract(entityDescription.getUnderlyingClass().getModifiers()) && entityDescription.getStaticLabels().containsAll(labels);
107+
var isAbstractClassOrInterface = Modifier.isAbstract(entityDescription.getUnderlyingClass().getModifiers());
108+
boolean isConcreteClassThatFulfillsEverything = !isAbstractClassOrInterface && entityDescription.getStaticLabels().containsAll(labels);
108109

109110
if (labels == null || labels.isEmpty() || isConcreteClassThatFulfillsEverything) {
110111
return new NodeDescriptionAndLabels(entityDescription, Collections.emptyList());
@@ -119,9 +120,12 @@ private NodeDescriptionAndLabels computeConcreteNodeDescription(NodeDescription<
119120

120121
if (!haystack.isEmpty()) {
121122

122-
NodeDescription<?> mostMatchingNodeDescription = null;
123+
NodeDescription<?> mostMatchingNodeDescription = !isAbstractClassOrInterface ? entityDescription : null;
124+
List<String> mostMatchingStaticLabels = !isAbstractClassOrInterface ? entityDescription.getStaticLabels() : null;
123125
Map<NodeDescription<?>, Integer> unmatchedLabelsCache = new HashMap<>();
124-
List<String> mostMatchingStaticLabels = null;
126+
if (!isAbstractClassOrInterface) {
127+
unmatchedLabelsCache.put(mostMatchingNodeDescription, labels.size() - mostMatchingStaticLabels.size());
128+
}
125129

126130
for (NodeDescription<?> nd : haystack) {
127131

src/test/java/org/springframework/data/neo4j/integration/issues/IssuesIT.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@
196196
import org.springframework.data.neo4j.integration.issues.gh2973.RelationshipB;
197197
import org.springframework.data.neo4j.integration.issues.gh2973.RelationshipC;
198198
import org.springframework.data.neo4j.integration.issues.gh2973.RelationshipD;
199+
import org.springframework.data.neo4j.integration.issues.gh3036.Vehicle;
200+
import org.springframework.data.neo4j.integration.issues.gh3036.VehicleRepository;
199201
import org.springframework.data.neo4j.integration.issues.qbe.A;
200202
import org.springframework.data.neo4j.integration.issues.qbe.ARepository;
201203
import org.springframework.data.neo4j.integration.issues.qbe.B;
@@ -1771,6 +1773,22 @@ void abstractedRelationshipTypesShouldBeMappedCorrectly(@Autowired Gh2973Reposit
17711773
assertThat(relationshipsCFail.get(0)).isNotExactlyInstanceOf(BaseRelationship.class);
17721774
}
17731775

1776+
@Tag("GH-3036")
1777+
@Test
1778+
void asdf(@Autowired VehicleRepository repository) {
1779+
var vehicleWithoutDynamicLabels = new Vehicle();
1780+
var vehicleWithOneDynamicLabel = new Vehicle();
1781+
vehicleWithOneDynamicLabel.setLabels(Set.of("label1"));
1782+
var vehicleWithTwoDynamicLabels = new Vehicle();
1783+
vehicleWithTwoDynamicLabels.setLabels(Set.of("label1", "label2"));
1784+
1785+
repository.saveAll(List.of(vehicleWithoutDynamicLabels, vehicleWithOneDynamicLabel, vehicleWithTwoDynamicLabels));
1786+
1787+
var vehicles = repository.findAllVehicles();
1788+
assertThat(vehicles).hasOnlyElementsOfType(Vehicle.class);
1789+
1790+
}
1791+
17741792
@Configuration
17751793
@EnableTransactionManagement
17761794
@EnableNeo4jRepositories(namedQueriesLocation = "more-custom-queries.properties")
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2011-2025 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.gh3036;
17+
18+
import org.springframework.data.neo4j.core.schema.Node;
19+
20+
/**
21+
* Detailed implementation candidate
22+
*/
23+
@Node(primaryLabel = "Car")
24+
public class Car extends Vehicle {
25+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2011-2025 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.gh3036;
17+
18+
import org.springframework.data.neo4j.core.schema.Node;
19+
20+
/**
21+
* Detailed implementation candidate
22+
*/
23+
@Node(primaryLabel = "Sedan")
24+
public class Sedan extends Car {
25+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2011-2025 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.gh3036;
17+
18+
import java.util.Set;
19+
20+
import org.springframework.data.neo4j.core.schema.DynamicLabels;
21+
import org.springframework.data.neo4j.core.schema.GeneratedValue;
22+
import org.springframework.data.neo4j.core.schema.Id;
23+
import org.springframework.data.neo4j.core.schema.Node;
24+
import org.springframework.data.neo4j.core.schema.Property;
25+
26+
/**
27+
* Parent class
28+
*/
29+
@Node(primaryLabel = "Vehicle")
30+
public class Vehicle {
31+
@Id
32+
@GeneratedValue
33+
public String id;
34+
35+
@Property(name = "name")
36+
public String name;
37+
38+
@DynamicLabels
39+
public Set<String> labels = Set.of();
40+
41+
public String getId() {
42+
return id;
43+
}
44+
45+
public void setId(String id) {
46+
this.id = id;
47+
}
48+
49+
public Set<String> getLabels() {
50+
return labels;
51+
}
52+
53+
public void setLabels(Set<String> labels) {
54+
this.labels = labels;
55+
}
56+
57+
public String getName() {
58+
return name;
59+
}
60+
61+
public void setName(String name) {
62+
this.name = name;
63+
}
64+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2011-2025 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.gh3036;
17+
18+
import java.util.List;
19+
20+
import org.springframework.data.neo4j.repository.Neo4jRepository;
21+
import org.springframework.data.neo4j.repository.query.Query;
22+
23+
/**
24+
* Repository for testing GH-3036
25+
*/
26+
public interface VehicleRepository extends Neo4jRepository<Vehicle, String> {
27+
@Query("MATCH (vehicle:Vehicle) RETURN vehicle")
28+
List<Vehicle> findAllVehicles();
29+
}

0 commit comments

Comments
 (0)