diff --git a/src/main/java/org/springframework/data/neo4j/core/Neo4jTemplate.java b/src/main/java/org/springframework/data/neo4j/core/Neo4jTemplate.java index fdf362f37..eee94aace 100644 --- a/src/main/java/org/springframework/data/neo4j/core/Neo4jTemplate.java +++ b/src/main/java/org/springframework/data/neo4j/core/Neo4jTemplate.java @@ -490,7 +490,6 @@ public final class Neo4jTemplate implements Neo4jOperations, BeanFactoryAware { rawValue); RelationshipDescription relationshipDescription = relationshipContext.getRelationship(); - RelationshipDescription relationshipDescriptionObverse = relationshipDescription.getRelationshipObverse(); Neo4jPersistentProperty idProperty; if (!relationshipDescription.hasInternalIdProperty()) { @@ -501,7 +500,7 @@ public final class Neo4jTemplate implements Neo4jOperations, BeanFactoryAware { } // break recursive procession and deletion of previously created relationships - ProcessState processState = stateMachine.getStateOf(fromId, relationshipDescriptionObverse, relatedValuesToStore); + ProcessState processState = stateMachine.getStateOf(fromId, relationshipDescription, relatedValuesToStore); if (processState == ProcessState.PROCESSED_ALL_RELATIONSHIPS || processState == ProcessState.PROCESSED_BOTH) { return; } diff --git a/src/main/java/org/springframework/data/neo4j/core/ReactiveNeo4jTemplate.java b/src/main/java/org/springframework/data/neo4j/core/ReactiveNeo4jTemplate.java index aa264f566..56ac75e5b 100644 --- a/src/main/java/org/springframework/data/neo4j/core/ReactiveNeo4jTemplate.java +++ b/src/main/java/org/springframework/data/neo4j/core/ReactiveNeo4jTemplate.java @@ -606,7 +606,6 @@ public final class ReactiveNeo4jTemplate implements ReactiveNeo4jOperations, Bea rawValue); RelationshipDescription relationshipDescription = relationshipContext.getRelationship(); - RelationshipDescription relationshipDescriptionObverse = relationshipDescription.getRelationshipObverse(); Neo4jPersistentProperty idProperty; if (!relationshipDescription.hasInternalIdProperty()) { @@ -617,7 +616,7 @@ public final class ReactiveNeo4jTemplate implements ReactiveNeo4jOperations, Bea } // break recursive procession and deletion of previously created relationships - ProcessState processState = stateMachine.getStateOf(fromId, relationshipDescriptionObverse, relatedValuesToStore); + ProcessState processState = stateMachine.getStateOf(fromId, relationshipDescription, relatedValuesToStore); if (processState == ProcessState.PROCESSED_ALL_RELATIONSHIPS || processState == ProcessState.PROCESSED_BOTH) { return; } diff --git a/src/main/java/org/springframework/data/neo4j/core/mapping/DefaultRelationshipDescription.java b/src/main/java/org/springframework/data/neo4j/core/mapping/DefaultRelationshipDescription.java index 6cede556f..919bd7840 100644 --- a/src/main/java/org/springframework/data/neo4j/core/mapping/DefaultRelationshipDescription.java +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/DefaultRelationshipDescription.java @@ -138,7 +138,6 @@ final class DefaultRelationshipDescription extends Association { + } + + @Configuration + @EnableTransactionManagement + @EnableNeo4jRepositories(considerNestedRepositories = true) + static class Config extends AbstractNeo4jConfig { + + @Bean + public Driver driver() { + + return neo4jConnectionSupport.getDriver(); + } + } +} diff --git a/src/test/java/org/springframework/data/neo4j/integration/issues/gh2289/RangeRelation.java b/src/test/java/org/springframework/data/neo4j/integration/issues/gh2289/RangeRelation.java new file mode 100644 index 000000000..b7c6acb09 --- /dev/null +++ b/src/test/java/org/springframework/data/neo4j/integration/issues/gh2289/RangeRelation.java @@ -0,0 +1,46 @@ +/* + * Copyright 2011-2021 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.gh2289; + +import lombok.Data; + +import org.springframework.data.neo4j.core.schema.GeneratedValue; +import org.springframework.data.neo4j.core.schema.Id; +import org.springframework.data.neo4j.core.schema.Property; +import org.springframework.data.neo4j.core.schema.RelationshipProperties; +import org.springframework.data.neo4j.core.schema.TargetNode; + +/** + * @author Michael J. Simons + */ +@Data // lombok +@RelationshipProperties +public class RangeRelation { + @Id @GeneratedValue private Long id; + + @Property private double minDelta; + @Property private double maxDelta; + @Property private RelationType relationType; + + @TargetNode private Sku targetSku; + + public RangeRelation(Sku targetSku, double minDelta, double maxDelta, RelationType relationType) { + this.targetSku = targetSku; + this.minDelta = minDelta; + this.maxDelta = maxDelta; + this.relationType = relationType; + } +} diff --git a/src/test/java/org/springframework/data/neo4j/integration/issues/gh2289/ReactiveGH2289IT.java b/src/test/java/org/springframework/data/neo4j/integration/issues/gh2289/ReactiveGH2289IT.java new file mode 100644 index 000000000..c17a74d31 --- /dev/null +++ b/src/test/java/org/springframework/data/neo4j/integration/issues/gh2289/ReactiveGH2289IT.java @@ -0,0 +1,110 @@ +/* + * Copyright 2011-2021 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.gh2289; + +import reactor.test.StepVerifier; + +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.api.Tag; +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.AbstractReactiveNeo4jConfig; +import org.springframework.data.neo4j.repository.ReactiveNeo4jRepository; +import org.springframework.data.neo4j.repository.config.EnableReactiveNeo4jRepositories; +import org.springframework.data.neo4j.test.Neo4jExtension; +import org.springframework.data.neo4j.test.Neo4jIntegrationTest; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * @author Michael J. Simons + */ +@Neo4jIntegrationTest +@Tag(Neo4jExtension.NEEDS_REACTIVE_SUPPORT) +class ReactiveGH2289IT { + + protected static Neo4jExtension.Neo4jConnectionSupport neo4jConnectionSupport; + + @BeforeAll + protected static void setupData() { + try (Session session = neo4jConnectionSupport.getDriver().session(); + Transaction transaction = session.beginTransaction(); + ) { + transaction.run("MATCH (n) detach delete n"); + transaction.commit(); + } + } + + @RepeatedTest(23) + void testNewRelation(@Autowired SkuRepository skuRepo) { + + AtomicLong bId = new AtomicLong(); + AtomicReference cRef = new AtomicReference<>(); + skuRepo.save(new Sku(0L, "A")) + .zipWith(skuRepo.save(new Sku(1L, "B"))) + .zipWith(skuRepo.save(new Sku(2L, "C"))) + .zipWith(skuRepo.save(new Sku(3L, "D"))).flatMap(t -> { + Sku a = t.getT1().getT1().getT1(); + Sku b = t.getT1().getT1().getT2(); + Sku c = t.getT1().getT2(); + Sku d = t.getT2(); + + bId.set(b.getId()); + cRef.set(c); + a.rangeRelationTo(b, 1, 1, RelationType.MULTIPLICATIVE); + a.rangeRelationTo(c, 1, 1, RelationType.MULTIPLICATIVE); + a.rangeRelationTo(d, 1, 1, RelationType.MULTIPLICATIVE); + return skuRepo.save(a); + }).as(StepVerifier::create) + .expectNextMatches(a -> a.getRangeRelationsOut().size() == 3) + .verifyComplete(); + + skuRepo.findById(bId.get()) + .doOnNext(b -> Assertions.assertThat(b.getRangeRelationsIn()).hasSize(1)) + .flatMap(b -> { + b.rangeRelationTo(cRef.get(), 1, 1, RelationType.MULTIPLICATIVE); + return skuRepo.save(b); + }) + .as(StepVerifier::create) + .expectNextMatches(a -> a.getRangeRelationsIn().size() == 1 && a.getRangeRelationsOut().size() == 1) + .verifyComplete(); + } + + @Repository + public interface SkuRepository extends ReactiveNeo4jRepository { + } + + @Configuration + @EnableTransactionManagement + @EnableReactiveNeo4jRepositories(considerNestedRepositories = true) + static class Config extends AbstractReactiveNeo4jConfig { + + @Bean + public Driver driver() { + + return neo4jConnectionSupport.getDriver(); + } + } +} diff --git a/src/test/java/org/springframework/data/neo4j/integration/issues/gh2289/RelationType.java b/src/test/java/org/springframework/data/neo4j/integration/issues/gh2289/RelationType.java new file mode 100644 index 000000000..b63caead6 --- /dev/null +++ b/src/test/java/org/springframework/data/neo4j/integration/issues/gh2289/RelationType.java @@ -0,0 +1,23 @@ +/* + * Copyright 2011-2021 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.gh2289; + +/** + * @author Michael J. Simons + */ +public enum RelationType { + MULTIPLICATIVE +} diff --git a/src/test/java/org/springframework/data/neo4j/integration/issues/gh2289/Sku.java b/src/test/java/org/springframework/data/neo4j/integration/issues/gh2289/Sku.java new file mode 100644 index 000000000..1375a0367 --- /dev/null +++ b/src/test/java/org/springframework/data/neo4j/integration/issues/gh2289/Sku.java @@ -0,0 +1,65 @@ +/* + * Copyright 2011-2021 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.gh2289; + +import lombok.Getter; +import lombok.Setter; + +import java.util.HashSet; +import java.util.Set; + +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.schema.Relationship; + +/** + * @author Michael J. Simons + */ +@Node("SKU") +@Getter // lombok +@Setter +public class Sku { + + @Id @GeneratedValue + private Long id; + + @Property("number") + private Long number; + + @Property("name") + private String name; + + @Relationship(type = "RANGE_RELATION_TO", direction = Relationship.Direction.OUTGOING) + private Set rangeRelationsOut = new HashSet<>(); + + @Relationship(type = "RANGE_RELATION_TO", direction = Relationship.Direction.INCOMING) + private Set rangeRelationsIn = new HashSet<>(); + + public Sku(Long number, String name) { + this.number = number; + this.name = name; + } + + public RangeRelation rangeRelationTo(Sku sku, double minDelta, double maxDelta, RelationType relationType) { + RangeRelation relationOut = new RangeRelation(sku, minDelta, maxDelta, relationType); + RangeRelation relationIn = new RangeRelation(this, minDelta, maxDelta, relationType); + rangeRelationsOut.add(relationOut); + sku.rangeRelationsIn.add(relationIn); + return relationOut; + } +}