Closed
Description
Given I have an entity that has a single relationship to another entity. If I want to have a projection of that entity with a nullable related entity, the request fails.
interface TestPersonRepository : Neo4jRepository<PersonEntity, String> {
@Query("""
MATCH (person:Person)
OPTIONAL MATCH (person)-[:WORKS_IN]->(department:Department)
return person, department
""")
fun findPeopleWithDepartments() : List<PersonProjection>
}
data class PersonProjection(
val person: PersonEntity,
val department: DepartmentEntity?
)
@Service
class TestService(
private val repository: TestPersonRepository
)
{
init {
val results = repository.findPeopleWithDepartments() // Throws exception
println(results)
}
}
I actually saw a related issue that is marked as resolved, but I think the null use-case was forgotten in 6.1.2: #2269
The stacktrace I get is
Caused by: org.neo4j.driver.exceptions.value.NotMultiValued: NULL is not iterable
at org.neo4j.driver.internal.value.ValueAdapter.values(ValueAdapter.java:401)
at org.neo4j.driver.internal.value.ValueAdapter.values(ValueAdapter.java:395)
at org.springframework.data.neo4j.core.mapping.DefaultNeo4jEntityConverter.determineQueryRoot(DefaultNeo4jEntityConverter.java:127)
at org.springframework.data.neo4j.core.mapping.DefaultNeo4jEntityConverter.read(DefaultNeo4jEntityConverter.java:102)
at org.springframework.data.neo4j.core.mapping.DefaultNeo4jEntityConverter.read(DefaultNeo4jEntityConverter.java:67)
at org.springframework.data.neo4j.core.mapping.DtoInstantiatingConverter.lambda$getPropertyValueFor$4(DtoInstantiatingConverter.java:160)
at org.springframework.data.neo4j.core.mapping.DtoInstantiatingConverter.getPropertyValueFor(DtoInstantiatingConverter.java:173)
at org.springframework.data.neo4j.core.mapping.DtoInstantiatingConverter$1.getParameterValue(DtoInstantiatingConverter.java:102)
at org.springframework.data.mapping.model.ClassGeneratingEntityInstantiator.extractInvocationArguments(ClassGeneratingEntityInstantiator.java:269)
The source of this is in DefaultNeo4jEntityConverter
.
Iterable<Value> recordValues = mapAccessor instanceof Value && ((Value) mapAccessor).hasType(nodeType) ?
Collections.singletonList((Value) mapAccessor) : mapAccessor.values(); // <-- this last part throws when the single entity result is null, since the mapAccessor is a `NullValue`.