Skip to content

Commit 3af7555

Browse files
michael-simonsdcrissmanTheNumberOne
authored
DATAGRAPH-1351 - Apply possible conversions for ids during delete.
Co-authored-by: Dennis Crissman <[email protected]> Co-authored-by: Rosetta Roberts <[email protected]>
1 parent f1518d6 commit 3af7555

File tree

6 files changed

+241
-26
lines changed

6 files changed

+241
-26
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ public <T> void deleteById(Object id, Class<T> domainType) {
316316
log.debug(() -> String.format("Deleting entity with id %s ", id));
317317

318318
Statement statement = cypherGenerator.prepareDeleteOf(entityMetaData, condition);
319-
ResultSummary summary = this.neo4jClient.query(renderer.render(statement)).in(getDatabaseName()).bind(id)
319+
ResultSummary summary = this.neo4jClient.query(renderer.render(statement)).in(getDatabaseName()).bind(convertIdValues(id))
320320
.to(nameOfParameter).run();
321321

322322
log.debug(() -> String.format("Deleted %d nodes and %d relationships.", summary.counters().nodesDeleted(),
@@ -333,7 +333,7 @@ public <T> void deleteAllById(Iterable<?> ids, Class<T> domainType) {
333333
log.debug(() -> String.format("Deleting all entities with the following ids: %s ", ids));
334334

335335
Statement statement = cypherGenerator.prepareDeleteOf(entityMetaData, condition);
336-
ResultSummary summary = this.neo4jClient.query(renderer.render(statement)).in(getDatabaseName()).bind(ids)
336+
ResultSummary summary = this.neo4jClient.query(renderer.render(statement)).in(getDatabaseName()).bind(convertIdValues(ids))
337337
.to(nameOfParameter).run();
338338

339339
log.debug(() -> String.format("Deleted %d nodes and %d relationships.", summary.counters().nodesDeleted(),

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ public <T> Mono<Void> deleteAllById(Iterable<?> ids, Class<T> domainType) {
329329

330330
Statement statement = cypherGenerator.prepareDeleteOf(entityMetaData, condition);
331331
return getDatabaseName().flatMap(databaseName -> this.neo4jClient.query(() -> renderer.render(statement))
332-
.in(databaseName.getValue()).bind(ids).to(nameOfParameter).run().then());
332+
.in(databaseName.getValue()).bind(convertIdValues(ids)).to(nameOfParameter).run().then());
333333
}
334334

335335
@Override
@@ -343,7 +343,7 @@ public <T> Mono<Void> deleteById(Object id, Class<T> domainType) {
343343

344344
Statement statement = cypherGenerator.prepareDeleteOf(entityMetaData, condition);
345345
return getDatabaseName().flatMap(databaseName -> this.neo4jClient.query(() -> renderer.render(statement))
346-
.in(databaseName.getValue()).bind(id).to(nameOfParameter).run().then());
346+
.in(databaseName.getValue()).bind(convertIdValues(id)).to(nameOfParameter).run().then());
347347
}
348348

349349
@Override

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ public String getPropertyName() {
172172

173173
String propertyName = this.graphPropertyName.getNullable();
174174
if (propertyName == null) {
175-
throw new MappingException("This property is not mapped to a Graph property!");
175+
throw new MappingException("The property '" + propertyName + "' is not mapped to a Graph property!");
176176
}
177177

178178
return propertyName;

src/test/java/org/springframework/data/neo4j/integration/imperative/Neo4jOperationsIT.java

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,20 @@
1515
*/
1616
package org.springframework.data.neo4j.integration.imperative;
1717

18-
import static java.util.Collections.*;
19-
import static org.assertj.core.api.Assertions.*;
18+
import static java.util.Collections.singletonList;
19+
import static java.util.Collections.singletonMap;
20+
import static org.assertj.core.api.Assertions.assertThat;
2021

2122
import java.util.Arrays;
2223
import java.util.Collection;
2324
import java.util.HashMap;
2425
import java.util.List;
2526
import java.util.Map;
2627
import java.util.Optional;
28+
import java.util.concurrent.atomic.AtomicLong;
2729
import java.util.function.Function;
30+
import java.util.stream.Collectors;
31+
import java.util.stream.Stream;
2832

2933
import org.junit.jupiter.api.BeforeEach;
3034
import org.junit.jupiter.api.Test;
@@ -38,14 +42,18 @@
3842
import org.neo4j.driver.Session;
3943
import org.neo4j.driver.SessionConfig;
4044
import org.neo4j.driver.Transaction;
45+
import org.neo4j.driver.TransactionWork;
4146
import org.neo4j.driver.Value;
4247
import org.neo4j.driver.Values;
48+
import org.neo4j.driver.summary.ResultSummary;
4349
import org.springframework.beans.factory.annotation.Autowired;
4450
import org.springframework.context.annotation.Bean;
4551
import org.springframework.context.annotation.Configuration;
4652
import org.springframework.data.neo4j.config.AbstractNeo4jConfig;
4753
import org.springframework.data.neo4j.core.Neo4jOperations;
54+
import org.springframework.data.neo4j.core.convert.Neo4jConversions;
4855
import org.springframework.data.neo4j.integration.shared.PersonWithAllConstructor;
56+
import org.springframework.data.neo4j.integration.shared.PersonWithCustomId;
4957
import org.springframework.data.neo4j.integration.shared.ThingWithGeneratedId;
5058
import org.springframework.data.neo4j.test.Neo4jExtension.Neo4jConnectionSupport;
5159
import org.springframework.data.neo4j.test.Neo4jIntegrationTest;
@@ -54,6 +62,7 @@
5462
/**
5563
* @author Gerrit Meier
5664
* @author Michael J. Simons
65+
* @author Rosetta Roberts
5766
*/
5867
@Neo4jIntegrationTest
5968
class Neo4jOperationsIT {
@@ -65,6 +74,7 @@ class Neo4jOperationsIT {
6574
private final Driver driver;
6675
private final Neo4jOperations neo4jOperations;
6776

77+
private final AtomicLong customIdValueGenerator = new AtomicLong();
6878
private Long person1Id;
6979
private Long person2Id;
7080

@@ -81,23 +91,25 @@ class Neo4jOperationsIT {
8191
* @return The session config used for verification methods.
8292
*/
8393
SessionConfig getSessionConfig() {
84-
8594
return SessionConfig.defaultConfig();
8695
}
8796

8897
@BeforeEach
8998
void setupData() {
9099

91-
Transaction transaction = driver.session(getSessionConfig()).beginTransaction();
92-
transaction.run("MATCH (n) detach delete n");
100+
try (
101+
Session session = driver.session(getSessionConfig());
102+
Transaction transaction = session.beginTransaction();
103+
) {
104+
transaction.run("MATCH (n) detach delete n");
93105

94-
person1Id = transaction.run("CREATE (n:PersonWithAllConstructor) SET n.name = $name RETURN id(n)",
95-
Values.parameters("name", TEST_PERSON1_NAME)).next().get(0).asLong();
96-
person2Id = transaction.run("CREATE (n:PersonWithAllConstructor) SET n.name = $name RETURN id(n)",
97-
Values.parameters("name", TEST_PERSON2_NAME)).next().get(0).asLong();
106+
person1Id = transaction.run("CREATE (n:PersonWithAllConstructor) SET n.name = $name RETURN id(n) AS id",
107+
Values.parameters("name", TEST_PERSON1_NAME)).single().get("id").asLong();
108+
person2Id = transaction.run("CREATE (n:PersonWithAllConstructor) SET n.name = $name RETURN id(n) AS id",
109+
Values.parameters("name", TEST_PERSON2_NAME)).single().get("id").asLong();
98110

99-
transaction.commit();
100-
transaction.close();
111+
transaction.commit();
112+
}
101113
}
102114

103115
@Test
@@ -157,8 +169,9 @@ void findAllWithStatementAndParameters() {
157169
Statement statement = Cypher.match(node).where(node.property("name").isEqualTo(Cypher.parameter("name")))
158170
.returning(node).build();
159171

160-
List<PersonWithAllConstructor> people = neo4jOperations.findAll(statement, singletonMap("name", TEST_PERSON1_NAME),
161-
PersonWithAllConstructor.class);
172+
List<PersonWithAllConstructor> people = neo4jOperations
173+
.findAll(statement, singletonMap("name", TEST_PERSON1_NAME),
174+
PersonWithAllConstructor.class);
162175

163176
assertThat(people).hasSize(1);
164177
}
@@ -276,15 +289,67 @@ void deleteAllById() {
276289
}
277290
}
278291

292+
TransactionWork<ResultSummary> createPersonWithCustomId(PersonWithCustomId.PersonId assignedId) {
293+
294+
return tx -> tx.run("CREATE (n:PersonWithCustomId) SET n.id = $id ",
295+
Values.parameters("id", assignedId.getId())).consume();
296+
}
297+
298+
@Test
299+
void deleteByCustomId() {
300+
301+
PersonWithCustomId.PersonId id = new PersonWithCustomId.PersonId(customIdValueGenerator.incrementAndGet());
302+
try (Session session = driver.session(getSessionConfig())) {
303+
session.writeTransaction(createPersonWithCustomId(id));
304+
}
305+
306+
assertThat(neo4jOperations.count(PersonWithCustomId.class)).isEqualTo(1L);
307+
neo4jOperations.deleteById(id, PersonWithCustomId.class);
308+
309+
try (Session session = driver.session(getSessionConfig())) {
310+
Result result = session.run("MATCH (p:PersonWithCustomId) return count(p) as count");
311+
assertThat(result.single().get("count").asLong()).isEqualTo(0);
312+
}
313+
}
314+
315+
@Test
316+
void deleteAllByCustomId() {
317+
318+
List<PersonWithCustomId.PersonId> ids = Stream.generate(customIdValueGenerator::incrementAndGet)
319+
.map(PersonWithCustomId.PersonId::new)
320+
.limit(2)
321+
.collect(Collectors.toList());
322+
try (
323+
Session session = driver.session(getSessionConfig());
324+
) {
325+
ids.forEach(id -> session.writeTransaction(createPersonWithCustomId(id)));
326+
}
327+
328+
assertThat(neo4jOperations.count(PersonWithCustomId.class)).isEqualTo(2L);
329+
neo4jOperations.deleteAllById(ids, PersonWithCustomId.class);
330+
331+
try (Session session = driver.session(getSessionConfig())) {
332+
Result result = session.run("MATCH (p:PersonWithCustomId) return count(p) as count");
333+
assertThat(result.single().get("count").asLong()).isEqualTo(0);
334+
}
335+
}
336+
279337
@Configuration
280338
@EnableTransactionManagement
281339
static class Config extends AbstractNeo4jConfig {
282340

283341
@Bean
342+
@Override
284343
public Driver driver() {
285344
return neo4jConnectionSupport.getDriver();
286345
}
287346

347+
@Bean
348+
@Override
349+
public Neo4jConversions neo4jConversions() {
350+
return new Neo4jConversions(singletonList(new PersonWithCustomId.CustomPersonIdConverter()));
351+
}
352+
288353
@Override // needed here because there is no implicit registration of entities upfront some methods under test
289354
protected Collection<String> getMappingBasePackages() {
290355
return singletonList(PersonWithAllConstructor.class.getPackage().getName());

src/test/java/org/springframework/data/neo4j/integration/reactive/ReactiveNeo4jOperationsIT.java

Lines changed: 79 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@
2828
import java.util.HashMap;
2929
import java.util.List;
3030
import java.util.Map;
31+
import java.util.concurrent.atomic.AtomicLong;
3132
import java.util.function.Function;
33+
import java.util.stream.Collectors;
34+
import java.util.stream.Stream;
3235

3336
import org.junit.jupiter.api.BeforeEach;
3437
import org.junit.jupiter.api.Tag;
@@ -43,14 +46,18 @@
4346
import org.neo4j.driver.Session;
4447
import org.neo4j.driver.SessionConfig;
4548
import org.neo4j.driver.Transaction;
49+
import org.neo4j.driver.TransactionWork;
4650
import org.neo4j.driver.Value;
4751
import org.neo4j.driver.Values;
52+
import org.neo4j.driver.summary.ResultSummary;
4853
import org.springframework.beans.factory.annotation.Autowired;
4954
import org.springframework.context.annotation.Bean;
5055
import org.springframework.context.annotation.Configuration;
5156
import org.springframework.data.neo4j.config.AbstractReactiveNeo4jConfig;
5257
import org.springframework.data.neo4j.core.ReactiveNeo4jOperations;
58+
import org.springframework.data.neo4j.core.convert.Neo4jConversions;
5359
import org.springframework.data.neo4j.integration.shared.PersonWithAllConstructor;
60+
import org.springframework.data.neo4j.integration.shared.PersonWithCustomId;
5461
import org.springframework.data.neo4j.integration.shared.ThingWithGeneratedId;
5562
import org.springframework.data.neo4j.test.Neo4jIntegrationTest;
5663
import org.springframework.data.neo4j.test.Neo4jExtension.*;
@@ -71,6 +78,7 @@ class ReactiveNeo4jOperationsIT {
7178
private final Driver driver;
7279
private final ReactiveNeo4jOperations neo4jOperations;
7380

81+
private final AtomicLong customIdValueGenerator = new AtomicLong();
7482
private Long person1Id;
7583
private Long person2Id;
7684

@@ -94,16 +102,19 @@ SessionConfig getSessionConfig() {
94102
@BeforeEach
95103
void setupData() {
96104

97-
Transaction transaction = driver.session(getSessionConfig()).beginTransaction();
98-
transaction.run("MATCH (n) detach delete n");
105+
try (
106+
Session session = driver.session(getSessionConfig());
107+
Transaction transaction = session.beginTransaction();
108+
) {
109+
transaction.run("MATCH (n) detach delete n");
99110

100-
person1Id = transaction.run("CREATE (n:PersonWithAllConstructor) SET n.name = $name RETURN id(n)",
101-
Values.parameters("name", TEST_PERSON1_NAME)).next().get(0).asLong();
102-
person2Id = transaction.run("CREATE (n:PersonWithAllConstructor) SET n.name = $name RETURN id(n)",
103-
Values.parameters("name", TEST_PERSON2_NAME)).next().get(0).asLong();
111+
person1Id = transaction.run("CREATE (n:PersonWithAllConstructor) SET n.name = $name RETURN id(n) AS id",
112+
Values.parameters("name", TEST_PERSON1_NAME)).single().get("id").asLong();
113+
person2Id = transaction.run("CREATE (n:PersonWithAllConstructor) SET n.name = $name RETURN id(n) AS id",
114+
Values.parameters("name", TEST_PERSON2_NAME)).single().get("id").asLong();
104115

105-
transaction.commit();
106-
transaction.close();
116+
transaction.commit();
117+
}
107118
}
108119

109120
@Test
@@ -265,8 +276,36 @@ void deleteById() {
265276
}
266277
}
267278

279+
TransactionWork<ResultSummary> createPersonWithCustomId(PersonWithCustomId.PersonId assignedId) {
280+
281+
return tx -> tx.run("CREATE (n:PersonWithCustomId) SET n.id = $id ",
282+
Values.parameters("id", assignedId.getId())).consume();
283+
}
284+
285+
@Test
286+
void deleteByCustomId() {
287+
288+
PersonWithCustomId.PersonId id = new PersonWithCustomId.PersonId(customIdValueGenerator.incrementAndGet());
289+
try (Session session = driver.session(getSessionConfig())) {
290+
session.writeTransaction(createPersonWithCustomId(id));
291+
}
292+
293+
StepVerifier.create(neo4jOperations.count(PersonWithCustomId.class))
294+
.expectNext(1L)
295+
.verifyComplete();
296+
297+
StepVerifier.create(neo4jOperations.deleteById(id, PersonWithCustomId.class))
298+
.verifyComplete();
299+
300+
try (Session session = driver.session(getSessionConfig())) {
301+
Result result = session.run("MATCH (p:PersonWithCustomId) return count(p) as count");
302+
assertThat(result.single().get("count").asLong()).isEqualTo(0);
303+
}
304+
}
305+
268306
@Test
269307
void deleteAllById() {
308+
270309
StepVerifier
271310
.create(neo4jOperations.deleteAllById(Arrays.asList(person1Id, person2Id), PersonWithAllConstructor.class))
272311
.verifyComplete();
@@ -277,6 +316,32 @@ void deleteAllById() {
277316
}
278317
}
279318

319+
@Test
320+
void deleteAllByCustomId() {
321+
322+
List<PersonWithCustomId.PersonId> ids = Stream.generate(customIdValueGenerator::incrementAndGet)
323+
.map(PersonWithCustomId.PersonId::new)
324+
.limit(2)
325+
.collect(Collectors.toList());
326+
try (
327+
Session session = driver.session(getSessionConfig());
328+
) {
329+
ids.forEach(id -> session.writeTransaction(createPersonWithCustomId(id)));
330+
}
331+
332+
StepVerifier.create(neo4jOperations.count(PersonWithCustomId.class))
333+
.expectNext(2L)
334+
.verifyComplete();
335+
336+
StepVerifier.create(neo4jOperations.deleteAllById(ids, PersonWithCustomId.class))
337+
.verifyComplete();
338+
339+
try (Session session = driver.session(getSessionConfig())) {
340+
Result result = session.run("MATCH (p:PersonWithCustomId) return count(p) as count");
341+
assertThat(result.single().get("count").asLong()).isEqualTo(0);
342+
}
343+
}
344+
280345
@Configuration
281346
@EnableTransactionManagement
282347
static class Config extends AbstractReactiveNeo4jConfig {
@@ -286,6 +351,12 @@ public Driver driver() {
286351
return neo4jConnectionSupport.getDriver();
287352
}
288353

354+
@Bean
355+
@Override
356+
public Neo4jConversions neo4jConversions() {
357+
return new Neo4jConversions(singletonList(new PersonWithCustomId.CustomPersonIdConverter()));
358+
}
359+
289360
@Override // needed here because there is no implicit registration of entities upfront some methods under test
290361
protected Collection<String> getMappingBasePackages() {
291362
return singletonList(PersonWithAllConstructor.class.getPackage().getName());

0 commit comments

Comments
 (0)