GH-2591 - Fix StackOverflow on first level of recursive projections.

Closes #2591
This commit is contained in:
Gerrit Meier
2022-09-14 19:17:10 +02:00
parent 1af73557e5
commit b10ea7fabe
5 changed files with 32 additions and 5 deletions

View File

@@ -72,6 +72,7 @@ import org.springframework.data.neo4j.core.mapping.CypherGenerator;
import org.springframework.data.neo4j.core.mapping.DtoInstantiatingConverter;
import org.springframework.data.neo4j.core.mapping.EntityFromDtoInstantiatingConverter;
import org.springframework.data.neo4j.core.mapping.EntityInstanceWithSource;
import org.springframework.data.neo4j.core.mapping.IdDescription;
import org.springframework.data.neo4j.core.mapping.MappingSupport;
import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext;
import org.springframework.data.neo4j.core.mapping.Neo4jPersistentEntity;
@@ -978,7 +979,8 @@ public final class Neo4jTemplate implements
@SuppressWarnings("unchecked")
Map<String, Object> properties = (Map<String, Object>) tree.get(Constants.NAME_OF_PROPERTIES_PARAM);
String idPropertyName = targetPersistentEntity.getIdProperty().getPropertyName();
boolean assignedId = targetPersistentEntity.getIdDescription().isAssignedId();
IdDescription idDescription = targetPersistentEntity.getIdDescription();
boolean assignedId = idDescription.isAssignedId() || idDescription.isExternallyGeneratedId();
if (!includeProperty.isNotFiltering()) {
properties.entrySet()
.removeIf(e -> {

View File

@@ -146,8 +146,10 @@ public final class PropertyFilterSupport {
TypeInformation<?> typeInformation = currentTypeInformation.getProperty(nestedInputProperty.getName());
ProjectionPathProcessor nextProjectionPathProcessor = projectionPathProcessor.next(nestedInputProperty, typeInformation);
if (projectionPathProcessor.isChildLevel() && (domainType.equals(nextProjectionPathProcessor.typeInformation.getType())
|| returnedType.equals(nextProjectionPathProcessor.typeInformation.getType()))) {
if (projectionPathProcessor.isChildLevel() &&
(domainType.equals(nextProjectionPathProcessor.typeInformation.getType())
|| returnedType.equals(nextProjectionPathProcessor.typeInformation.getActualType().getType())
|| returnedType.equals(nextProjectionPathProcessor.typeInformation.getType()))) {
break;
}

View File

@@ -19,6 +19,7 @@ import static org.neo4j.cypherdsl.core.Cypher.anyNode;
import static org.neo4j.cypherdsl.core.Cypher.asterisk;
import static org.neo4j.cypherdsl.core.Cypher.parameter;
import org.springframework.data.neo4j.core.mapping.IdDescription;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;
@@ -1068,7 +1069,8 @@ public final class ReactiveNeo4jTemplate implements
@SuppressWarnings("unchecked")
Function<Object, Map<String, Object>> binderFunction = neo4jMappingContext.getRequiredBinderFunctionFor(entityType);
String idPropertyName = targetNodeDescription.getIdProperty().getPropertyName();
boolean assignedId = targetNodeDescription.getIdDescription().isAssignedId();
IdDescription idDescription = targetNodeDescription.getIdDescription();
boolean assignedId = idDescription.isAssignedId() || idDescription.isExternallyGeneratedId();
binderFunction = binderFunction.andThen(tree -> {
@SuppressWarnings("unchecked")
Map<String, Object> properties = (Map<String, Object>) tree.get(Constants.NAME_OF_PROPERTIES_PARAM);

View File

@@ -44,6 +44,7 @@ import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.neo4j.core.mapping.Constants;
import org.springframework.data.neo4j.core.mapping.EntityInstanceWithSource;
import org.springframework.data.neo4j.core.mapping.IdDescription;
import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext;
import org.springframework.data.neo4j.core.mapping.Neo4jPersistentEntity;
import org.springframework.data.neo4j.core.mapping.Neo4jPersistentProperty;
@@ -280,7 +281,8 @@ public final class TemplateSupport {
Map<String, Object> properties = (Map<String, Object>) tree.get(Constants.NAME_OF_PROPERTIES_PARAM);
String idPropertyName = entityMetaData.getIdProperty().getPropertyName();
boolean assignedId = entityMetaData.getIdDescription().isAssignedId();
IdDescription idDescription = entityMetaData.getIdDescription();
boolean assignedId = idDescription.isAssignedId() || idDescription.isExternallyGeneratedId();
if (!includeProperty.isNotFiltering()) {
properties.entrySet()
.removeIf(e -> {

View File

@@ -132,6 +132,12 @@ class AdvancedMappingIT {
Movie getSequel();
}
interface MovieWithMovieList {
String getTitle();
List<MovieWithMovieList> getSequel();
}
interface MovieRepository extends Neo4jRepository<Movie, String> {
MovieProjection findProjectionByTitle(String title);
@@ -496,6 +502,19 @@ class AdvancedMappingIT {
assertThat(movies.get(0).getActors()).hasSize(6);
}
@Test // GH-2591
void createPropertyFilterPathCorrectly(@Autowired Neo4jTemplate neo4jTemplate) {
Movie movie = new Movie("Test", "Movie");
neo4jTemplate.saveAs(movie, MovieWithMovieList.class);
Movie foundMovie = neo4jTemplate.findOne("MATCH (m:Movie{title:'Test'}) return m", Collections.emptyMap(), Movie.class).get();
assertThat(foundMovie.getTitle()).isEqualTo("Test");
assertThat(foundMovie.getDescription()).isNull();
// clean up to keep the other tests healthy
neo4jTemplate.deleteById("Test", Movie.class);
}
@Configuration
@EnableTransactionManagement
@EnableNeo4jRepositories(considerNestedRepositories = true)