Skip to content

Commit 2efd858

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

File tree

2 files changed

+97
-2
lines changed

2 files changed

+97
-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: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright 2011-2022 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.core.mapping;
17+
18+
import org.junit.jupiter.api.Test;
19+
import org.neo4j.driver.Value;
20+
import org.neo4j.driver.Values;
21+
import org.neo4j.driver.internal.InternalNode;
22+
import org.neo4j.driver.internal.types.InternalTypeSystem;
23+
import org.neo4j.driver.internal.value.NodeValue;
24+
import org.neo4j.driver.types.TypeSystem;
25+
import org.springframework.data.mapping.callback.EntityCallbacks;
26+
import org.springframework.data.mapping.model.EntityInstantiators;
27+
import org.springframework.data.neo4j.core.convert.Neo4jConversions;
28+
import org.springframework.data.neo4j.core.mapping.callback.EventSupport;
29+
import org.springframework.data.neo4j.core.schema.GeneratedValue;
30+
import org.springframework.data.neo4j.core.schema.Id;
31+
import org.springframework.data.neo4j.core.schema.Node;
32+
import org.springframework.data.util.ClassTypeInformation;
33+
34+
import java.util.Collections;
35+
import java.util.HashMap;
36+
import java.util.Map;
37+
38+
import static org.assertj.core.api.Assertions.assertThat;
39+
40+
/**
41+
* @author Gerrit Meier
42+
*/
43+
class DefaultNeo4jEntityConverterTest {
44+
45+
private final DefaultNeo4jEntityConverter entityConverter;
46+
47+
DefaultNeo4jEntityConverterTest() {
48+
EntityInstantiators entityInstantiators = new EntityInstantiators();
49+
NodeDescriptionStore nodeDescriptionStore = new NodeDescriptionStore();
50+
DefaultNeo4jConversionService conversionService = new DefaultNeo4jConversionService(new Neo4jConversions());
51+
Neo4jMappingContext context = new Neo4jMappingContext();
52+
context.addPersistentEntity(ClassTypeInformation.from(EntityWithDefaultValues.class));
53+
nodeDescriptionStore.put("User", (DefaultNeo4jPersistentEntity<?>) context.getNodeDescription(EntityWithDefaultValues.class));
54+
EventSupport eventSupport = EventSupport.useExistingCallbacks(context, EntityCallbacks.create());
55+
TypeSystem typeSystem = InternalTypeSystem.TYPE_SYSTEM;
56+
this.entityConverter = new DefaultNeo4jEntityConverter(entityInstantiators, nodeDescriptionStore, conversionService, eventSupport, typeSystem);
57+
}
58+
59+
@Test
60+
void readEntityWithDefaultValuesWithEmptyPropertiesFromDatabase() {
61+
Map<String, Value> properties = new HashMap<>();
62+
NodeValue mapAccessor = new NodeValue(
63+
new InternalNode(1L, Collections.singleton("EntityWithDefaultValues"), properties)
64+
);
65+
66+
EntityWithDefaultValues readNode = entityConverter.read(EntityWithDefaultValues.class, mapAccessor);
67+
assertThat(readNode).isNotNull();
68+
assertThat(readNode.noDefaultValue).isNull();
69+
assertThat(readNode.defaultValue).isEqualTo("Test");
70+
}
71+
72+
@Test
73+
void readEntityWithDefaultValuesWithPropertiesFromDatabase() {
74+
Map<String, Value> properties = new HashMap<>();
75+
properties.put("noDefaultValue", Values.value("valueFromDatabase1"));
76+
properties.put("defaultValue", Values.value("valueFromDatabase2"));
77+
NodeValue mapAccessor = new NodeValue(
78+
new InternalNode(1L, Collections.singleton("EntityWithDefaultValues"), properties)
79+
);
80+
81+
EntityWithDefaultValues readNode = entityConverter.read(EntityWithDefaultValues.class, mapAccessor);
82+
assertThat(readNode).isNotNull();
83+
assertThat(readNode.noDefaultValue).isEqualTo("valueFromDatabase1");
84+
assertThat(readNode.defaultValue).isEqualTo("valueFromDatabase2");
85+
}
86+
87+
@Node
88+
static class EntityWithDefaultValues {
89+
@Id @GeneratedValue Long id;
90+
public String noDefaultValue;
91+
public String defaultValue = "Test";
92+
}
93+
}

0 commit comments

Comments
 (0)