Skip to content

Commit 879355c

Browse files
GH-2493 - Unwrap MapValueWrapper for list parameters, too.
This fixes #2493.
1 parent ae26328 commit 879355c

File tree

6 files changed

+334
-0
lines changed

6 files changed

+334
-0
lines changed

src/main/java/org/springframework/data/neo4j/core/NamedParameters.java

+34
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
*/
1616
package org.springframework.data.neo4j.core;
1717

18+
import java.util.ArrayList;
1819
import java.util.Collection;
1920
import java.util.Collections;
2021
import java.util.HashMap;
22+
import java.util.List;
2123
import java.util.Map;
2224
import java.util.stream.Collectors;
2325

@@ -68,13 +70,45 @@ void add(String name, @Nullable Object value) {
6870

6971
if (Constants.NAME_OF_PROPERTIES_PARAM.equals(name) && value != null) {
7072
this.parameters.put(name, unwrapMapValueWrapper((Map<String, Object>) value));
73+
} else if (Constants.NAME_OF_ENTITY_LIST_PARAM.equals(name) && value != null) {
74+
this.parameters.put(name, unwrapMapValueWrapperInListOfEntities((List<Map<String, Object>>) value));
7175
} else {
7276
this.parameters.put(name, value);
7377
}
7478
}
7579

