diff --git a/src/main/java/org/springframework/data/neo4j/core/mapping/DefaultNeo4jEntityConverter.java b/src/main/java/org/springframework/data/neo4j/core/mapping/DefaultNeo4jEntityConverter.java index 4043f0305..c71f787b0 100644 --- a/src/main/java/org/springframework/data/neo4j/core/mapping/DefaultNeo4jEntityConverter.java +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/DefaultNeo4jEntityConverter.java @@ -587,7 +587,14 @@ final class DefaultNeo4jEntityConverter implements Neo4jEntityConverter { for (Relationship possibleRelationship : allMatchingTypeRelationshipsInResult) { if (targetIdSelector.apply(possibleRelationship) == targetNodeId) { - Object mappedObject = map(possibleValueNode, concreteTargetNodeDescription, null, relationshipsFromResult, nodesFromResult); + // If the target is the same(equal) node, get the related object from the cache. + // Avoiding the call to the map method also breaks an endless cycle of trying to finish + // the property population of _this_ object. + // The initial population will happen at the end of this mapping. This is sufficient because + // it only affects properties not changing the instance of the object. + Object mappedObject = sourceNodeId != null && sourceNodeId.equals(targetNodeId) + ? knownObjects.getObject(sourceNodeId) + : map(possibleValueNode, concreteTargetNodeDescription, null, relationshipsFromResult, nodesFromResult); if (relationshipDescription.hasRelationshipProperties()) { Object relationshipProperties = map(possibleRelationship, diff --git a/src/test/java/org/springframework/data/neo4j/integration/imperative/RepositoryIT.java b/src/test/java/org/springframework/data/neo4j/integration/imperative/RepositoryIT.java index 1bc5c04cf..76cecbb06 100644 --- a/src/test/java/org/springframework/data/neo4j/integration/imperative/RepositoryIT.java +++ b/src/test/java/org/springframework/data/neo4j/integration/imperative/RepositoryIT.java @@ -824,6 +824,24 @@ class RepositoryIT { assertThat(slice.getTotalElements()).isEqualTo(2); assertThat(slice.getTotalPages()).isEqualTo(2); } + + @Test + void findEntityPointingToEqualEntity(@Autowired PetRepository repository) { + doWithSession(session -> + session + .run("CREATE (:Pet{name: 'Pet2'})-[:Has]->(p1:Pet{name: 'Pet1'})-[:Has]->(p1) RETURN p1") + .consume()); + + List allPets = repository.findAllFriends(); + for (Pet pet : allPets) { + // everybody has a friend + assertThat(pet.getFriends()).hasSize(1); + // but only Pet1 is its own best friend + if (pet.getName().equals("Pet1")) { + assertThat(pet.getFriends().get(0)).isEqualTo(pet); + } + } + } } @Nested @@ -4149,6 +4167,11 @@ class RepositoryIT { @Query("MATCH (n:Pet) where n.name='Luna' OPTIONAL MATCH (n)-[r:Has]->(m:Pet) return n, collect(r), collect(m)") List findLunas(); + + @Query("MATCH (p:Pet)" + + " OPTIONAL MATCH (p)-[rel:Has]->(op)" + + " RETURN p, collect(rel), collect(op)") + List findAllFriends(); } interface ImmutablePetRepository extends Neo4jRepository {