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 7914e4aa2..89dff6c83 100644 --- a/src/main/java/org/springframework/data/neo4j/core/Neo4jTemplate.java +++ b/src/main/java/org/springframework/data/neo4j/core/Neo4jTemplate.java @@ -401,7 +401,7 @@ public final class Neo4jTemplate implements neo4jMappingContext.getRequiredBinderFunctionFor((Class) entityToBeSaved.getClass()) ); Optional newOrUpdatedNode = neo4jClient - .query(() -> renderer.render(cypherGenerator.prepareSaveOf(entityMetaData, dynamicLabels))) + .query(() -> renderer.render(cypherGenerator.prepareSaveOf(entityMetaData, dynamicLabels, TemplateSupport.rendererRendersElementId(renderer)))) .bind(entityToBeSaved) .with(binderFunction) .fetchAs(Entity.class) @@ -416,7 +416,7 @@ public final class Neo4jTemplate implements } Object elementId = newOrUpdatedNode.map(node -> { - if (!entityMetaData.isUsingDeprecatedInternalId() && entityMetaData.isUsingInternalIds()) { + if (!entityMetaData.isUsingDeprecatedInternalId() && TemplateSupport.rendererRendersElementId(renderer)) { return IdentitySupport.getElementId(node); } return node.id(); @@ -764,6 +764,7 @@ public final class Neo4jTemplate implements // Remove all relationships before creating all new if the entity is not new and the relationship // has not been processed before. // This avoids the usage of cache but might have significant impact on overall performance + boolean canUseElementId = TemplateSupport.rendererRendersElementId(renderer); if (!isParentObjectNew && !stateMachine.hasProcessedRelationship(fromId, relationshipDescription)) { List knownRelationshipsIds = new ArrayList<>(); @@ -780,7 +781,7 @@ public final class Neo4jTemplate implements } } - Statement relationshipRemoveQuery = cypherGenerator.prepareDeleteOf(sourceEntity, relationshipDescription); + Statement relationshipRemoveQuery = cypherGenerator.prepareDeleteOf(sourceEntity, relationshipDescription, canUseElementId); neo4jClient.query(renderer.render(relationshipRemoveQuery)) .bind(convertIdValues(sourceEntity.getIdProperty(), fromId)) // @@ -858,7 +859,7 @@ public final class Neo4jTemplate implements // create new dynamic relationship properties if (relationshipDescription.hasRelationshipProperties() && isNewRelationship && idProperty != null) { CreateRelationshipStatementHolder statementHolder = neo4jMappingContext.createStatementForSingleRelationship( - sourceEntity, relationshipDescription, relatedValueToStore, true); + sourceEntity, relationshipDescription, relatedValueToStore, true, canUseElementId); List row = Collections.singletonList(properties); statementHolder = statementHolder.addProperty(Constants.NAME_OF_RELATIONSHIP_LIST_PARAM, row); @@ -879,7 +880,7 @@ public final class Neo4jTemplate implements } else { // plain (new or to update) dynamic relationship or dynamic relationships with properties to update CreateRelationshipStatementHolder statementHolder = neo4jMappingContext.createStatementForSingleRelationship( - sourceEntity, relationshipDescription, relatedValueToStore, false); + sourceEntity, relationshipDescription, relatedValueToStore, false, canUseElementId); List row = Collections.singletonList(properties); statementHolder = statementHolder.addProperty(Constants.NAME_OF_RELATIONSHIP_LIST_PARAM, row); @@ -921,7 +922,7 @@ public final class Neo4jTemplate implements // batch operations if (!(relationshipDescription.hasRelationshipProperties() || relationshipDescription.isDynamic() || plainRelationshipRows.isEmpty())) { CreateRelationshipStatementHolder statementHolder = neo4jMappingContext.createStatementForImperativeSimpleRelationshipBatch( - sourceEntity, relationshipDescription, plainRelationshipRows); + sourceEntity, relationshipDescription, plainRelationshipRows, canUseElementId); statementHolder = statementHolder.addProperty(Constants.NAME_OF_RELATIONSHIP_LIST_PARAM, plainRelationshipRows); neo4jClient.query(renderer.render(statementHolder.getStatement())) .bindAll(statementHolder.getProperties()) @@ -929,7 +930,7 @@ public final class Neo4jTemplate implements } else if (relationshipDescription.hasRelationshipProperties()) { if (!relationshipPropertiesRows.isEmpty()) { CreateRelationshipStatementHolder statementHolder = neo4jMappingContext.createStatementForImperativeRelationshipsWithPropertiesBatch(false, - sourceEntity, relationshipDescription, updateRelatedValuesToStore, relationshipPropertiesRows); + sourceEntity, relationshipDescription, updateRelatedValuesToStore, relationshipPropertiesRows, canUseElementId); statementHolder = statementHolder.addProperty(Constants.NAME_OF_RELATIONSHIP_LIST_PARAM, relationshipPropertiesRows); neo4jClient.query(renderer.render(statementHolder.getStatement())) @@ -938,7 +939,7 @@ public final class Neo4jTemplate implements } if (!newRelatedValuesToStore.isEmpty()) { CreateRelationshipStatementHolder statementHolder = neo4jMappingContext.createStatementForImperativeRelationshipsWithPropertiesBatch(true, - sourceEntity, relationshipDescription, newRelatedValuesToStore, newRelationshipPropertiesRows); + sourceEntity, relationshipDescription, newRelatedValuesToStore, newRelationshipPropertiesRows, canUseElementId); List all = new ArrayList<>(neo4jClient.query(renderer.render(statementHolder.getStatement())) .bindAll(statementHolder.getProperties()) .fetchAs(Object.class) @@ -996,7 +997,7 @@ public final class Neo4jTemplate implements return tree; }); Optional optionalSavedNode = neo4jClient - .query(() -> renderer.render(cypherGenerator.prepareSaveOf(targetNodeDescription, dynamicLabels))) + .query(() -> renderer.render(cypherGenerator.prepareSaveOf(targetNodeDescription, dynamicLabels, TemplateSupport.rendererRendersElementId(renderer)))) .bind(entity).with(binderFunction) .fetchAs(Entity.class) .one(); 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 259caeb9e..033cc441c 100644 --- a/src/main/java/org/springframework/data/neo4j/core/ReactiveNeo4jTemplate.java +++ b/src/main/java/org/springframework/data/neo4j/core/ReactiveNeo4jTemplate.java @@ -438,7 +438,8 @@ public final class ReactiveNeo4jTemplate implements includedProperties, entityMetaData, neo4jMappingContext.getRequiredBinderFunctionFor((Class) entityToBeSaved.getClass())); - Mono idMono = this.neo4jClient.query(() -> renderer.render(cypherGenerator.prepareSaveOf(entityMetaData, dynamicLabels))) + boolean canUseElementId = TemplateSupport.rendererRendersElementId(renderer); + Mono idMono = this.neo4jClient.query(() -> renderer.render(cypherGenerator.prepareSaveOf(entityMetaData, dynamicLabels, canUseElementId))) .bind(entityToBeSaved) .with(binderFunction) .fetchAs(Entity.class) @@ -452,7 +453,7 @@ public final class ReactiveNeo4jTemplate implements PersistentPropertyAccessor propertyAccessor = entityMetaData.getPropertyAccessor(entityToBeSaved); return idMono.doOnNext(newOrUpdatedNode -> { - var elementId = !entityMetaData.isUsingDeprecatedInternalId() && entityMetaData.isUsingInternalIds() + var elementId = !entityMetaData.isUsingDeprecatedInternalId() && canUseElementId ? IdentitySupport.getElementId(newOrUpdatedNode) : newOrUpdatedNode.id(); TemplateSupport.setGeneratedIdIfNecessary(entityMetaData, propertyAccessor, elementId, Optional.of(newOrUpdatedNode)); @@ -901,6 +902,7 @@ public final class ReactiveNeo4jTemplate implements // Remove all relationships before creating all new if the entity is not new and the relationship // has not been processed before. // This avoids the usage of cache but might have significant impact on overall performance + boolean canUseElementId = TemplateSupport.rendererRendersElementId(renderer); if (!isParentObjectNew && !stateMachine.hasProcessedRelationship(fromId, relationshipDescription)) { List knownRelationshipsIds = new ArrayList<>(); @@ -919,7 +921,7 @@ public final class ReactiveNeo4jTemplate implements } } - Statement relationshipRemoveQuery = cypherGenerator.prepareDeleteOf(sourceEntity, relationshipDescription); + Statement relationshipRemoveQuery = cypherGenerator.prepareDeleteOf(sourceEntity, relationshipDescription, canUseElementId); relationshipDeleteMonos.add( neo4jClient.query(renderer.render(relationshipRemoveQuery)) @@ -990,7 +992,7 @@ public final class ReactiveNeo4jTemplate implements boolean isNewRelationship = idValue == null; CreateRelationshipStatementHolder statementHolder = neo4jMappingContext.createStatementForSingleRelationship( - sourceEntity, relationshipDescription, relatedValueToStore, isNewRelationship); + sourceEntity, relationshipDescription, relatedValueToStore, isNewRelationship, canUseElementId); Map properties = new HashMap<>(); properties.put(Constants.FROM_ID_PARAMETER_NAME, convertIdValues(sourceEntity.getRequiredIdProperty(), fromId)); @@ -1086,7 +1088,7 @@ public final class ReactiveNeo4jTemplate implements return tree; }); return neo4jClient - .query(() -> renderer.render(cypherGenerator.prepareSaveOf(targetNodeDescription, dynamicLabels))) + .query(() -> renderer.render(cypherGenerator.prepareSaveOf(targetNodeDescription, dynamicLabels, TemplateSupport.rendererRendersElementId(renderer)))) .bind(entity).with(binderFunction) .fetchAs(Entity.class) .one(); diff --git a/src/main/java/org/springframework/data/neo4j/core/TemplateSupport.java b/src/main/java/org/springframework/data/neo4j/core/TemplateSupport.java index 776bdac19..264e6ca8a 100644 --- a/src/main/java/org/springframework/data/neo4j/core/TemplateSupport.java +++ b/src/main/java/org/springframework/data/neo4j/core/TemplateSupport.java @@ -427,10 +427,10 @@ public final class TemplateSupport { * @return {@literal true} if renderer will use elementId */ static boolean rendererCanUseElementIdIfPresent(Renderer renderer, Neo4jPersistentEntity targetEntity) { - return !targetEntity.isUsingDeprecatedInternalId() && targetEntity.isUsingInternalIds() && rendererRendersElementId(renderer); + return !targetEntity.isUsingDeprecatedInternalId() && rendererRendersElementId(renderer); } - private static boolean rendererRendersElementId(Renderer renderer) { + static boolean rendererRendersElementId(Renderer renderer) { return renderer.render(Cypher.returning(Functions.elementId(Cypher.anyNode("n"))).build()) .equals("RETURN elementId(n)"); } diff --git a/src/main/java/org/springframework/data/neo4j/core/mapping/CypherGenerator.java b/src/main/java/org/springframework/data/neo4j/core/mapping/CypherGenerator.java index d150f5137..049ea164d 100644 --- a/src/main/java/org/springframework/data/neo4j/core/mapping/CypherGenerator.java +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/CypherGenerator.java @@ -299,7 +299,7 @@ public enum CypherGenerator { } public Statement prepareSaveOf(NodeDescription nodeDescription, - UnaryOperator updateDecorator) { + UnaryOperator updateDecorator, boolean canUseElementId) { String primaryLabel = nodeDescription.getPrimaryLabel(); List additionalLabels = nodeDescription.getAdditionalLabels(); @@ -367,7 +367,7 @@ public enum CypherGenerator { Statement updateIfExists; var neo4jPersistentEntity = (Neo4jPersistentEntity) nodeDescription; - var nodeIdFunction = getNodeIdFunction(neo4jPersistentEntity); + var nodeIdFunction = getNodeIdFunction(neo4jPersistentEntity, canUseElementId); if (neo4jPersistentEntity.hasVersionProperty()) { Property versionProperty = rootNode.property(neo4jPersistentEntity.getRequiredVersionProperty().getName()); @@ -436,7 +436,7 @@ public enum CypherGenerator { @NonNull public Statement prepareSaveOfRelationship(Neo4jPersistentEntity neo4jPersistentEntity, - RelationshipDescription relationship, @Nullable String dynamicRelationshipType) { + RelationshipDescription relationship, @Nullable String dynamicRelationshipType, boolean canUseElementId) { final Node startNode = neo4jPersistentEntity.isUsingInternalIds() ? anyNode(START_NODE_NAME) : node(neo4jPersistentEntity.getPrimaryLabel(), neo4jPersistentEntity.getAdditionalLabels()) @@ -450,22 +450,22 @@ public enum CypherGenerator { startNode.relationshipTo(endNode, type) : startNode.relationshipFrom(endNode, type)).named(RELATIONSHIP_NAME); - var startNodeIdFunction = getNodeIdFunction(neo4jPersistentEntity); + var startNodeIdFunction = getNodeIdFunction(neo4jPersistentEntity, canUseElementId); return match(startNode) .where(startNodeIdFunction.apply(startNode).isEqualTo(idParameter)) .match(endNode) - .where(getEndNodeIdFunction((Neo4jPersistentEntity) relationship.getTarget()).apply(endNode).isEqualTo(parameter(Constants.TO_ID_PARAMETER_NAME))) + .where(getEndNodeIdFunction((Neo4jPersistentEntity) relationship.getTarget(), canUseElementId).apply(endNode).isEqualTo(parameter(Constants.TO_ID_PARAMETER_NAME))) .merge(relationshipFragment) .returning(getReturnedIdExpressionsForRelationship(relationship, relationshipFragment)) .build(); } - private static Function getNodeIdFunction(@Nullable Neo4jPersistentEntity entity) { + private static Function getNodeIdFunction(@Nullable Neo4jPersistentEntity entity, boolean canUseElementId) { Function startNodeIdFunction; var idProperty = entity.getRequiredIdProperty(); if (entity.isUsingInternalIds()) { - if (entity.isUsingDeprecatedInternalId()) { + if (entity.isUsingDeprecatedInternalId() || !canUseElementId) { startNodeIdFunction = Functions::id; } else { startNodeIdFunction = Functions::elementId; @@ -476,13 +476,13 @@ public enum CypherGenerator { return startNodeIdFunction; } - private static Function getEndNodeIdFunction(@Nullable Neo4jPersistentEntity entity) { + private static Function getEndNodeIdFunction(@Nullable Neo4jPersistentEntity entity, boolean canUseElementId) { Function startNodeIdFunction; if (entity == null) { return Functions::elementId; } - if (!entity.isUsingDeprecatedInternalId() && entity.isUsingInternalIds()) { + if (!entity.isUsingDeprecatedInternalId() && canUseElementId) { startNodeIdFunction = Functions::elementId; } else { startNodeIdFunction = Functions::id; @@ -490,12 +490,12 @@ public enum CypherGenerator { return startNodeIdFunction; } - private static Function getRelationshipIdFunction(RelationshipDescription relationshipDescription) { + private static Function getRelationshipIdFunction(RelationshipDescription relationshipDescription, boolean canUseElementId) { - Function result = Functions::elementId; + Function result = canUseElementId ? Functions::elementId : Functions::id; if (relationshipDescription.hasRelationshipProperties()) { Neo4jPersistentEntity entity = (Neo4jPersistentEntity) relationshipDescription.getRelationshipPropertiesEntity(); - if (entity != null && entity.isUsingDeprecatedInternalId()) { + if ((entity != null && entity.isUsingDeprecatedInternalId()) || !canUseElementId) { result = Functions::id; } else { result = Functions::elementId; @@ -506,7 +506,7 @@ public enum CypherGenerator { @NonNull public Statement prepareSaveOfRelationships(Neo4jPersistentEntity neo4jPersistentEntity, - RelationshipDescription relationship, @Nullable String dynamicRelationshipType) { + RelationshipDescription relationship, @Nullable String dynamicRelationshipType, boolean canUseElementId) { final Node startNode = neo4jPersistentEntity.isUsingInternalIds() ? anyNode(START_NODE_NAME) @@ -525,9 +525,9 @@ public enum CypherGenerator { return Cypher.unwind(parameter(Constants.NAME_OF_RELATIONSHIP_LIST_PARAM)).as(row) .with(row) .match(startNode) - .where(getNodeIdFunction(neo4jPersistentEntity).apply(startNode).isEqualTo(idProperty)) + .where(getNodeIdFunction(neo4jPersistentEntity, canUseElementId).apply(startNode).isEqualTo(idProperty)) .match(endNode) - .where(getEndNodeIdFunction((Neo4jPersistentEntity) relationship.getTarget()).apply(endNode).isEqualTo(Cypher.property(row, Constants.TO_ID_PARAMETER_NAME))) + .where(getEndNodeIdFunction((Neo4jPersistentEntity) relationship.getTarget(), canUseElementId).apply(endNode).isEqualTo(Cypher.property(row, Constants.TO_ID_PARAMETER_NAME))) .merge(relationshipFragment) .returning(getReturnedIdExpressionsForRelationship(relationship, relationshipFragment)) .build(); @@ -537,7 +537,8 @@ public enum CypherGenerator { public Statement prepareSaveOfRelationshipWithProperties(Neo4jPersistentEntity neo4jPersistentEntity, RelationshipDescription relationship, boolean isNew, - @Nullable String dynamicRelationshipType) { + @Nullable String dynamicRelationshipType, + boolean canUseElementId) { Assert.isTrue(relationship.hasRelationshipProperties(), "Properties required to create a relationship with properties"); @@ -555,13 +556,13 @@ public enum CypherGenerator { startNode.relationshipFrom(endNode, type)) .named(RELATIONSHIP_NAME); - var nodeIdFunction = getNodeIdFunction(neo4jPersistentEntity); - var relationshipIdFunction = getRelationshipIdFunction(relationship); + var nodeIdFunction = getNodeIdFunction(neo4jPersistentEntity, canUseElementId); + var relationshipIdFunction = getRelationshipIdFunction(relationship, canUseElementId); StatementBuilder.OngoingReadingWithWhere startAndEndNodeMatch = match(startNode) .where(nodeIdFunction.apply(startNode).isEqualTo(idParameter)) .match(endNode) - .where(getEndNodeIdFunction((Neo4jPersistentEntity) relationship.getTarget()).apply(endNode).isEqualTo(parameter(Constants.TO_ID_PARAMETER_NAME))); + .where(getEndNodeIdFunction((Neo4jPersistentEntity) relationship.getTarget(), canUseElementId).apply(endNode).isEqualTo(parameter(Constants.TO_ID_PARAMETER_NAME))); StatementBuilder.ExposesSet createOrMatch = isNew ? startAndEndNodeMatch.create(relationshipFragment) @@ -575,7 +576,7 @@ public enum CypherGenerator { @NonNull public Statement prepareUpdateOfRelationshipsWithProperties(Neo4jPersistentEntity neo4jPersistentEntity, - RelationshipDescription relationship, boolean isNew) { + RelationshipDescription relationship, boolean isNew, boolean canUseElementId) { Assert.isTrue(relationship.hasRelationshipProperties(), "Properties required to create a relationship with properties"); @@ -598,8 +599,8 @@ public enum CypherGenerator { .as(row) .with(row); - var nodeIdFunction = getNodeIdFunction(neo4jPersistentEntity); - var relationshipIdFunction = getRelationshipIdFunction(relationship); + var nodeIdFunction = getNodeIdFunction(neo4jPersistentEntity, canUseElementId); + var relationshipIdFunction = getRelationshipIdFunction(relationship, canUseElementId); // we only need start and end node querying if we have to create a new relationship... if (isNew) { @@ -607,7 +608,7 @@ public enum CypherGenerator { .match(startNode) .where(nodeIdFunction.apply(startNode).isEqualTo(idProperty)) .match(endNode) - .where(getEndNodeIdFunction((Neo4jPersistentEntity) relationship.getTarget()).apply(endNode).isEqualTo(Cypher.property(row, Constants.TO_ID_PARAMETER_NAME))) + .where(getEndNodeIdFunction((Neo4jPersistentEntity) relationship.getTarget(), canUseElementId).apply(endNode).isEqualTo(Cypher.property(row, Constants.TO_ID_PARAMETER_NAME))) .create(relationshipFragment) .mutate(RELATIONSHIP_NAME, relationshipProperties) .returning(getReturnedIdExpressionsForRelationship(relationship, relationshipFragment)) @@ -632,7 +633,8 @@ public enum CypherGenerator { @NonNull public Statement prepareDeleteOf( Neo4jPersistentEntity neo4jPersistentEntity, - RelationshipDescription relationshipDescription + RelationshipDescription relationshipDescription, + boolean canUseElementId ) { final Node startNode = neo4jPersistentEntity.isUsingInternalIds() ? anyNode(START_NODE_NAME) : node(neo4jPersistentEntity.getPrimaryLabel(), neo4jPersistentEntity.getAdditionalLabels()) @@ -652,8 +654,8 @@ public enum CypherGenerator { Parameter idParameter = parameter(Constants.FROM_ID_PARAMETER_NAME); return match(relationship) - .where(getNodeIdFunction(neo4jPersistentEntity).apply(startNode).isEqualTo(idParameter)) - .and(getRelationshipIdFunction(relationshipDescription).apply(relationship).in(Cypher.parameter(Constants.NAME_OF_KNOWN_RELATIONSHIPS_PARAM)).not()) + .where(getNodeIdFunction(neo4jPersistentEntity, canUseElementId).apply(startNode).isEqualTo(idParameter)) + .and(getRelationshipIdFunction(relationshipDescription, canUseElementId).apply(relationship).in(Cypher.parameter(Constants.NAME_OF_KNOWN_RELATIONSHIPS_PARAM)).not()) .delete(relationship.getRequiredSymbolicName()) .build(); } diff --git a/src/main/java/org/springframework/data/neo4j/core/mapping/Neo4jMappingContext.java b/src/main/java/org/springframework/data/neo4j/core/mapping/Neo4jMappingContext.java index 3af5d1a86..b447b1c51 100644 --- a/src/main/java/org/springframework/data/neo4j/core/mapping/Neo4jMappingContext.java +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/Neo4jMappingContext.java @@ -538,29 +538,31 @@ public final class Neo4jMappingContext extends AbstractMappingContext neo4jPersistentEntity, RelationshipDescription relationshipDescription, - List plainRelationshipRows) { + List plainRelationshipRows, boolean canUseElementId) { return createStatementForSingleRelationship(neo4jPersistentEntity, (DefaultRelationshipDescription) relationshipDescription, - plainRelationshipRows); + plainRelationshipRows, canUseElementId); } public CreateRelationshipStatementHolder createStatementForImperativeRelationshipsWithPropertiesBatch(boolean isNew, Neo4jPersistentEntity neo4jPersistentEntity, RelationshipDescription relationshipDescription, Object relatedValues, - List> relationshipPropertiesRows) { + List> relationshipPropertiesRows, + boolean canUseElementId) { List relationshipPropertyValues = ((Collection) relatedValues).stream() .map(MappingSupport.RelationshipPropertiesWithEntityHolder.class::cast).collect(Collectors.toList()); return createStatementForRelationshipWithPropertiesBatch(isNew, neo4jPersistentEntity, relationshipDescription, - relationshipPropertyValues, relationshipPropertiesRows); + relationshipPropertyValues, relationshipPropertiesRows, canUseElementId); } public CreateRelationshipStatementHolder createStatementForSingleRelationship(Neo4jPersistentEntity neo4jPersistentEntity, RelationshipDescription relationshipContext, Object relatedValue, - boolean isNewRelationship) { + boolean isNewRelationship, + boolean canUseElementId) { if (relationshipContext.hasRelationshipProperties()) { MappingSupport.RelationshipPropertiesWithEntityHolder relatedValueEntityHolder = @@ -586,22 +588,22 @@ public final class Neo4jMappingContext extends AbstractMappingContext neo4jPersistentEntity, RelationshipDescription relationshipDescription, @Nullable String dynamicRelationshipType, - MappingSupport.RelationshipPropertiesWithEntityHolder relatedValue, boolean isNewRelationship) { + MappingSupport.RelationshipPropertiesWithEntityHolder relatedValue, boolean isNewRelationship, boolean canUseElementId) { Statement relationshipCreationQuery = CypherGenerator.INSTANCE.prepareSaveOfRelationshipWithProperties( neo4jPersistentEntity, relationshipDescription, isNewRelationship, - dynamicRelationshipType); + dynamicRelationshipType, canUseElementId); Map propMap = new HashMap<>(); // write relationship properties @@ -615,10 +617,11 @@ public final class Neo4jMappingContext extends AbstractMappingContext neo4jPersistentEntity, RelationshipDescription relationshipDescription, List relatedValues, - List> relationshipPropertiesRows) { + List> relationshipPropertiesRows, + boolean canUseElementId) { Statement relationshipCreationQuery = CypherGenerator.INSTANCE - .prepareUpdateOfRelationshipsWithProperties(neo4jPersistentEntity, relationshipDescription, isNew); + .prepareUpdateOfRelationshipsWithProperties(neo4jPersistentEntity, relationshipDescription, isNew, canUseElementId); List relationshipRows = new ArrayList<>(); Map relationshipPropertiesEntries = new HashMap<>(); if (isNew) { @@ -636,7 +639,7 @@ public final class Neo4jMappingContext extends AbstractMappingContext neo4jPersistentEntity, - DefaultRelationshipDescription relationshipDescription, Object relatedValue) { + DefaultRelationshipDescription relationshipDescription, Object relatedValue, boolean canUseElementId) { String relationshipType; if (!relationshipDescription.isDynamic()) { @@ -649,7 +652,7 @@ public final class Neo4jMappingContext extends AbstractMappingContext * @return True if the underlying domain classes uses {@code id()} to compute internally generated ids. */ default boolean isUsingDeprecatedInternalId() { + for (NodeDescription nodeDescription : getChildNodeDescriptionsInHierarchy()) { + if (nodeDescription.isUsingInternalIds() && ((Neo4jPersistentEntity) nodeDescription).getIdProperty() != null + && Neo4jPersistentEntity.DEPRECATED_GENERATED_ID_TYPES.contains(((Neo4jPersistentEntity) nodeDescription).getIdProperty().getType())) { + return true; + } + } return isUsingInternalIds() && Neo4jPersistentEntity.DEPRECATED_GENERATED_ID_TYPES.contains(getRequiredIdProperty().getType()); } } diff --git a/src/test/java/org/springframework/data/neo4j/core/mapping/CypherGeneratorTest.java b/src/test/java/org/springframework/data/neo4j/core/mapping/CypherGeneratorTest.java index 766f29ff1..55b116039 100644 --- a/src/test/java/org/springframework/data/neo4j/core/mapping/CypherGeneratorTest.java +++ b/src/test/java/org/springframework/data/neo4j/core/mapping/CypherGeneratorTest.java @@ -56,7 +56,7 @@ class CypherGeneratorTest { when(relationshipDescription.isDynamic()).thenReturn(true); Statement statement = CypherGenerator.INSTANCE.prepareSaveOfRelationship(persistentEntity, - relationshipDescription, "REL"); + relationshipDescription, "REL", true); String expectedQuery = "MATCH (startNode:`Entity1`) WHERE startNode.id = $fromId MATCH (endNode)" + " WHERE elementId(endNode) = $toId MERGE (startNode)<-[relProps:`REL`]-(endNode) RETURN elementId(relProps) AS __elementId__"; @@ -71,7 +71,7 @@ class CypherGeneratorTest { when(relationshipDescription.isDynamic()).thenReturn(true); Statement statement = CypherGenerator.INSTANCE.prepareSaveOfRelationship(persistentEntity, - relationshipDescription, "REL"); + relationshipDescription, "REL", true); String expectedQuery = "MATCH (startNode:`Entity1`:`MultipleLabel`) WHERE startNode.id = $fromId MATCH (endNode)" @@ -92,7 +92,7 @@ class CypherGeneratorTest { when(persistentEntity.isUsingDeprecatedInternalId()).thenReturn(true); Statement statement = CypherGenerator.INSTANCE.prepareSaveOfRelationship(persistentEntity, - relationshipDescription, "REL"); + relationshipDescription, "REL", true); String expectedQuery = "MATCH (startNode) WHERE id(startNode) = $fromId MATCH (endNode)" + " WHERE elementId(endNode) = $toId MERGE (startNode)<-[relProps:`REL`]-(endNode) RETURN elementId(relProps) AS __elementId__"; @@ -106,7 +106,7 @@ class CypherGeneratorTest { RelationshipDescription relationshipDescription = Mockito.mock(RelationshipDescription.class); doReturn(relatedEntity).when(relationshipDescription).getTarget(); - Statement statement = CypherGenerator.INSTANCE.prepareDeleteOf(persistentEntity, relationshipDescription); + Statement statement = CypherGenerator.INSTANCE.prepareDeleteOf(persistentEntity, relationshipDescription, true); String expectedQuery = "MATCH (startNode:`Entity1`)<-[rel]-(:`Entity2`) WHERE (startNode.id = $fromId AND NOT (elementId(rel) IN $__knownRelationShipIds__)) DELETE rel"; Assertions.assertEquals(expectedQuery, Renderer.getRenderer(Configuration.newConfig().withDialect(Dialect.NEO4J_5).build()).render(statement)); @@ -121,7 +121,7 @@ class CypherGeneratorTest { RelationshipDescription relationshipDescription = Mockito.mock(RelationshipDescription.class); doReturn(relatedEntity).when(relationshipDescription).getTarget(); - Statement statement = CypherGenerator.INSTANCE.prepareDeleteOf(persistentEntity, relationshipDescription); + Statement statement = CypherGenerator.INSTANCE.prepareDeleteOf(persistentEntity, relationshipDescription, true); String expectedQuery = "MATCH (startNode:`Entity1`:`MultipleLabel`)<-[rel]-(:`Entity2`:`MultipleLabel`) WHERE (startNode.id = $fromId AND NOT (elementId(rel) IN $__knownRelationShipIds__)) DELETE rel"; Assertions.assertEquals(expectedQuery, Renderer.getRenderer(Configuration.newConfig().withDialect(Dialect.NEO4J_5).build()).render(statement)); @@ -143,7 +143,7 @@ class CypherGeneratorTest { when(persistentEntity.getRequiredIdProperty()).thenReturn(persistentProperty); when(persistentEntity.isUsingDeprecatedInternalId()).thenReturn(true); - Statement statement = CypherGenerator.INSTANCE.prepareDeleteOf(persistentEntity, relationshipDescription); + Statement statement = CypherGenerator.INSTANCE.prepareDeleteOf(persistentEntity, relationshipDescription, true); String expectedQuery = "MATCH (startNode)<-[rel]-(:`Entity2`) WHERE (id(startNode) = $fromId AND NOT (elementId(rel) IN $__knownRelationShipIds__)) DELETE rel"; Assertions.assertEquals(expectedQuery, Renderer.getRenderer(Configuration.newConfig().withDialect(Dialect.NEO4J_5).build()).render(statement));