Allow different relationship types to the same entity instance.
This allows for multiple relationships of different types between the same two objects. It is implemented via a simple state machine that checks both the type of the relationship and the state of the related objects already processed before terminating the traversal. In addition the change makes sure that only the object returned by Spring Data Commons event support is passed on in the recursive traversal. Last but not least, we switch to Neo4j 4.0 in tests by default to make testing of reactive components active by default.
This commit is contained in:
@@ -36,11 +36,12 @@ import org.apiguardian.api.API;
|
||||
import org.neo4j.driver.exceptions.NoSuchRecordException;
|
||||
import org.neo4j.driver.summary.ResultSummary;
|
||||
import org.neo4j.driver.summary.SummaryCounters;
|
||||
import org.neo4j.springframework.data.core.Neo4jClient.RunnableSpecTightToDatabase;
|
||||
import org.neo4j.opencypherdsl.Condition;
|
||||
import org.neo4j.opencypherdsl.Functions;
|
||||
import org.neo4j.opencypherdsl.Statement;
|
||||
import org.neo4j.opencypherdsl.renderer.Renderer;
|
||||
import org.neo4j.springframework.data.core.Neo4jClient.RunnableSpecTightToDatabase;
|
||||
import org.neo4j.springframework.data.core.NestedRelationshipProcessingStateMachine.ProcessState;
|
||||
import org.neo4j.springframework.data.core.mapping.Neo4jMappingContext;
|
||||
import org.neo4j.springframework.data.core.mapping.Neo4jPersistentEntity;
|
||||
import org.neo4j.springframework.data.core.mapping.Neo4jPersistentProperty;
|
||||
@@ -399,21 +400,23 @@ public final class Neo4jTemplate implements Neo4jOperations, BeanFactoryAware {
|
||||
return toExecutableQuery(preparedQuery);
|
||||
}
|
||||
|
||||
private void processRelations(Neo4jPersistentEntity<?> neo4jPersistentEntity, Object parentObject, @Nullable String inDatabase) {
|
||||
private void processRelations(Neo4jPersistentEntity<?> neo4jPersistentEntity, Object parentObject,
|
||||
@Nullable String inDatabase) {
|
||||
|
||||
processNestedRelations(neo4jPersistentEntity, parentObject, inDatabase, new NestedRelationshipProcessState());
|
||||
processNestedRelations(neo4jPersistentEntity, parentObject, inDatabase, new NestedRelationshipProcessingStateMachine());
|
||||
}
|
||||
|
||||
private void processNestedRelations(Neo4jPersistentEntity<?> neo4jPersistentEntity, Object parentObject,
|
||||
@Nullable String inDatabase, NestedRelationshipProcessState processState) {
|
||||
@Nullable String inDatabase, NestedRelationshipProcessingStateMachine stateMachine) {
|
||||
|
||||
PersistentPropertyAccessor<?> propertyAccessor = neo4jPersistentEntity.getPropertyAccessor(parentObject);
|
||||
Object fromId = propertyAccessor.getProperty(neo4jPersistentEntity.getRequiredIdProperty());
|
||||
|
||||
neo4jPersistentEntity.doWithAssociations((AssociationHandler<Neo4jPersistentProperty>) handler -> {
|
||||
neo4jPersistentEntity.doWithAssociations((AssociationHandler<Neo4jPersistentProperty>) association -> {
|
||||
|
||||
// create context to bundle parameters
|
||||
NestedRelationshipContext relationshipContext = NestedRelationshipContext
|
||||
.of(handler, propertyAccessor, neo4jPersistentEntity);
|
||||
.of(association, propertyAccessor, neo4jPersistentEntity);
|
||||
|
||||
Collection<?> relatedValuesToStore = Relationships
|
||||
.unifyRelationshipValue(relationshipContext.getInverse(), relationshipContext.getValue());
|
||||
@@ -422,19 +425,20 @@ public final class Neo4jTemplate implements Neo4jOperations, BeanFactoryAware {
|
||||
RelationshipDescription relationshipDescriptionObverse = relationshipDescription.getRelationshipObverse();
|
||||
|
||||
// break recursive procession and deletion of previously created relationships
|
||||
if (processState.hasProcessedEither(relationshipDescriptionObverse, relatedValuesToStore)) {
|
||||
ProcessState processState = stateMachine
|
||||
.getStateOf(relationshipDescriptionObverse, relatedValuesToStore);
|
||||
if (processState == ProcessState.PROCESSED_BOTH) {
|
||||
return;
|
||||
}
|
||||
|
||||
Neo4jPersistentEntity<?> relationshipsToRemoveDescription = neo4jMappingContext
|
||||
.getPersistentEntity(relationshipContext.getAssociationTargetType());
|
||||
|
||||
Object fromId = propertyAccessor.getProperty(neo4jPersistentEntity.getRequiredIdProperty());
|
||||
// remove all relationships before creating all new if the entity is not new
|
||||
// this avoids the usage of cache but might have significant impact on overall performance
|
||||
if (!neo4jPersistentEntity.isNew(parentObject)) {
|
||||
Neo4jPersistentEntity<?> previouslyRelatedPersistentEntity = neo4jMappingContext
|
||||
.getPersistentEntity(relationshipContext.getAssociationTargetType());
|
||||
|
||||
Statement relationshipRemoveQuery = cypherGenerator.createRelationshipRemoveQuery(neo4jPersistentEntity,
|
||||
relationshipDescription, relationshipsToRemoveDescription);
|
||||
relationshipDescription, previouslyRelatedPersistentEntity);
|
||||
|
||||
neo4jClient.query(renderer.render(relationshipRemoveQuery))
|
||||
.in(inDatabase)
|
||||
@@ -446,22 +450,25 @@ public final class Neo4jTemplate implements Neo4jOperations, BeanFactoryAware {
|
||||
return;
|
||||
}
|
||||
|
||||
processState.markAsProcessed(relationshipDescription, relatedValuesToStore);
|
||||
stateMachine.markAsProcessed(relationshipDescription, relatedValuesToStore);
|
||||
|
||||
for (Object relatedValueToStore : relatedValuesToStore) {
|
||||
|
||||
// here map entry is not always anymore a dynamic association
|
||||
Object valueToBeSaved = relationshipContext.identifyAndExtractRelationshipValue(relatedValueToStore);
|
||||
Object valueToBeSavedPreEvt = relationshipContext
|
||||
.identifyAndExtractRelationshipValue(relatedValueToStore);
|
||||
valueToBeSavedPreEvt = eventSupport.maybeCallBeforeBind(valueToBeSavedPreEvt);
|
||||
|
||||
Neo4jPersistentEntity<?> targetNodeDescription = neo4jMappingContext.getPersistentEntity(valueToBeSaved.getClass());
|
||||
Neo4jPersistentEntity<?> targetNodeDescription = neo4jMappingContext
|
||||
.getPersistentEntity(valueToBeSavedPreEvt.getClass());
|
||||
|
||||
valueToBeSaved = eventSupport.maybeCallBeforeBind(valueToBeSaved);
|
||||
|
||||
Long relatedInternalId = saveRelatedNode(valueToBeSaved, relationshipContext.getAssociationTargetType(),
|
||||
Long relatedInternalId = saveRelatedNode(valueToBeSavedPreEvt,
|
||||
relationshipContext.getAssociationTargetType(),
|
||||
targetNodeDescription, inDatabase);
|
||||
|
||||
RelationshipStatementHolder statementHolder = RelationshipStatementHolder.createStatement(
|
||||
neo4jMappingContext, neo4jPersistentEntity, relationshipContext, relatedInternalId, relatedValueToStore);
|
||||
neo4jMappingContext, neo4jPersistentEntity, relationshipContext, relatedInternalId,
|
||||
relatedValueToStore);
|
||||
|
||||
neo4jClient.query(renderer.render(statementHolder.getRelationshipCreationQuery()))
|
||||
.in(inDatabase)
|
||||
@@ -472,11 +479,13 @@ public final class Neo4jTemplate implements Neo4jOperations, BeanFactoryAware {
|
||||
// if an internal id is used this must get set to link this entity in the next iteration
|
||||
if (targetNodeDescription.isUsingInternalIds()) {
|
||||
PersistentPropertyAccessor<?> targetPropertyAccessor = targetNodeDescription
|
||||
.getPropertyAccessor(valueToBeSaved);
|
||||
.getPropertyAccessor(valueToBeSavedPreEvt);
|
||||
targetPropertyAccessor
|
||||
.setProperty(targetNodeDescription.getRequiredIdProperty(), relatedInternalId);
|
||||
}
|
||||
processNestedRelations(targetNodeDescription, valueToBeSaved, inDatabase, processState);
|
||||
if (processState != ProcessState.PROCESSED_ALL_VALUES) {
|
||||
processNestedRelations(targetNodeDescription, valueToBeSavedPreEvt, inDatabase, stateMachine);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -34,7 +34,14 @@ import org.springframework.lang.Nullable;
|
||||
* @author Michael J. Simons
|
||||
* @soundtrack Helge Schneider - Heart Attack No. 1
|
||||
*/
|
||||
final class NestedRelationshipProcessState {
|
||||
final class NestedRelationshipProcessingStateMachine {
|
||||
|
||||
enum ProcessState {
|
||||
PROCESSED_NONE,
|
||||
PROCESSED_BOTH,
|
||||
PROCESSED_ONLY_RELATIONSHIP,
|
||||
PROCESSED_ALL_VALUES
|
||||
}
|
||||
|
||||
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
private final Lock read = lock.readLock();
|
||||
@@ -53,13 +60,24 @@ final class NestedRelationshipProcessState {
|
||||
/**
|
||||
* @param relationshipDescription Check whether this relationship description has been processed
|
||||
* @param valuesToStore Check whether all the values in the collection have been processed
|
||||
* @return True, if either the relationship has been already process or all of the values to store.
|
||||
* @return The state of things processed
|
||||
*/
|
||||
boolean hasProcessedEither(RelationshipDescription relationshipDescription, @Nullable Collection<?> valuesToStore) {
|
||||
ProcessState getStateOf(RelationshipDescription relationshipDescription, @Nullable Collection<?> valuesToStore) {
|
||||
|
||||
try {
|
||||
read.lock();
|
||||
return hasProcessed(relationshipDescription) || hasProcessedAllOf(valuesToStore);
|
||||
boolean hasProcessedRelationship = hasProcessed(relationshipDescription);
|
||||
boolean hasProcessedAllValues = hasProcessedAllOf(valuesToStore);
|
||||
if (hasProcessedRelationship && hasProcessedAllValues) {
|
||||
return ProcessState.PROCESSED_BOTH;
|
||||
}
|
||||
if (hasProcessedRelationship) {
|
||||
return ProcessState.PROCESSED_ONLY_RELATIONSHIP;
|
||||
}
|
||||
if (hasProcessedAllValues) {
|
||||
return ProcessState.PROCESSED_ALL_VALUES;
|
||||
}
|
||||
return ProcessState.PROCESSED_NONE;
|
||||
} finally {
|
||||
read.unlock();
|
||||
}
|
||||
@@ -20,8 +20,8 @@ package org.neo4j.springframework.data.core;
|
||||
|
||||
import static java.util.Collections.*;
|
||||
import static java.util.stream.Collectors.*;
|
||||
import static org.neo4j.springframework.data.core.DatabaseSelection.*;
|
||||
import static org.neo4j.opencypherdsl.Cypher.*;
|
||||
import static org.neo4j.springframework.data.core.DatabaseSelection.*;
|
||||
import static org.neo4j.springframework.data.core.schema.Constants.*;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
@@ -45,6 +45,7 @@ import org.neo4j.opencypherdsl.Condition;
|
||||
import org.neo4j.opencypherdsl.Functions;
|
||||
import org.neo4j.opencypherdsl.Statement;
|
||||
import org.neo4j.opencypherdsl.renderer.Renderer;
|
||||
import org.neo4j.springframework.data.core.NestedRelationshipProcessingStateMachine.ProcessState;
|
||||
import org.neo4j.springframework.data.core.mapping.Neo4jMappingContext;
|
||||
import org.neo4j.springframework.data.core.mapping.Neo4jPersistentEntity;
|
||||
import org.neo4j.springframework.data.core.mapping.Neo4jPersistentProperty;
|
||||
@@ -407,41 +408,46 @@ public final class ReactiveNeo4jTemplate implements ReactiveNeo4jOperations, Bea
|
||||
|
||||
private Mono<Void> processRelations(Neo4jPersistentEntity<?> neo4jPersistentEntity, Object parentObject, @Nullable String inDatabase) {
|
||||
|
||||
return processNestedRelations(neo4jPersistentEntity, parentObject, inDatabase, new NestedRelationshipProcessState());
|
||||
return processNestedRelations(neo4jPersistentEntity, parentObject, inDatabase, new NestedRelationshipProcessingStateMachine());
|
||||
}
|
||||
|
||||
private Mono<Void> processNestedRelations(Neo4jPersistentEntity<?> neo4jPersistentEntity, Object parentObject,
|
||||
@Nullable String inDatabase, NestedRelationshipProcessState processState) {
|
||||
@Nullable String inDatabase, NestedRelationshipProcessingStateMachine stateMachine) {
|
||||
|
||||
return Mono.defer(() -> {
|
||||
PersistentPropertyAccessor<?> propertyAccessor = neo4jPersistentEntity.getPropertyAccessor(parentObject);
|
||||
Object fromId = propertyAccessor.getProperty(neo4jPersistentEntity.getRequiredIdProperty());
|
||||
List<Mono<Void>> relationshipCreationMonos = new ArrayList<>();
|
||||
|
||||
neo4jPersistentEntity.doWithAssociations((AssociationHandler<Neo4jPersistentProperty>) handler -> {
|
||||
neo4jPersistentEntity.doWithAssociations((AssociationHandler<Neo4jPersistentProperty>) association -> {
|
||||
|
||||
// create context to bundle parameters
|
||||
NestedRelationshipContext relationshipContext = NestedRelationshipContext
|
||||
.of(handler, propertyAccessor, neo4jPersistentEntity);
|
||||
.of(association, propertyAccessor, neo4jPersistentEntity);
|
||||
|
||||
Collection<?> relatedValuesToStore = Relationships
|
||||
.unifyRelationshipValue(relationshipContext.getInverse(), relationshipContext.getValue());
|
||||
|
||||
RelationshipDescription relationshipDescription = relationshipContext.getRelationship();
|
||||
RelationshipDescription relationshipDescriptionObverse = relationshipDescription.getRelationshipObverse();
|
||||
RelationshipDescription relationshipDescriptionObverse = relationshipDescription
|
||||
.getRelationshipObverse();
|
||||
|
||||
// break recursive procession and deletion of previously created relationships
|
||||
if (processState.hasProcessedEither(relationshipDescriptionObverse, relatedValuesToStore)) {
|
||||
ProcessState processState = stateMachine
|
||||
.getStateOf(relationshipDescriptionObverse, relatedValuesToStore);
|
||||
if (processState == ProcessState.PROCESSED_BOTH) {
|
||||
return;
|
||||
}
|
||||
|
||||
Neo4jPersistentEntity<?> targetNodeDescription = (Neo4jPersistentEntity<?>) neo4jMappingContext
|
||||
.getRequiredNodeDescription(relationshipContext.getAssociationTargetType());
|
||||
|
||||
// remove all relationships before creating all new if the entity is not new
|
||||
// this avoids the usage of cache but might have significant impact on overall performance
|
||||
if (!neo4jPersistentEntity.isNew(parentObject)) {
|
||||
Statement relationshipRemoveQuery = cypherGenerator.createRelationshipRemoveQuery(neo4jPersistentEntity, relationshipDescription, targetNodeDescription);
|
||||
Neo4jPersistentEntity<?> previouslyRelatedPersistentEntity = neo4jMappingContext
|
||||
.getPersistentEntity(relationshipContext.getAssociationTargetType());
|
||||
|
||||
Statement relationshipRemoveQuery = cypherGenerator
|
||||
.createRelationshipRemoveQuery(neo4jPersistentEntity, relationshipDescription,
|
||||
previouslyRelatedPersistentEntity);
|
||||
relationshipCreationMonos.add(
|
||||
neo4jClient.query(renderer.render(relationshipRemoveQuery))
|
||||
.in(inDatabase)
|
||||
@@ -454,43 +460,54 @@ public final class ReactiveNeo4jTemplate implements ReactiveNeo4jOperations, Bea
|
||||
return;
|
||||
}
|
||||
|
||||
processState.markAsProcessed(relationshipDescription, relatedValuesToStore);
|
||||
stateMachine.markAsProcessed(relationshipDescription, relatedValuesToStore);
|
||||
|
||||
for (Object relatedValueToStore : relatedValuesToStore) {
|
||||
|
||||
Object valueToBeSavedPreEvt = relationshipContext.identifyAndExtractRelationshipValue(relatedValueToStore);
|
||||
Mono<Object> valueToBeSavedMono = eventSupport.maybeCallBeforeBind(valueToBeSavedPreEvt);
|
||||
Object valueToBeSavedPreEvt = relationshipContext
|
||||
.identifyAndExtractRelationshipValue(relatedValueToStore);
|
||||
|
||||
relationshipCreationMonos.add(
|
||||
valueToBeSavedMono
|
||||
.flatMap(valueToBeSaved ->
|
||||
saveRelatedNode(valueToBeSaved, relationshipContext.getAssociationTargetType(),
|
||||
targetNodeDescription, inDatabase)
|
||||
.flatMap(relatedInternalId -> {
|
||||
Mono<Void> createRelationship = eventSupport
|
||||
.maybeCallBeforeBind(valueToBeSavedPreEvt)
|
||||
.flatMap(valueToBeSaved -> {
|
||||
Neo4jPersistentEntity<?> targetNodeDescription = neo4jMappingContext
|
||||
.getPersistentEntity(valueToBeSavedPreEvt.getClass());
|
||||
return saveRelatedNode(valueToBeSaved, relationshipContext.getAssociationTargetType(),
|
||||
targetNodeDescription, inDatabase)
|
||||
.flatMap(relatedInternalId -> {
|
||||
|
||||
// if an internal id is used this must get set to link this entity in the next iteration
|
||||
if (targetNodeDescription.isUsingInternalIds()) {
|
||||
PersistentPropertyAccessor<?> targetPropertyAccessor = targetNodeDescription
|
||||
.getPropertyAccessor(valueToBeSaved);
|
||||
targetPropertyAccessor
|
||||
.setProperty(targetNodeDescription.getRequiredIdProperty(),
|
||||
relatedInternalId);
|
||||
}
|
||||
// if an internal id is used this must get set to link this entity in the next iteration
|
||||
if (targetNodeDescription.isUsingInternalIds()) {
|
||||
PersistentPropertyAccessor<?> targetPropertyAccessor = targetNodeDescription
|
||||
.getPropertyAccessor(valueToBeSaved);
|
||||
targetPropertyAccessor
|
||||
.setProperty(targetNodeDescription.getRequiredIdProperty(),
|
||||
relatedInternalId);
|
||||
}
|
||||
|
||||
RelationshipStatementHolder statementHolder = RelationshipStatementHolder.createStatement(
|
||||
neo4jMappingContext, neo4jPersistentEntity, relationshipContext, relatedInternalId, relatedValueToStore);
|
||||
RelationshipStatementHolder statementHolder = RelationshipStatementHolder
|
||||
.createStatement(
|
||||
neo4jMappingContext, neo4jPersistentEntity, relationshipContext,
|
||||
relatedInternalId, relatedValueToStore);
|
||||
|
||||
// in case of no properties the bind will just return an empty map
|
||||
Mono<ResultSummary> relationshipCreationMonoNested = neo4jClient
|
||||
.query(renderer.render(statementHolder.getRelationshipCreationQuery()))
|
||||
.in(inDatabase)
|
||||
.bind(convertIdValues(fromId)).to(FROM_ID_PARAMETER_NAME)
|
||||
.bindAll(statementHolder.getProperties())
|
||||
.run();
|
||||
// in case of no properties the bind will just return an empty map
|
||||
Mono<ResultSummary> relationshipCreationMonoNested = neo4jClient
|
||||
.query(renderer.render(statementHolder.getRelationshipCreationQuery()))
|
||||
.in(inDatabase)
|
||||
.bind(convertIdValues(fromId)).to(FROM_ID_PARAMETER_NAME)
|
||||
.bindAll(statementHolder.getProperties())
|
||||
.run();
|
||||
|
||||
if (processState != ProcessState.PROCESSED_ALL_VALUES) {
|
||||
return relationshipCreationMonoNested.checkpoint()
|
||||
.then(processNestedRelations(targetNodeDescription, valueToBeSaved, inDatabase, processState));
|
||||
}).checkpoint()));
|
||||
.then(processNestedRelations(targetNodeDescription, valueToBeSaved,
|
||||
inDatabase, stateMachine));
|
||||
} else {
|
||||
return relationshipCreationMonoNested.checkpoint().then();
|
||||
}
|
||||
}).checkpoint();
|
||||
});
|
||||
relationshipCreationMonos.add(createRelationship);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 "Neo4j,"
|
||||
* Neo4j Sweden AB [https://neo4j.com]
|
||||
*
|
||||
* This file is part of Neo4j.
|
||||
*
|
||||
* 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.neo4j.springframework.data.integration.imperative;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.neo4j.driver.Driver;
|
||||
import org.neo4j.driver.Record;
|
||||
import org.neo4j.driver.Session;
|
||||
import org.neo4j.springframework.data.config.AbstractNeo4jConfig;
|
||||
import org.neo4j.springframework.data.integration.shared.MultipleRelationshipsThing;
|
||||
import org.neo4j.springframework.data.integration.shared.RelationshipsITBase;
|
||||
import org.neo4j.springframework.data.repository.config.EnableNeo4jRepositories;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
/**
|
||||
* Test cases for various relationship scenarios (self references, multiple times to same instance).
|
||||
*
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class RelationshipsIT extends RelationshipsITBase {
|
||||
|
||||
@Autowired RelationshipsIT(Driver driver) {
|
||||
super(driver);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSaveSingleRelationship(@Autowired MultipleRelationshipsThingRepository repository) {
|
||||
|
||||
MultipleRelationshipsThing p = new MultipleRelationshipsThing("p");
|
||||
p.setTypeA(new MultipleRelationshipsThing("c"));
|
||||
|
||||
p = repository.save(p);
|
||||
|
||||
Optional<MultipleRelationshipsThing> loadedThing = repository.findById(p.getId());
|
||||
assertThat(loadedThing).isPresent()
|
||||
.map(MultipleRelationshipsThing::getTypeA)
|
||||
.map(MultipleRelationshipsThing::getName)
|
||||
.hasValue("c");
|
||||
|
||||
try (Session session = driver.session()) {
|
||||
List<String> names = session.run("MATCH (n:MultipleRelationshipsThing) RETURN n.name AS name")
|
||||
.list(r -> r.get("name").asString());
|
||||
assertThat(names).hasSize(2).containsExactlyInAnyOrder("p", "c");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSaveSingleRelationshipInList(@Autowired MultipleRelationshipsThingRepository repository) {
|
||||
|
||||
MultipleRelationshipsThing p = new MultipleRelationshipsThing("p");
|
||||
p.setTypeB(Collections.singletonList(new MultipleRelationshipsThing("c")));
|
||||
|
||||
p = repository.save(p);
|
||||
|
||||
Optional<MultipleRelationshipsThing> loadedThing = repository.findById(p.getId());
|
||||
assertThat(loadedThing).isPresent()
|
||||
.map(MultipleRelationshipsThing::getTypeB)
|
||||
.hasValueSatisfying(
|
||||
l -> assertThat(l).extracting(MultipleRelationshipsThing::getName).containsExactly("c"));
|
||||
|
||||
try (Session session = driver.session()) {
|
||||
List<String> names = session.run("MATCH (n:MultipleRelationshipsThing) RETURN n.name AS name")
|
||||
.list(r -> r.get("name").asString());
|
||||
assertThat(names).hasSize(2).containsExactlyInAnyOrder("p", "c");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This stores multiple, different instances.
|
||||
*
|
||||
* @param repository The repository to use.
|
||||
*/
|
||||
@Test
|
||||
void shouldSaveMultipleRelationshipsOfSameObjectType(@Autowired MultipleRelationshipsThingRepository repository) {
|
||||
|
||||
MultipleRelationshipsThing p = new MultipleRelationshipsThing("p");
|
||||
p.setTypeA(new MultipleRelationshipsThing("c1"));
|
||||
p.setTypeB(Collections.singletonList(new MultipleRelationshipsThing("c2")));
|
||||
p.setTypeC(Collections.singletonList(new MultipleRelationshipsThing("c3")));
|
||||
|
||||
p = repository.save(p);
|
||||
|
||||
Optional<MultipleRelationshipsThing> loadedThing = repository.findById(p.getId());
|
||||
assertThat(loadedThing).isPresent()
|
||||
.hasValueSatisfying(t -> {
|
||||
|
||||
MultipleRelationshipsThing typeA = t.getTypeA();
|
||||
List<MultipleRelationshipsThing> typeB = t.getTypeB();
|
||||
List<MultipleRelationshipsThing> typeC = t.getTypeC();
|
||||
|
||||
assertThat(typeA).isNotNull();
|
||||
assertThat(typeA).extracting(MultipleRelationshipsThing::getName).isEqualTo("c1");
|
||||
assertThat(typeB).extracting(MultipleRelationshipsThing::getName).containsExactly("c2");
|
||||
assertThat(typeC).extracting(MultipleRelationshipsThing::getName).containsExactly("c3");
|
||||
});
|
||||
|
||||
try (Session session = driver.session()) {
|
||||
|
||||
List<String> names = session.run(
|
||||
"MATCH (n:MultipleRelationshipsThing {name: 'p'}) - [r:TYPE_A|TYPE_B|TYPE_C] -> (o) RETURN r, o")
|
||||
.list(record -> {
|
||||
String type = record.get("r").asRelationship().type();
|
||||
String name = record.get("o").get("name").asString();
|
||||
return type + "_" + name;
|
||||
});
|
||||
assertThat(names).containsExactlyInAnyOrder("TYPE_A_c1", "TYPE_B_c2", "TYPE_C_c3");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This stores the same instance in different relationships
|
||||
*
|
||||
* @param repository The repository to use.
|
||||
*/
|
||||
@Test
|
||||
void shouldSaveMultipleRelationshipsOfSameInstance(@Autowired MultipleRelationshipsThingRepository repository) {
|
||||
|
||||
MultipleRelationshipsThing p = new MultipleRelationshipsThing("p");
|
||||
MultipleRelationshipsThing c = new MultipleRelationshipsThing("c1");
|
||||
p.setTypeA(c);
|
||||
p.setTypeB(Collections.singletonList(c));
|
||||
p.setTypeC(Collections.singletonList(c));
|
||||
|
||||
p = repository.save(p);
|
||||
|
||||
Optional<MultipleRelationshipsThing> loadedThing = repository.findById(p.getId());
|
||||
assertThat(loadedThing).isPresent()
|
||||
.hasValueSatisfying(t -> {
|
||||
|
||||
MultipleRelationshipsThing typeA = t.getTypeA();
|
||||
List<MultipleRelationshipsThing> typeB = t.getTypeB();
|
||||
List<MultipleRelationshipsThing> typeC = t.getTypeC();
|
||||
|
||||
assertThat(typeA).isNotNull();
|
||||
assertThat(typeA).extracting(MultipleRelationshipsThing::getName).isEqualTo("c1");
|
||||
assertThat(typeB).extracting(MultipleRelationshipsThing::getName).containsExactly("c1");
|
||||
assertThat(typeC).extracting(MultipleRelationshipsThing::getName).containsExactly("c1");
|
||||
});
|
||||
|
||||
try (Session session = driver.session()) {
|
||||
|
||||
List<String> names = session.run(
|
||||
"MATCH (n:MultipleRelationshipsThing {name: 'p'}) - [r:TYPE_A|TYPE_B|TYPE_C] -> (o) RETURN r, o")
|
||||
.list(record -> {
|
||||
String type = record.get("r").asRelationship().type();
|
||||
String name = record.get("o").get("name").asString();
|
||||
return type + "_" + name;
|
||||
});
|
||||
assertThat(names).containsExactlyInAnyOrder("TYPE_A_c1", "TYPE_B_c1", "TYPE_C_c1");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This stores the same instance in different relationships
|
||||
*
|
||||
* @param repository The repository to use.
|
||||
*/
|
||||
@Test
|
||||
void shouldSaveMultipleRelationshipsOfSameInstanceWithBackReference(
|
||||
@Autowired MultipleRelationshipsThingRepository repository) {
|
||||
|
||||
MultipleRelationshipsThing p = new MultipleRelationshipsThing("p");
|
||||
MultipleRelationshipsThing c = new MultipleRelationshipsThing("c1");
|
||||
p.setTypeA(c);
|
||||
p.setTypeB(Collections.singletonList(c));
|
||||
p.setTypeC(Collections.singletonList(c));
|
||||
|
||||
c.setTypeA(p);
|
||||
|
||||
p = repository.save(p);
|
||||
|
||||
Optional<MultipleRelationshipsThing> loadedThing = repository.findById(p.getId());
|
||||
assertThat(loadedThing).isPresent()
|
||||
.hasValueSatisfying(t -> {
|
||||
|
||||
MultipleRelationshipsThing typeA = t.getTypeA();
|
||||
List<MultipleRelationshipsThing> typeB = t.getTypeB();
|
||||
List<MultipleRelationshipsThing> typeC = t.getTypeC();
|
||||
|
||||
assertThat(typeA).isNotNull();
|
||||
assertThat(typeA).extracting(MultipleRelationshipsThing::getName).isEqualTo("c1");
|
||||
assertThat(typeB).extracting(MultipleRelationshipsThing::getName).containsExactly("c1");
|
||||
assertThat(typeC).extracting(MultipleRelationshipsThing::getName).containsExactly("c1");
|
||||
});
|
||||
|
||||
try (Session session = driver.session()) {
|
||||
|
||||
Function<Record, String> withMapper = record -> {
|
||||
String type = record.get("r").asRelationship().type();
|
||||
String name = record.get("o").get("name").asString();
|
||||
return type + "_" + name;
|
||||
};
|
||||
|
||||
String query = "MATCH (n:MultipleRelationshipsThing {name: $name}) - [r:TYPE_A|TYPE_B|TYPE_C] -> (o) RETURN r, o";
|
||||
List<String> names = session.run(query, Collections.singletonMap("name", "p")).list(withMapper);
|
||||
assertThat(names).containsExactlyInAnyOrder("TYPE_A_c1", "TYPE_B_c1", "TYPE_C_c1");
|
||||
|
||||
names = session.run(query, Collections.singletonMap("name", "c1")).list(withMapper);
|
||||
assertThat(names).containsExactlyInAnyOrder("TYPE_A_p");
|
||||
}
|
||||
}
|
||||
|
||||
interface MultipleRelationshipsThingRepository extends CrudRepository<MultipleRelationshipsThing, Long> {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableTransactionManagement
|
||||
@EnableNeo4jRepositories(considerNestedRepositories = true)
|
||||
static class Config extends AbstractNeo4jConfig {
|
||||
|
||||
@Bean
|
||||
public Driver driver() {
|
||||
return neo4jConnectionSupport.getDriver();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,13 +56,16 @@ class KotlinIT {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
Session session = driver.session();
|
||||
Transaction transaction = session.beginTransaction();
|
||||
transaction.run("MATCH (n) detach delete n");
|
||||
transaction.run("CREATE (n:KotlinPerson) SET n.name = $personName", Values.parameters("personName", PERSON_NAME));
|
||||
transaction.commit();
|
||||
transaction.close();
|
||||
session.close();
|
||||
try (
|
||||
Session session = driver.session();
|
||||
Transaction transaction = session.beginTransaction()
|
||||
) {
|
||||
transaction.run("MATCH (n) detach delete n").consume();
|
||||
transaction
|
||||
.run("CREATE (n:KotlinPerson) SET n.name = $personName", Values.parameters("personName", PERSON_NAME))
|
||||
.consume();
|
||||
transaction.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 "Neo4j,"
|
||||
* Neo4j Sweden AB [https://neo4j.com]
|
||||
*
|
||||
* This file is part of Neo4j.
|
||||
*
|
||||
* 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.neo4j.springframework.data.integration.reactive;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.neo4j.driver.Driver;
|
||||
import org.neo4j.driver.Record;
|
||||
import org.neo4j.driver.Session;
|
||||
import org.neo4j.springframework.data.config.AbstractReactiveNeo4jConfig;
|
||||
import org.neo4j.springframework.data.integration.shared.MultipleRelationshipsThing;
|
||||
import org.neo4j.springframework.data.integration.shared.RelationshipsITBase;
|
||||
import org.neo4j.springframework.data.repository.config.EnableReactiveNeo4jRepositories;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
/**
|
||||
* Test cases for various relationship scenarios (self references, multiple times to same instance).
|
||||
*
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class ReactiveRelationshipsIT extends RelationshipsITBase {
|
||||
|
||||
@Autowired ReactiveRelationshipsIT(Driver driver) {
|
||||
super(driver);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSaveSingleRelationship(@Autowired MultipleRelationshipsThingRepository repository) {
|
||||
|
||||
MultipleRelationshipsThing p = new MultipleRelationshipsThing("p");
|
||||
p.setTypeA(new MultipleRelationshipsThing("c"));
|
||||
|
||||
repository.save(p)
|
||||
.map(MultipleRelationshipsThing::getId)
|
||||
.flatMap(repository::findById)
|
||||
.as(StepVerifier::create)
|
||||
.assertNext(loadedThing -> assertThat(loadedThing)
|
||||
.extracting(MultipleRelationshipsThing::getTypeA)
|
||||
.extracting(MultipleRelationshipsThing::getName)
|
||||
.isEqualTo("c"))
|
||||
.verifyComplete();
|
||||
|
||||
try (Session session = driver.session()) {
|
||||
List<String> names = session.run("MATCH (n:MultipleRelationshipsThing) RETURN n.name AS name")
|
||||
.list(r -> r.get("name").asString());
|
||||
assertThat(names).hasSize(2).containsExactlyInAnyOrder("p", "c");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSaveSingleRelationshipInList(@Autowired MultipleRelationshipsThingRepository repository) {
|
||||
|
||||
MultipleRelationshipsThing p = new MultipleRelationshipsThing("p");
|
||||
p.setTypeB(Collections.singletonList(new MultipleRelationshipsThing("c")));
|
||||
|
||||
repository.save(p)
|
||||
.map(MultipleRelationshipsThing::getId)
|
||||
.flatMap(repository::findById)
|
||||
.as(StepVerifier::create)
|
||||
.assertNext(loadedThing -> assertThat(loadedThing.getTypeB())
|
||||
.extracting(MultipleRelationshipsThing::getName)
|
||||
.containsExactly("c"))
|
||||
.verifyComplete();
|
||||
|
||||
try (Session session = driver.session()) {
|
||||
List<String> names = session.run("MATCH (n:MultipleRelationshipsThing) RETURN n.name AS name")
|
||||
.list(r -> r.get("name").asString());
|
||||
assertThat(names).hasSize(2).containsExactlyInAnyOrder("p", "c");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This stores multiple, different instances.
|
||||
*
|
||||
* @param repository The repository to use.
|
||||
*/
|
||||
@Test
|
||||
void shouldSaveMultipleRelationshipsOfSameObjectType(@Autowired MultipleRelationshipsThingRepository repository) {
|
||||
|
||||
MultipleRelationshipsThing p = new MultipleRelationshipsThing("p");
|
||||
p.setTypeA(new MultipleRelationshipsThing("c1"));
|
||||
p.setTypeB(Collections.singletonList(new MultipleRelationshipsThing("c2")));
|
||||
p.setTypeC(Collections.singletonList(new MultipleRelationshipsThing("c3")));
|
||||
|
||||
repository.save(p)
|
||||
.map(MultipleRelationshipsThing::getId)
|
||||
.flatMap(repository::findById)
|
||||
.as(StepVerifier::create)
|
||||
.assertNext(loadedThing -> {
|
||||
MultipleRelationshipsThing typeA = loadedThing.getTypeA();
|
||||
List<MultipleRelationshipsThing> typeB = loadedThing.getTypeB();
|
||||
List<MultipleRelationshipsThing> typeC = loadedThing.getTypeC();
|
||||
|
||||
assertThat(typeA).isNotNull();
|
||||
assertThat(typeA).extracting(MultipleRelationshipsThing::getName).isEqualTo("c1");
|
||||
assertThat(typeB).extracting(MultipleRelationshipsThing::getName).containsExactly("c2");
|
||||
assertThat(typeC).extracting(MultipleRelationshipsThing::getName).containsExactly("c3");
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
try (Session session = driver.session()) {
|
||||
|
||||
List<String> names = session.run(
|
||||
"MATCH (n:MultipleRelationshipsThing {name: 'p'}) - [r:TYPE_A|TYPE_B|TYPE_C] -> (o) RETURN r, o")
|
||||
.list(record -> {
|
||||
String type = record.get("r").asRelationship().type();
|
||||
String name = record.get("o").get("name").asString();
|
||||
return type + "_" + name;
|
||||
});
|
||||
assertThat(names).containsExactlyInAnyOrder("TYPE_A_c1", "TYPE_B_c2", "TYPE_C_c3");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This stores the same instance in different relationships
|
||||
*
|
||||
* @param repository The repository to use.
|
||||
*/
|
||||
@Test
|
||||
void shouldSaveMultipleRelationshipsOfSameInstance(@Autowired MultipleRelationshipsThingRepository repository) {
|
||||
|
||||
MultipleRelationshipsThing p = new MultipleRelationshipsThing("p");
|
||||
MultipleRelationshipsThing c = new MultipleRelationshipsThing("c1");
|
||||
p.setTypeA(c);
|
||||
p.setTypeB(Collections.singletonList(c));
|
||||
p.setTypeC(Collections.singletonList(c));
|
||||
|
||||
repository.save(p)
|
||||
.map(MultipleRelationshipsThing::getId)
|
||||
.flatMap(repository::findById)
|
||||
.as(StepVerifier::create)
|
||||
.assertNext(loadedThing -> {
|
||||
|
||||
MultipleRelationshipsThing typeA = loadedThing.getTypeA();
|
||||
List<MultipleRelationshipsThing> typeB = loadedThing.getTypeB();
|
||||
List<MultipleRelationshipsThing> typeC = loadedThing.getTypeC();
|
||||
|
||||
assertThat(typeA).isNotNull();
|
||||
assertThat(typeA).extracting(MultipleRelationshipsThing::getName).isEqualTo("c1");
|
||||
assertThat(typeB).extracting(MultipleRelationshipsThing::getName).containsExactly("c1");
|
||||
assertThat(typeC).extracting(MultipleRelationshipsThing::getName).containsExactly("c1");
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
try (Session session = driver.session()) {
|
||||
|
||||
List<String> names = session.run(
|
||||
"MATCH (n:MultipleRelationshipsThing {name: 'p'}) - [r:TYPE_A|TYPE_B|TYPE_C] -> (o) RETURN r, o")
|
||||
.list(record -> {
|
||||
String type = record.get("r").asRelationship().type();
|
||||
String name = record.get("o").get("name").asString();
|
||||
return type + "_" + name;
|
||||
});
|
||||
assertThat(names).containsExactlyInAnyOrder("TYPE_A_c1", "TYPE_B_c1", "TYPE_C_c1");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This stores the same instance in different relationships
|
||||
*
|
||||
* @param repository The repository to use.
|
||||
*/
|
||||
@Test
|
||||
void shouldSaveMultipleRelationshipsOfSameInstanceWithBackReference(
|
||||
@Autowired MultipleRelationshipsThingRepository repository) {
|
||||
|
||||
MultipleRelationshipsThing p = new MultipleRelationshipsThing("p");
|
||||
MultipleRelationshipsThing c = new MultipleRelationshipsThing("c1");
|
||||
p.setTypeA(c);
|
||||
p.setTypeB(Collections.singletonList(c));
|
||||
p.setTypeC(Collections.singletonList(c));
|
||||
|
||||
c.setTypeA(p);
|
||||
|
||||
repository.save(p)
|
||||
.map(MultipleRelationshipsThing::getId)
|
||||
.flatMap(repository::findById)
|
||||
.as(StepVerifier::create)
|
||||
.assertNext(loadedThing -> {
|
||||
|
||||
MultipleRelationshipsThing typeA = loadedThing.getTypeA();
|
||||
List<MultipleRelationshipsThing> typeB = loadedThing.getTypeB();
|
||||
List<MultipleRelationshipsThing> typeC = loadedThing.getTypeC();
|
||||
|
||||
assertThat(typeA).isNotNull();
|
||||
assertThat(typeA).extracting(MultipleRelationshipsThing::getName).isEqualTo("c1");
|
||||
assertThat(typeB).extracting(MultipleRelationshipsThing::getName).containsExactly("c1");
|
||||
assertThat(typeC).extracting(MultipleRelationshipsThing::getName).containsExactly("c1");
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
try (Session session = driver.session()) {
|
||||
|
||||
Function<Record, String> withMapper = record -> {
|
||||
String type = record.get("r").asRelationship().type();
|
||||
String name = record.get("o").get("name").asString();
|
||||
return type + "_" + name;
|
||||
};
|
||||
|
||||
String query = "MATCH (n:MultipleRelationshipsThing {name: $name}) - [r:TYPE_A|TYPE_B|TYPE_C] -> (o) RETURN r, o";
|
||||
List<String> names = session.run(query, Collections.singletonMap("name", "p")).list(withMapper);
|
||||
assertThat(names).containsExactlyInAnyOrder("TYPE_A_c1", "TYPE_B_c1", "TYPE_C_c1");
|
||||
|
||||
names = session.run(query, Collections.singletonMap("name", "c1")).list(withMapper);
|
||||
assertThat(names).containsExactlyInAnyOrder("TYPE_A_p");
|
||||
}
|
||||
}
|
||||
|
||||
interface MultipleRelationshipsThingRepository extends ReactiveCrudRepository<MultipleRelationshipsThing, Long> {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableTransactionManagement
|
||||
@EnableReactiveNeo4jRepositories(considerNestedRepositories = true)
|
||||
static class Config extends AbstractReactiveNeo4jConfig {
|
||||
|
||||
@Bean
|
||||
public Driver driver() {
|
||||
return neo4jConnectionSupport.getDriver();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 "Neo4j,"
|
||||
* Neo4j Sweden AB [https://neo4j.com]
|
||||
*
|
||||
* This file is part of Neo4j.
|
||||
*
|
||||
* 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.neo4j.springframework.data.integration.shared;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.neo4j.springframework.data.core.schema.GeneratedValue;
|
||||
import org.neo4j.springframework.data.core.schema.Id;
|
||||
import org.neo4j.springframework.data.core.schema.Node;
|
||||
|
||||
/**
|
||||
* This thing has several relationships to other things of the same kind but with a different type.
|
||||
* It is used to test whether all types are stored correctly even if those relationships point to
|
||||
* the same instance of the thing.
|
||||
*
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
@Node
|
||||
public class MultipleRelationshipsThing {
|
||||
|
||||
@Id @GeneratedValue Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
private MultipleRelationshipsThing typeA;
|
||||
|
||||
private List<MultipleRelationshipsThing> typeB;
|
||||
|
||||
private List<MultipleRelationshipsThing> typeC;
|
||||
|
||||
public MultipleRelationshipsThing(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public MultipleRelationshipsThing getTypeA() {
|
||||
return typeA;
|
||||
}
|
||||
|
||||
public void setTypeA(MultipleRelationshipsThing typeA) {
|
||||
this.typeA = typeA;
|
||||
}
|
||||
|
||||
public List<MultipleRelationshipsThing> getTypeB() {
|
||||
return typeB;
|
||||
}
|
||||
|
||||
public void setTypeB(List<MultipleRelationshipsThing> typeB) {
|
||||
this.typeB = typeB;
|
||||
}
|
||||
|
||||
public List<MultipleRelationshipsThing> getTypeC() {
|
||||
return typeC;
|
||||
}
|
||||
|
||||
public void setTypeC(List<MultipleRelationshipsThing> typeC) {
|
||||
this.typeC = typeC;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 "Neo4j,"
|
||||
* Neo4j Sweden AB [https://neo4j.com]
|
||||
*
|
||||
* This file is part of Neo4j.
|
||||
*
|
||||
* 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.neo4j.springframework.data.integration.shared;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.neo4j.driver.Driver;
|
||||
import org.neo4j.driver.Session;
|
||||
import org.neo4j.driver.Transaction;
|
||||
import org.neo4j.springframework.data.test.Neo4jExtension;
|
||||
import org.neo4j.springframework.data.test.Neo4jIntegrationTest;
|
||||
|
||||
/**
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
@Neo4jIntegrationTest
|
||||
public abstract class RelationshipsITBase {
|
||||
|
||||
protected static Neo4jExtension.Neo4jConnectionSupport neo4jConnectionSupport;
|
||||
|
||||
protected final Driver driver;
|
||||
|
||||
protected RelationshipsITBase(Driver driver) {
|
||||
this.driver = driver;
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
try (
|
||||
Session session = driver.session();
|
||||
Transaction transaction = session.beginTransaction()
|
||||
) {
|
||||
transaction.run("MATCH (n) detach delete n").consume();
|
||||
transaction.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -258,7 +258,7 @@ public class Neo4jExtension implements BeforeAllCallback, BeforeEachCallback {
|
||||
|
||||
private final String repository = Optional.ofNullable(System.getenv(SYS_PROPERTY_NEO4J_REPOSITORY)).orElse("neo4j");
|
||||
|
||||
private final String imageVersion = Optional.ofNullable(System.getenv(SYS_PROPERTY_NEO4J_VERSION)).orElse("3.5.12");
|
||||
private final String imageVersion = Optional.ofNullable(System.getenv(SYS_PROPERTY_NEO4J_VERSION)).orElse("4.0");
|
||||
|
||||
private final Neo4jContainer<?> neo4jContainer = new Neo4jContainer<>(repository + ":" + imageVersion)
|
||||
.withoutAuthentication()
|
||||
|
||||
Reference in New Issue
Block a user