80+
@SuppressWarnings("unchecked")
81+
private List<Map<String, Object>> unwrapMapValueWrapperInListOfEntities(List<Map<String, Object>> entityList) {
82+
boolean requiresChange = entityList.stream().anyMatch(
83+
entity ->
84+
entity.containsKey(Constants.NAME_OF_PROPERTIES_PARAM) &&
85+
((Map<String, Object>) entity.get(Constants.NAME_OF_PROPERTIES_PARAM)).values().stream()
86+
.anyMatch(MapValueWrapper.class::isInstance)
87+
);
88+
89+
if (!requiresChange) {
90+
return entityList;
91+
}
92+
93+
List<Map<String, Object>> newEntityList = new ArrayList<>(entityList.size());
94+
for (Map<String, Object> entity : entityList) {
95+
if (entity.containsKey(Constants.NAME_OF_PROPERTIES_PARAM)) {
96+
Map<String, Object> newEntity = new HashMap<>(entity);
97+
newEntity.put(Constants.NAME_OF_PROPERTIES_PARAM, unwrapMapValueWrapper((Map<String, Object>) entity.get(Constants.NAME_OF_PROPERTIES_PARAM)));
98+
newEntityList.add(newEntity);
99+
} else {
100+
newEntityList.add(entity);
101+
}
102+
}
103+
return newEntityList;
104+
}
105+
76106
private static Map<String, Object> unwrapMapValueWrapper(Map<String, Object> properties) {
77107

108+
if (properties.values().stream().noneMatch(MapValueWrapper.class::isInstance)) {
109+
return properties;
110+
}
111+
78112
Map<String, Object> newProperties = new HashMap<>(properties.size());
79113
properties.forEach((k, v) -> {
80114
if (v instanceof MapValueWrapper) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
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.integration.issues.gh2493;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
20+
import java.util.Collections;
21+
import java.util.HashMap;
22+
import java.util.Map;
23+
24+
import org.junit.jupiter.api.BeforeAll;
25+
import org.junit.jupiter.api.Test;
26+
import org.neo4j.driver.Driver;
27+
import org.neo4j.driver.Session;
28+
import org.neo4j.driver.Transaction;
29+
import org.springframework.beans.factory.annotation.Autowired;
30+
import org.springframework.context.annotation.Bean;
31+
import org.springframework.context.annotation.Configuration;
32+
import org.springframework.data.neo4j.config.AbstractNeo4jConfig;
33+
import org.springframework.data.neo4j.core.DatabaseSelectionProvider;
34+
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
35+
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
36+
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
37+
import org.springframework.data.neo4j.test.BookmarkCapture;
38+
import org.springframework.data.neo4j.test.Neo4jExtension;
39+
import org.springframework.data.neo4j.test.Neo4jIntegrationTest;
40+
import org.springframework.transaction.PlatformTransactionManager;
41+
import org.springframework.transaction.annotation.EnableTransactionManagement;
42+
43+
/**
44+
* @author Michael J. Simons
45+
*/
46+
@Neo4jIntegrationTest
47+
public class Gh2493IT {
48+
49+
protected static Neo4jExtension.Neo4jConnectionSupport neo4jConnectionSupport;
50+
51+
@BeforeAll
52+
protected static void setupData(@Autowired BookmarkCapture bookmarkCapture) {
53+
54+
try (Session session = neo4jConnectionSupport.getDriver().session(bookmarkCapture.createSessionConfig());
55+
Transaction transaction = session.beginTransaction();
56+
) {
57+
transaction.run("MATCH (n) DETACH DELETE n").consume();
58+
transaction.commit();
59+
bookmarkCapture.seedWith(session.lastBookmark());
60+
}
61+
}
62+
63+
@Test
64+
void saveOneShouldWork(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture,
65+
@Autowired TestObjectRepository repository) {
66+
67+
TestObject testObject = new TestObject(new TestData(4711, "Foobar"));
68+
testObject = repository.save(testObject);
69+
70+
assertThat(testObject.getId()).isNotNull();
71+
assertThatTestObjectHasBeenCreated(driver, bookmarkCapture, testObject);
72+
}
73+
74+
@Test
75+
void saveAllShouldWork(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture,
76+
@Autowired TestObjectRepository repository) {
77+
78+
TestObject testObject = new TestObject(new TestData(4711, "Foobar"));
79+
testObject = repository.saveAll(Collections.singletonList(testObject)).get(0);
80+
81+
assertThat(testObject.getId()).isNotNull();
82+
assertThatTestObjectHasBeenCreated(driver, bookmarkCapture, testObject);
83+
}
84+
85+
private static void assertThatTestObjectHasBeenCreated(Driver driver, BookmarkCapture bookmarkCapture,
86+
TestObject testObject) {
87+
try (Session session = driver.session(bookmarkCapture.createSessionConfig())) {
88+
Map<String, Object> arguments = new HashMap<>();
89+
arguments.put("id", testObject.getId());
90+
arguments.put("num", testObject.getData().getNum());
91+
arguments.put("string", testObject.getData().getString());
92+
long cnt = session.run(
93+
"MATCH (n:TestObject) WHERE n.id = $id AND n.dataNum = $num AND n.dataString = $string RETURN count(n)",
94+
arguments)
95+
.single().get(0).asLong();
96+
assertThat(cnt).isOne();
97+
}
98+
}
99+
100+
@Configuration
101+
@EnableTransactionManagement
102+
@EnableNeo4jRepositories
103+
static class Config extends AbstractNeo4jConfig {
104+
105+
@Bean
106+
public Driver driver() {
107+
108+
return neo4jConnectionSupport.getDriver();
109+
}
110+
111+
@Bean
112+
public BookmarkCapture bookmarkCapture() {
113+
return new BookmarkCapture();
114+
}
115+
116+
@Override
117+
public PlatformTransactionManager transactionManager(Driver driver,
118+
DatabaseSelectionProvider databaseNameProvider) {
119+
120+
BookmarkCapture bookmarkCapture = bookmarkCapture();
121+
return new Neo4jTransactionManager(driver, databaseNameProvider,
122+
Neo4jBookmarkManager.create(bookmarkCapture));
123+
}
124+
}
125+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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.integration.issues.gh2493;
17+
18+
import java.util.Collections;
19+
import java.util.HashMap;
20+
import java.util.Map;
21+
22+
import org.neo4j.driver.Value;
23+
import org.neo4j.driver.Values;
24+
import org.springframework.data.neo4j.core.convert.Neo4jConversionService;
25+
import org.springframework.data.neo4j.core.convert.Neo4jPersistentPropertyToMapConverter;
26+
27+
/**
28+
* @author Michael J. Simons
29+
*/
30+
public class TestConverter implements Neo4jPersistentPropertyToMapConverter<String, TestData> {
31+
32+
static final String NUM = "Num";
33+
static final String STRING = "String";
34+
35+
@Override
36+
public Map<String, Value> decompose(TestData property,
37+
Neo4jConversionService neo4jConversionService) {
38+
39+
if (property == null) {
40+
return Collections.emptyMap();
41+
}
42+
43+
Map<String, Value> result = new HashMap<>();
44+
result.put(NUM, Values.value(property.getNum()));
45+
result.put(STRING, Values.value(property.getString()));
46+
return result;
47+
}
48+
49+
@Override
50+
public TestData compose(Map<String, Value> source,
51+
Neo4jConversionService neo4jConversionService) {
52+
TestData data = new TestData();
53+
if (source.get(NUM) != null) {
54+
data.setNum(source.get(NUM).asInt());
55+
}
56+
if (source.get(STRING) != null) {
57+
data.setString(source.get(STRING).asString());
58+
}
59+
return data;
60+
}
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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.integration.issues.gh2493;
17+
18+
import lombok.Getter;
19+
import lombok.Setter;
20+
21+
/**
22+
* @author Michael J. Simons
23+
*/
24+
@Getter
25+
@Setter
26+
public class TestData {
27+
28+
private int num;
29+
private String string;
30+
31+
public TestData() {
32+
super();
33+
}
34+
35+
public TestData(int num, String string) {
36+
this.num = num;
37+
this.string = string;
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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.integration.issues.gh2493;
17+
18+
import lombok.Getter;
19+
import lombok.Setter;
20+
21+
import org.springframework.data.neo4j.core.schema.CompositeProperty;
22+
import org.springframework.data.neo4j.core.schema.GeneratedValue;
23+
import org.springframework.data.neo4j.core.schema.Id;
24+
import org.springframework.data.neo4j.core.schema.Node;
25+
import org.springframework.data.neo4j.core.schema.Property;
26+
import org.springframework.data.neo4j.core.support.UUIDStringGenerator;
27+
28+
import com.fasterxml.jackson.annotation.JsonIgnore;
29+
30+
/**
31+
* @author Michael J. Simons
32+
*/
33+
@Node
34+
@Getter
35+
@Setter
36+
public class TestObject {
37+
38+
@Id
39+
@Property(name = "id")
40+
@GeneratedValue(value = UUIDStringGenerator.class)
41+
protected String id;
42+
43+
@JsonIgnore
44+
@CompositeProperty(delimiter = "", converter = TestConverter.class)
45+
private TestData data;
46+
47+
public TestObject(TestData aData) {
48+
super();
49+
data = aData;
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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.integration.issues.gh2493;
17+
18+
import org.springframework.data.neo4j.repository.Neo4jRepository;
19+
20+
/**
21+
* @author Michael J. Simons
22+
*/
23+
public interface TestObjectRepository extends Neo4jRepository<TestObject, String> {
24+
}

0 commit comments

Comments
 (0)