Skip to content

Commit 4d0eff9

Browse files
committed
GH-2565 - Skip null properties on mapping/reading.
Allows to use default values in the entity classes. Closes #2565
1 parent adbc3aa commit 4d0eff9

File tree

2 files changed

+79
-2
lines changed

2 files changed

+79
-2
lines changed

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -481,8 +481,10 @@ private PropertyHandler<Neo4jPersistentProperty> populateFrom(MapAccessor queryR
481481
}
482482
} else {
483483
Object value = conversionService.readValue(extractValueOf(property, queryResult), typeInformation, property.getOptionalConverter());
484-
Class<?> rawType = typeInformation.getType();
485-
propertyAccessor.setProperty(property, getValueOrDefault(ownerIsKotlinType, rawType, value));
484+
if (value != null) {
485+
Class<?> rawType = typeInformation.getType();
486+
propertyAccessor.setProperty(property, getValueOrDefault(ownerIsKotlinType, rawType, value));
487+
}
486488
}
487489
};
488490
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package org.springframework.data.neo4j.core.mapping;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.neo4j.driver.Value;
5+
import org.neo4j.driver.Values;
6+
import org.neo4j.driver.internal.InternalNode;
7+
import org.neo4j.driver.internal.types.InternalTypeSystem;
8+
import org.neo4j.driver.internal.value.NodeValue;
9+
import org.neo4j.driver.types.TypeSystem;
10+
import org.springframework.data.mapping.callback.EntityCallbacks;
11+
import org.springframework.data.mapping.model.EntityInstantiators;
12+
import org.springframework.data.neo4j.core.convert.Neo4jConversions;
13+
import org.springframework.data.neo4j.core.mapping.callback.EventSupport;
14+
import org.springframework.data.neo4j.core.schema.GeneratedValue;
15+
import org.springframework.data.neo4j.core.schema.Id;
16+
import org.springframework.data.neo4j.core.schema.Node;
17+
import org.springframework.data.util.ClassTypeInformation;
18+
19+
import java.util.Collections;
20+
import java.util.HashMap;
21+
import java.util.Map;
22+
23+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
24+
25+
class DefaultNeo4jEntityConverterTest {
26+
27+
private final DefaultNeo4jEntityConverter entityConverter;
28+
29+
DefaultNeo4jEntityConverterTest() {
30+
EntityInstantiators entityInstantiators = new EntityInstantiators();
31+
NodeDescriptionStore nodeDescriptionStore = new NodeDescriptionStore();
32+
DefaultNeo4jConversionService conversionService = new DefaultNeo4jConversionService(new Neo4jConversions());
33+
Neo4jMappingContext context = new Neo4jMappingContext();
34+
context.addPersistentEntity(ClassTypeInformation.from(EntityWithDefaultValues.class));
35+
nodeDescriptionStore.put("User", (DefaultNeo4jPersistentEntity<?>) context.getNodeDescription(EntityWithDefaultValues.class));
36+
EventSupport eventSupport = EventSupport.useExistingCallbacks(context, EntityCallbacks.create());
37+
TypeSystem typeSystem = InternalTypeSystem.TYPE_SYSTEM;
38+
this.entityConverter = new DefaultNeo4jEntityConverter(entityInstantiators, nodeDescriptionStore, conversionService, eventSupport, typeSystem);
39+
}
40+
41+
@Test
42+
void readEntityWithDefaultValuesWithEmptyPropertiesFromDatabase() {
43+
Map<String, Value> properties = new HashMap<>();
44+
NodeValue mapAccessor = new NodeValue(
45+
new InternalNode(1L, Collections.singleton("EntityWithDefaultValues"), properties)
46+
);
47+
48+
EntityWithDefaultValues readNode = entityConverter.read(EntityWithDefaultValues.class, mapAccessor);
49+
assertThat(readNode).isNotNull();
50+
assertThat(readNode.noDefaultValue).isNull();
51+
assertThat(readNode.defaultValue).isEqualTo("Test");
52+
}
53+
54+
@Test
55+
void readEntityWithDefaultValuesWithPropertiesFromDatabase() {
56+
Map<String, Value> properties = new HashMap<>();
57+
properties.put("noDefaultValue", Values.value("valueFromDatabase1"));
58+
properties.put("defaultValue", Values.value("valueFromDatabase2"));
59+
NodeValue mapAccessor = new NodeValue(
60+
new InternalNode(1L, Collections.singleton("EntityWithDefaultValues"), properties)
61+
);
62+
63+
EntityWithDefaultValues readNode = entityConverter.read(EntityWithDefaultValues.class, mapAccessor);
64+
assertThat(readNode).isNotNull();
65+
assertThat(readNode.noDefaultValue).isEqualTo("valueFromDatabase1");
66+
assertThat(readNode.defaultValue).isEqualTo("valueFromDatabase2");
67+
}
68+
69+
@Node
70+
static class EntityWithDefaultValues {
71+
@Id @GeneratedValue Long id;
72+
public String noDefaultValue;
73+
public String defaultValue = "Test";
74+
}
75+
}

0 commit comments

Comments
 (0)