GH-2493 - Unwrap MapValueWrapper for list parameters, too.

This fixes #2493.
This commit is contained in:
Michael Simons
2022-03-31 11:16:17 +02:00
parent 48035e08f9
commit ed1d47bf37
6 changed files with 334 additions and 0 deletions

View File

@@ -15,9 +15,11 @@
*/
package org.springframework.data.neo4j.core;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@@ -68,13 +70,45 @@ final class NamedParameters {
if (Constants.NAME_OF_PROPERTIES_PARAM.equals(name) && value != null) {
this.parameters.put(name, unwrapMapValueWrapper((Map<String, Object>) value));
} else if (Constants.NAME_OF_ENTITY_LIST_PARAM.equals(name) && value != null) {
this.parameters.put(name, unwrapMapValueWrapperInListOfEntities((List<Map<String, Object>>) value));
} else {
this.parameters.put(name, value);
}
}
@SuppressWarnings("unchecked")
private List<Map<String, Object>> unwrapMapValueWrapperInListOfEntities(List<Map<String, Object>> entityList) {
boolean requiresChange = entityList.stream().anyMatch(
entity ->
entity.containsKey(Constants.NAME_OF_PROPERTIES_PARAM) &&
((Map<String, Object>) entity.get(Constants.NAME_OF_PROPERTIES_PARAM)).values().stream()
.anyMatch(MapValueWrapper.class::isInstance)
);
if (!requiresChange) {
return entityList;
}
List<Map<String, Object>> newEntityList = new ArrayList<>(entityList.size());
for (Map<String, Object> entity : entityList) {
if (entity.containsKey(Constants.NAME_OF_PROPERTIES_PARAM)) {
Map<String, Object> newEntity = new HashMap<>(entity);
newEntity.put(Constants.NAME_OF_PROPERTIES_PARAM, unwrapMapValueWrapper((Map<String, Object>) entity.get(Constants.NAME_OF_PROPERTIES_PARAM)));
newEntityList.add(newEntity);
} else {
newEntityList.add(entity);
}
}
return newEntityList;
}
private static Map<String, Object> unwrapMapValueWrapper(Map<String, Object> properties) {
if (properties.values().stream().noneMatch(MapValueWrapper.class::isInstance)) {
return properties;
}
Map<String, Object> newProperties = new HashMap<>(properties.size());
properties.forEach((k, v) -> {
if (v instanceof MapValueWrapper) {

View File

@@ -0,0 +1,125 @@
/*
* 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.integration.issues.gh2493;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.neo4j.driver.Driver;
import org.neo4j.driver.Session;
import org.neo4j.driver.Transaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.config.AbstractNeo4jConfig;
import org.springframework.data.neo4j.core.DatabaseSelectionProvider;
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
import org.springframework.data.neo4j.test.BookmarkCapture;
import org.springframework.data.neo4j.test.Neo4jExtension;
import org.springframework.data.neo4j.test.Neo4jIntegrationTest;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* @author Michael J. Simons
*/
@Neo4jIntegrationTest
public class Gh2493IT {
protected static Neo4jExtension.Neo4jConnectionSupport neo4jConnectionSupport;
@BeforeAll
protected static void setupData(@Autowired BookmarkCapture bookmarkCapture) {
try (Session session = neo4jConnectionSupport.getDriver().session(bookmarkCapture.createSessionConfig());
Transaction transaction = session.beginTransaction();
) {
transaction.run("MATCH (n) DETACH DELETE n").consume();
transaction.commit();
bookmarkCapture.seedWith(session.lastBookmark());
}
}
@Test
void saveOneShouldWork(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture,
@Autowired TestObjectRepository repository) {
TestObject testObject = new TestObject(new TestData(4711, "Foobar"));
testObject = repository.save(testObject);
assertThat(testObject.getId()).isNotNull();
assertThatTestObjectHasBeenCreated(driver, bookmarkCapture, testObject);
}
@Test
void saveAllShouldWork(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture,
@Autowired TestObjectRepository repository) {
TestObject testObject = new TestObject(new TestData(4711, "Foobar"));
testObject = repository.saveAll(Collections.singletonList(testObject)).get(0);
assertThat(testObject.getId()).isNotNull();
assertThatTestObjectHasBeenCreated(driver, bookmarkCapture, testObject);
}
private static void assertThatTestObjectHasBeenCreated(Driver driver, BookmarkCapture bookmarkCapture,
TestObject testObject) {
try (Session session = driver.session(bookmarkCapture.createSessionConfig())) {
Map<String, Object> arguments = new HashMap<>();
arguments.put("id", testObject.getId());
arguments.put("num", testObject.getData().getNum());
arguments.put("string", testObject.getData().getString());
long cnt = session.run(
"MATCH (n:TestObject) WHERE n.id = $id AND n.dataNum = $num AND n.dataString = $string RETURN count(n)",
arguments)
.single().get(0).asLong();
assertThat(cnt).isOne();
}
}
@Configuration
@EnableTransactionManagement
@EnableNeo4jRepositories
static class Config extends AbstractNeo4jConfig {
@Bean
public Driver driver() {
return neo4jConnectionSupport.getDriver();
}
@Bean
public BookmarkCapture bookmarkCapture() {
return new BookmarkCapture();
}
@Override
public PlatformTransactionManager transactionManager(Driver driver,
DatabaseSelectionProvider databaseNameProvider) {
BookmarkCapture bookmarkCapture = bookmarkCapture();
return new Neo4jTransactionManager(driver, databaseNameProvider,
Neo4jBookmarkManager.create(bookmarkCapture));
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.integration.issues.gh2493;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.neo4j.driver.Value;
import org.neo4j.driver.Values;
import org.springframework.data.neo4j.core.convert.Neo4jConversionService;
import org.springframework.data.neo4j.core.convert.Neo4jPersistentPropertyToMapConverter;
/**
* @author Michael J. Simons
*/
public class TestConverter implements Neo4jPersistentPropertyToMapConverter<String, TestData> {
static final String NUM = "Num";
static final String STRING = "String";
@Override
public Map<String, Value> decompose(TestData property,
Neo4jConversionService neo4jConversionService) {
if (property == null) {
return Collections.emptyMap();
}
Map<String, Value> result = new HashMap<>();
result.put(NUM, Values.value(property.getNum()));
result.put(STRING, Values.value(property.getString()));
return result;
}
@Override
public TestData compose(Map<String, Value> source,
Neo4jConversionService neo4jConversionService) {
TestData data = new TestData();
if (source.get(NUM) != null) {
data.setNum(source.get(NUM).asInt());
}
if (source.get(STRING) != null) {
data.setString(source.get(STRING).asString());
}
return data;
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.integration.issues.gh2493;
import lombok.Getter;
import lombok.Setter;
/**
* @author Michael J. Simons
*/
@Getter
@Setter
public class TestData {
private int num;
private String string;
public TestData() {
super();
}
public TestData(int num, String string) {
this.num = num;
this.string = string;
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.integration.issues.gh2493;
import lombok.Getter;
import lombok.Setter;
import org.springframework.data.neo4j.core.schema.CompositeProperty;
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.neo4j.core.schema.Property;
import org.springframework.data.neo4j.core.support.UUIDStringGenerator;
import com.fasterxml.jackson.annotation.JsonIgnore;
/**
* @author Michael J. Simons
*/
@Node
@Getter
@Setter
public class TestObject {
@Id
@Property(name = "id")
@GeneratedValue(value = UUIDStringGenerator.class)
protected String id;
@JsonIgnore
@CompositeProperty(delimiter = "", converter = TestConverter.class)
private TestData data;
public TestObject(TestData aData) {
super();
data = aData;
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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.integration.issues.gh2493;
import org.springframework.data.neo4j.repository.Neo4jRepository;
/**
* @author Michael J. Simons
*/
public interface TestObjectRepository extends Neo4jRepository<TestObject, String> {
}