GH-2565 - Skip null properties on mapping/reading.

Allows to use default values in the entity classes.
Closes #2565
This commit is contained in:
Gerrit Meier
2022-07-13 12:29:51 +02:00
parent 29cfd3cac6
commit 25a35b1653
2 changed files with 97 additions and 2 deletions

View File

@@ -476,8 +476,10 @@ final class DefaultNeo4jEntityConverter implements Neo4jEntityConverter {
}
} else {
Object value = conversionService.readValue(extractValueOf(property, queryResult), typeInformation, property.getOptionalConverter());
Class<?> rawType = typeInformation.getType();
propertyAccessor.setProperty(property, getValueOrDefault(ownerIsKotlinType, rawType, value));
if (value != null) {
Class<?> rawType = typeInformation.getType();
propertyAccessor.setProperty(property, getValueOrDefault(ownerIsKotlinType, rawType, value));
}
}
};
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright 2011-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.neo4j.core.mapping;
import org.junit.jupiter.api.Test;
import org.neo4j.driver.Value;
import org.neo4j.driver.Values;
import org.neo4j.driver.internal.InternalNode;
import org.neo4j.driver.internal.types.InternalTypeSystem;
import org.neo4j.driver.internal.value.NodeValue;
import org.neo4j.driver.types.TypeSystem;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.mapping.model.EntityInstantiators;
import org.springframework.data.neo4j.core.convert.Neo4jConversions;
import org.springframework.data.neo4j.core.mapping.callback.EventSupport;
import org.springframework.data.neo4j.core.schema.GeneratedValue;
import org.springframework.data.neo4j.core.schema.Id;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.data.util.ClassTypeInformation;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Gerrit Meier
*/
class DefaultNeo4jEntityConverterTest {
private final DefaultNeo4jEntityConverter entityConverter;
DefaultNeo4jEntityConverterTest() {
EntityInstantiators entityInstantiators = new EntityInstantiators();
NodeDescriptionStore nodeDescriptionStore = new NodeDescriptionStore();
DefaultNeo4jConversionService conversionService = new DefaultNeo4jConversionService(new Neo4jConversions());
Neo4jMappingContext context = new Neo4jMappingContext();
context.addPersistentEntity(ClassTypeInformation.from(EntityWithDefaultValues.class));
nodeDescriptionStore.put("User", (DefaultNeo4jPersistentEntity<?>) context.getNodeDescription(EntityWithDefaultValues.class));
EventSupport eventSupport = EventSupport.useExistingCallbacks(context, EntityCallbacks.create());
TypeSystem typeSystem = InternalTypeSystem.TYPE_SYSTEM;
this.entityConverter = new DefaultNeo4jEntityConverter(entityInstantiators, conversionService, nodeDescriptionStore, typeSystem);
}
@Test
void readEntityWithDefaultValuesWithEmptyPropertiesFromDatabase() {
Map<String, Value> properties = new HashMap<>();
NodeValue mapAccessor = new NodeValue(
new InternalNode(1L, Collections.singleton("EntityWithDefaultValues"), properties)
);
EntityWithDefaultValues readNode = entityConverter.read(EntityWithDefaultValues.class, mapAccessor);
assertThat(readNode).isNotNull();
assertThat(readNode.noDefaultValue).isNull();
assertThat(readNode.defaultValue).isEqualTo("Test");
}
@Test
void readEntityWithDefaultValuesWithPropertiesFromDatabase() {
Map<String, Value> properties = new HashMap<>();
properties.put("noDefaultValue", Values.value("valueFromDatabase1"));
properties.put("defaultValue", Values.value("valueFromDatabase2"));
NodeValue mapAccessor = new NodeValue(
new InternalNode(1L, Collections.singleton("EntityWithDefaultValues"), properties)
);
EntityWithDefaultValues readNode = entityConverter.read(EntityWithDefaultValues.class, mapAccessor);
assertThat(readNode).isNotNull();
assertThat(readNode.noDefaultValue).isEqualTo("valueFromDatabase1");
assertThat(readNode.defaultValue).isEqualTo("valueFromDatabase2");
}
@Node
static class EntityWithDefaultValues {
@Id @GeneratedValue Long id;
public String noDefaultValue;
public String defaultValue = "Test";
}
}