GH-2750 - Avoid toString in relationship creation.

Without the string conversion Neo4j will either to an (element)id or
label seek instead of a full node scan.
Also: In some corner case adding labels to the generic cyclic query
can improve the performance of the query.
On the other hand it cannot make it worse.

Closes #2750
This commit is contained in:
Gerrit Meier
2023-06-22 14:57:16 +02:00
parent e465b75138
commit 804a954a1a
5 changed files with 62 additions and 40 deletions

View File

@@ -409,7 +409,12 @@ public final class Neo4jTemplate implements
throw new IllegalStateException("Could not retrieve an internal id while saving"); throw new IllegalStateException("Could not retrieve an internal id while saving");
} }
String elementId = newOrUpdatedNode.map(IdentitySupport::getElementId).get(); Object elementId = newOrUpdatedNode.map(node -> {
if (!entityMetaData.isUsingDeprecatedInternalId() && entityMetaData.isUsingInternalIds()) {
return IdentitySupport.getElementId(node);
}
return node.id();
}).get();
PersistentPropertyAccessor<T> propertyAccessor = entityMetaData.getPropertyAccessor(entityToBeSaved); PersistentPropertyAccessor<T> propertyAccessor = entityMetaData.getPropertyAccessor(entityToBeSaved);
TemplateSupport.setGeneratedIdIfNecessary(entityMetaData, propertyAccessor, elementId, newOrUpdatedNode); TemplateSupport.setGeneratedIdIfNecessary(entityMetaData, propertyAccessor, elementId, newOrUpdatedNode);
@@ -807,14 +812,14 @@ public final class Neo4jTemplate implements
? stateMachine.getProcessedAs(relatedObjectBeforeCallbacksApplied) ? stateMachine.getProcessedAs(relatedObjectBeforeCallbacksApplied)
: eventSupport.maybeCallBeforeBind(relatedObjectBeforeCallbacksApplied); : eventSupport.maybeCallBeforeBind(relatedObjectBeforeCallbacksApplied);
String relatedInternalId; Object relatedInternalId;
Entity savedEntity = null; Entity savedEntity = null;
// No need to save values if processed // No need to save values if processed
if (stateMachine.hasProcessedValue(relatedValueToStore)) { if (stateMachine.hasProcessedValue(relatedValueToStore)) {
relatedInternalId = stateMachine.getObjectId(relatedValueToStore); relatedInternalId = stateMachine.getObjectId(relatedValueToStore);
} else { } else {
savedEntity = saveRelatedNode(newRelatedObject, targetEntity, includeProperty, currentPropertyPath); savedEntity = saveRelatedNode(newRelatedObject, targetEntity, includeProperty, currentPropertyPath);
relatedInternalId = TemplateSupport.rendererCanUseElementIdIfPresent(renderer) ? savedEntity.elementId() : Long.toString(savedEntity.id()); relatedInternalId = TemplateSupport.rendererCanUseElementIdIfPresent(renderer, targetEntity) ? savedEntity.elementId() : savedEntity.id();
stateMachine.markEntityAsProcessed(relatedValueToStore, relatedInternalId); stateMachine.markEntityAsProcessed(relatedValueToStore, relatedInternalId);
if (relatedValueToStore instanceof MappingSupport.RelationshipPropertiesWithEntityHolder) { if (relatedValueToStore instanceof MappingSupport.RelationshipPropertiesWithEntityHolder) {
Object entity = ((MappingSupport.RelationshipPropertiesWithEntityHolder) relatedValueToStore).getRelatedEntity(); Object entity = ((MappingSupport.RelationshipPropertiesWithEntityHolder) relatedValueToStore).getRelatedEntity();

View File

@@ -447,7 +447,9 @@ public final class ReactiveNeo4jTemplate implements
PersistentPropertyAccessor<T> propertyAccessor = entityMetaData.getPropertyAccessor(entityToBeSaved); PersistentPropertyAccessor<T> propertyAccessor = entityMetaData.getPropertyAccessor(entityToBeSaved);
return idMono.doOnNext(newOrUpdatedNode -> { return idMono.doOnNext(newOrUpdatedNode -> {
var elementId = IdentitySupport.getElementId(newOrUpdatedNode); var elementId = !entityMetaData.isUsingDeprecatedInternalId() && entityMetaData.isUsingInternalIds()
? IdentitySupport.getElementId(newOrUpdatedNode)
: newOrUpdatedNode.id();
TemplateSupport.setGeneratedIdIfNecessary(entityMetaData, propertyAccessor, elementId, Optional.of(newOrUpdatedNode)); TemplateSupport.setGeneratedIdIfNecessary(entityMetaData, propertyAccessor, elementId, Optional.of(newOrUpdatedNode));
TemplateSupport.updateVersionPropertyIfPossible(entityMetaData, propertyAccessor, newOrUpdatedNode); TemplateSupport.updateVersionPropertyIfPossible(entityMetaData, propertyAccessor, newOrUpdatedNode);
finalStateMachine.markEntityAsProcessed(instance, elementId); finalStateMachine.markEntityAsProcessed(instance, elementId);
@@ -942,17 +944,17 @@ public final class ReactiveNeo4jTemplate implements
.flatMap(newRelatedObject -> { .flatMap(newRelatedObject -> {
Neo4jPersistentEntity<?> targetEntity = neo4jMappingContext.getRequiredPersistentEntity(relatedObjectBeforeCallbacksApplied.getClass()); Neo4jPersistentEntity<?> targetEntity = neo4jMappingContext.getRequiredPersistentEntity(relatedObjectBeforeCallbacksApplied.getClass());
Mono<Tuple2<AtomicReference<String>, AtomicReference<Entity>>> queryOrSave; Mono<Tuple2<AtomicReference<Object>, AtomicReference<Entity>>> queryOrSave;
if (stateMachine.hasProcessedValue(relatedValueToStore)) { if (stateMachine.hasProcessedValue(relatedValueToStore)) {
AtomicReference<String> relatedInternalId = new AtomicReference<>(); AtomicReference<Object> relatedInternalId = new AtomicReference<>();
String possibleValue = stateMachine.getObjectId(relatedValueToStore); Object possibleValue = stateMachine.getObjectId(relatedValueToStore);
if (possibleValue != null) { if (possibleValue != null) {
relatedInternalId.set(possibleValue); relatedInternalId.set(possibleValue);
} }
queryOrSave = Mono.just(Tuples.of(relatedInternalId, new AtomicReference<>())); queryOrSave = Mono.just(Tuples.of(relatedInternalId, new AtomicReference<>()));
} else { } else {
queryOrSave = saveRelatedNode(newRelatedObject, targetEntity, includeProperty, currentPropertyPath) queryOrSave = saveRelatedNode(newRelatedObject, targetEntity, includeProperty, currentPropertyPath)
.map(entity -> Tuples.of(new AtomicReference<>(TemplateSupport.rendererCanUseElementIdIfPresent(renderer) ? entity.elementId() : Long.toString(entity.id())), new AtomicReference<>(entity))) .map(entity -> Tuples.of(new AtomicReference<>((Object) (TemplateSupport.rendererCanUseElementIdIfPresent(renderer, targetEntity) ? entity.elementId() : entity.id())), new AtomicReference<>(entity)))
.doOnNext(t -> { .doOnNext(t -> {
var relatedInternalId = t.getT1().get(); var relatedInternalId = t.getT1().get();
stateMachine.markEntityAsProcessed(relatedValueToStore, relatedInternalId); stateMachine.markEntityAsProcessed(relatedValueToStore, relatedInternalId);
@@ -963,7 +965,7 @@ public final class ReactiveNeo4jTemplate implements
}); });
} }
return queryOrSave.flatMap(idAndEntity -> { return queryOrSave.flatMap(idAndEntity -> {
String relatedInternalId = idAndEntity.getT1().get(); Object relatedInternalId = idAndEntity.getT1().get();
Entity savedEntity = idAndEntity.getT2().get(); Entity savedEntity = idAndEntity.getT2().get();
Neo4jPersistentProperty requiredIdProperty = targetEntity.getRequiredIdProperty(); Neo4jPersistentProperty requiredIdProperty = targetEntity.getRequiredIdProperty();
PersistentPropertyAccessor<?> targetPropertyAccessor = targetEntity.getPropertyAccessor(newRelatedObject); PersistentPropertyAccessor<?> targetPropertyAccessor = targetEntity.getPropertyAccessor(newRelatedObject);

View File

@@ -204,28 +204,26 @@ public final class TemplateSupport {
Statement toStatement(NodeDescription<?> nodeDescription) { Statement toStatement(NodeDescription<?> nodeDescription) {
String rootNodeIds = "rootNodeIds"; String primaryLabel = nodeDescription.getPrimaryLabel();
String relationshipIds = "relationshipIds"; Node rootNodes = Cypher.node(primaryLabel).named(ROOT_NODE_IDS);
String relatedNodeIds = "relatedNodeIds"; Node relatedNodes = Cypher.anyNode(RELATED_NODE_IDS);
Node rootNodes = Cypher.anyNode(rootNodeIds); Relationship relationships = Cypher.anyNode().relationshipBetween(Cypher.anyNode()).named(RELATIONSHIP_IDS);
Node relatedNodes = Cypher.anyNode(relatedNodeIds);
Relationship relationships = Cypher.anyNode().relationshipBetween(Cypher.anyNode()).named(relationshipIds);
return Cypher.match(rootNodes) return Cypher.match(rootNodes)
.where(Functions.elementId(rootNodes).in(Cypher.parameter(rootNodeIds))) .where(Functions.elementId(rootNodes).in(Cypher.parameter(ROOT_NODE_IDS)))
.with(Functions.collect(rootNodes).as(Constants.NAME_OF_ROOT_NODE)) .with(Functions.collect(rootNodes).as(Constants.NAME_OF_ROOT_NODE))
.optionalMatch(relationships) .optionalMatch(relationships)
.where(Functions.elementId(relationships).in(Cypher.parameter(relationshipIds))) .where(Functions.elementId(relationships).in(Cypher.parameter(RELATIONSHIP_IDS)))
.with(Constants.NAME_OF_ROOT_NODE, Functions.collectDistinct(relationships).as(Constants.NAME_OF_SYNTHESIZED_RELATIONS)) .with(Constants.NAME_OF_ROOT_NODE, Functions.collectDistinct(relationships).as(Constants.NAME_OF_SYNTHESIZED_RELATIONS))
.optionalMatch(relatedNodes) .optionalMatch(relatedNodes)
.where(Functions.elementId(relatedNodes).in(Cypher.parameter(relatedNodeIds))) .where(Functions.elementId(relatedNodes).in(Cypher.parameter(RELATED_NODE_IDS)))
.with( .with(
Constants.NAME_OF_ROOT_NODE, Constants.NAME_OF_ROOT_NODE,
Cypher.name(Constants.NAME_OF_SYNTHESIZED_RELATIONS).as(Constants.NAME_OF_SYNTHESIZED_RELATIONS), Cypher.name(Constants.NAME_OF_SYNTHESIZED_RELATIONS).as(Constants.NAME_OF_SYNTHESIZED_RELATIONS),
Functions.collectDistinct(relatedNodes).as(Constants.NAME_OF_SYNTHESIZED_RELATED_NODES) Functions.collectDistinct(relatedNodes).as(Constants.NAME_OF_SYNTHESIZED_RELATED_NODES)
) )
.unwind(Constants.NAME_OF_ROOT_NODE).as(rootNodeIds) .unwind(Constants.NAME_OF_ROOT_NODE).as(ROOT_NODE_IDS)
.with( .with(
Cypher.name(rootNodeIds).as(Constants.NAME_OF_TYPED_ROOT_NODE.apply(nodeDescription).getValue()), Cypher.name(ROOT_NODE_IDS).as(Constants.NAME_OF_TYPED_ROOT_NODE.apply(nodeDescription).getValue()),
Cypher.name(Constants.NAME_OF_SYNTHESIZED_RELATIONS), Cypher.name(Constants.NAME_OF_SYNTHESIZED_RELATIONS),
Cypher.name(Constants.NAME_OF_SYNTHESIZED_RELATED_NODES)) Cypher.name(Constants.NAME_OF_SYNTHESIZED_RELATED_NODES))
.orderBy(queryFragments.getOrderBy()) .orderBy(queryFragments.getOrderBy())
@@ -353,7 +351,7 @@ public final class TemplateSupport {
static <T> void setGeneratedIdIfNecessary( static <T> void setGeneratedIdIfNecessary(
Neo4jPersistentEntity<?> entityMetaData, Neo4jPersistentEntity<?> entityMetaData,
PersistentPropertyAccessor<T> propertyAccessor, PersistentPropertyAccessor<T> propertyAccessor,
String elementId, Object elementId,
Optional<Entity> databaseEntity Optional<Entity> databaseEntity
) { ) {
if (!entityMetaData.isUsingInternalIds()) { if (!entityMetaData.isUsingInternalIds()) {
@@ -381,11 +379,11 @@ public final class TemplateSupport {
* @param <T> The type of the entity * @param <T> The type of the entity
* @return The actual related internal id being used. * @return The actual related internal id being used.
*/ */
static <T> String retrieveOrSetRelatedId( static <T> Object retrieveOrSetRelatedId(
Neo4jPersistentEntity<?> entityMetadata, Neo4jPersistentEntity<?> entityMetadata,
PersistentPropertyAccessor<T> propertyAccessor, PersistentPropertyAccessor<T> propertyAccessor,
Optional<Entity> databaseEntity, Optional<Entity> databaseEntity,
@Nullable String relatedInternalId @Nullable Object relatedInternalId
) { ) {
if (!entityMetadata.isUsingInternalIds()) { if (!entityMetadata.isUsingInternalIds()) {
return Objects.requireNonNull(relatedInternalId); return Objects.requireNonNull(relatedInternalId);
@@ -403,7 +401,7 @@ public final class TemplateSupport {
} }
} else { } else {
if (relatedInternalId == null && current != null) { if (relatedInternalId == null && current != null) {
relatedInternalId = (String) current; relatedInternalId = current;
} else if (current == null) { } else if (current == null) {
propertyAccessor.setProperty(requiredIdProperty, relatedInternalId); propertyAccessor.setProperty(requiredIdProperty, relatedInternalId);
} }
@@ -415,8 +413,8 @@ public final class TemplateSupport {
* Checks if the renderer is configured in such a way that it will use element id or apply toString(id(n)) workaround. * Checks if the renderer is configured in such a way that it will use element id or apply toString(id(n)) workaround.
* @return {@literal true} if renderer will use elementId * @return {@literal true} if renderer will use elementId
*/ */
static boolean rendererCanUseElementIdIfPresent(Renderer renderer) { static boolean rendererCanUseElementIdIfPresent(Renderer renderer, Neo4jPersistentEntity<?> targetEntity) {
return renderer.render(Cypher.returning(Functions.elementId(Cypher.anyNode("n"))).build()) return !targetEntity.isUsingDeprecatedInternalId() && targetEntity.isUsingInternalIds() && renderer.render(Cypher.returning(Functions.elementId(Cypher.anyNode("n"))).build())
.equals("RETURN elementId(n)"); .equals("RETURN elementId(n)");
} }

View File

@@ -430,13 +430,14 @@ public enum CypherGenerator {
var startNodeIdFunction = getNodeIdFunction(neo4jPersistentEntity); var startNodeIdFunction = getNodeIdFunction(neo4jPersistentEntity);
return match(startNode) return match(startNode)
.where(startNodeIdFunction.apply(startNode).isEqualTo(idParameter)) .where(startNodeIdFunction.apply(startNode).isEqualTo(idParameter))
.match(endNode).where(Functions.elementId(endNode).isEqualTo(parameter(Constants.TO_ID_PARAMETER_NAME))) .match(endNode)
.where(getEndNodeIdFunction((Neo4jPersistentEntity<?>) relationship.getTarget()).apply(endNode).isEqualTo(parameter(Constants.TO_ID_PARAMETER_NAME)))
.merge(relationshipFragment) .merge(relationshipFragment)
.returning(getReturnedIdExpressionsForRelationship(relationship, relationshipFragment)) .returning(getReturnedIdExpressionsForRelationship(relationship, relationshipFragment))
.build(); .build();
} }
private static Function<Node, Expression> getNodeIdFunction(Neo4jPersistentEntity<?> entity) { private static Function<Node, Expression> getNodeIdFunction(@Nullable Neo4jPersistentEntity<?> entity) {
Function<Node, Expression> startNodeIdFunction; Function<Node, Expression> startNodeIdFunction;
var idProperty = entity.getRequiredIdProperty(); var idProperty = entity.getRequiredIdProperty();
@@ -452,6 +453,20 @@ public enum CypherGenerator {
return startNodeIdFunction; return startNodeIdFunction;
} }
private static Function<Node, Expression> getEndNodeIdFunction(@Nullable Neo4jPersistentEntity<?> entity) {
Function<Node, Expression> startNodeIdFunction;
if (entity == null) {
return Functions::elementId;
}
if (!entity.isUsingDeprecatedInternalId() && entity.isUsingInternalIds()) {
startNodeIdFunction = Functions::elementId;
} else {
startNodeIdFunction = Functions::id;
}
return startNodeIdFunction;
}
private static Function<Relationship, Expression> getRelationshipIdFunction(RelationshipDescription relationshipDescription) { private static Function<Relationship, Expression> getRelationshipIdFunction(RelationshipDescription relationshipDescription) {
Function<Relationship, Expression> result = Functions::elementId; Function<Relationship, Expression> result = Functions::elementId;
@@ -488,7 +503,8 @@ public enum CypherGenerator {
.with(row) .with(row)
.match(startNode) .match(startNode)
.where(getNodeIdFunction(neo4jPersistentEntity).apply(startNode).isEqualTo(idProperty)) .where(getNodeIdFunction(neo4jPersistentEntity).apply(startNode).isEqualTo(idProperty))
.match(endNode).where(Functions.elementId(endNode).isEqualTo(Cypher.property(row, Constants.TO_ID_PARAMETER_NAME))) .match(endNode)
.where(getEndNodeIdFunction((Neo4jPersistentEntity<?>) relationship.getTarget()).apply(endNode).isEqualTo(Cypher.property(row, Constants.TO_ID_PARAMETER_NAME)))
.merge(relationshipFragment) .merge(relationshipFragment)
.returning(getReturnedIdExpressionsForRelationship(relationship, relationshipFragment)) .returning(getReturnedIdExpressionsForRelationship(relationship, relationshipFragment))
.build(); .build();
@@ -521,7 +537,8 @@ public enum CypherGenerator {
StatementBuilder.OngoingReadingWithWhere startAndEndNodeMatch = match(startNode) StatementBuilder.OngoingReadingWithWhere startAndEndNodeMatch = match(startNode)
.where(nodeIdFunction.apply(startNode).isEqualTo(idParameter)) .where(nodeIdFunction.apply(startNode).isEqualTo(idParameter))
.match(endNode).where(Functions.elementId(endNode).isEqualTo(parameter(Constants.TO_ID_PARAMETER_NAME))); .match(endNode)
.where(getEndNodeIdFunction((Neo4jPersistentEntity<?>) relationship.getTarget()).apply(endNode).isEqualTo(parameter(Constants.TO_ID_PARAMETER_NAME)));
StatementBuilder.ExposesSet createOrMatch = isNew StatementBuilder.ExposesSet createOrMatch = isNew
? startAndEndNodeMatch.create(relationshipFragment) ? startAndEndNodeMatch.create(relationshipFragment)
@@ -567,7 +584,7 @@ public enum CypherGenerator {
.match(startNode) .match(startNode)
.where(nodeIdFunction.apply(startNode).isEqualTo(idProperty)) .where(nodeIdFunction.apply(startNode).isEqualTo(idProperty))
.match(endNode) .match(endNode)
.where(endNode.elementId().isEqualTo(Cypher.property(row, Constants.TO_ID_PARAMETER_NAME))) .where(getEndNodeIdFunction((Neo4jPersistentEntity<?>) relationship.getTarget()).apply(endNode).isEqualTo(Cypher.property(row, Constants.TO_ID_PARAMETER_NAME)))
.create(relationshipFragment) .create(relationshipFragment)
.mutate(RELATIONSHIP_NAME, relationshipProperties) .mutate(RELATIONSHIP_NAME, relationshipProperties)
.returning(getReturnedIdExpressionsForRelationship(relationship, relationshipFragment)) .returning(getReturnedIdExpressionsForRelationship(relationship, relationshipFragment))

View File

@@ -64,7 +64,7 @@ public final class NestedRelationshipProcessingStateMachine {
* A map pointing from a processed object to the internal id. * A map pointing from a processed object to the internal id.
* This will be useful during the persistence to avoid another DB network round-trip. * This will be useful during the persistence to avoid another DB network round-trip.
*/ */
private final Map<Integer, String> processedObjectsIds = new HashMap<>(); private final Map<Integer, Object> processedObjectsIds = new HashMap<>();
public NestedRelationshipProcessingStateMachine(final Neo4jMappingContext mappingContext) { public NestedRelationshipProcessingStateMachine(final Neo4jMappingContext mappingContext) {
@@ -73,7 +73,7 @@ public final class NestedRelationshipProcessingStateMachine {
this.mappingContext = mappingContext; this.mappingContext = mappingContext;
} }
public NestedRelationshipProcessingStateMachine(final Neo4jMappingContext mappingContext, Object initialObject, String elementId) { public NestedRelationshipProcessingStateMachine(final Neo4jMappingContext mappingContext, Object initialObject, Object elementId) {
this(mappingContext); this(mappingContext);
Assert.notNull(initialObject, "Initial object must not be null"); Assert.notNull(initialObject, "Initial object must not be null");
@@ -143,7 +143,7 @@ public final class NestedRelationshipProcessingStateMachine {
* @param valueToStore If not {@literal null}, all non-null values will be marked as processed * @param valueToStore If not {@literal null}, all non-null values will be marked as processed
* @param elementId The internal id of the value processed * @param elementId The internal id of the value processed
*/ */
public void markEntityAsProcessed(Object valueToStore, String elementId) { public void markEntityAsProcessed(Object valueToStore, Object elementId) {
final long stamp = lock.writeLock(); final long stamp = lock.writeLock();
try { try {
@@ -154,7 +154,7 @@ public final class NestedRelationshipProcessingStateMachine {
} }
} }
private void doMarkValueAsProcessed(Object valueToStore, String elementId) { private void doMarkValueAsProcessed(Object valueToStore, Object elementId) {
Object value = extractRelatedValueFromRelationshipProperties(valueToStore); Object value = extractRelatedValueFromRelationshipProperties(valueToStore);
storeHashedVersionInProcessedObjectsIds(valueToStore, elementId); storeHashedVersionInProcessedObjectsIds(valueToStore, elementId);
@@ -191,7 +191,7 @@ public final class NestedRelationshipProcessingStateMachine {
.findAny(); .findAny();
if (alreadyProcessedObject.isPresent()) { // Skip the show the next time around. if (alreadyProcessedObject.isPresent()) { // Skip the show the next time around.
processed = true; processed = true;
String internalId = getObjectId(alreadyProcessedObject.get()); Object internalId = getObjectId(alreadyProcessedObject.get());
if (internalId != null) { if (internalId != null) {
stamp = lock.tryConvertToWriteLock(stamp); stamp = lock.tryConvertToWriteLock(stamp);
doMarkValueAsProcessed(valueToCheck, internalId); doMarkValueAsProcessed(valueToCheck, internalId);
@@ -239,11 +239,11 @@ public final class NestedRelationshipProcessingStateMachine {
* @return The objects id * @return The objects id
*/ */
@Nullable @Nullable
public String getObjectId(Object object) { public Object getObjectId(Object object) {
final long stamp = lock.readLock(); final long stamp = lock.readLock();
try { try {
Object valueToCheck = extractRelatedValueFromRelationshipProperties(object); Object valueToCheck = extractRelatedValueFromRelationshipProperties(object);
String possibleId = getProcessedObjectIds(valueToCheck); Object possibleId = getProcessedObjectIds(valueToCheck);
return possibleId != null ? possibleId : getProcessedObjectIds(getProcessedAs(valueToCheck)); return possibleId != null ? possibleId : getProcessedObjectIds(getProcessedAs(valueToCheck));
} finally { } finally {
lock.unlock(stamp); lock.unlock(stamp);
@@ -261,7 +261,7 @@ public final class NestedRelationshipProcessingStateMachine {
} }
@Nullable @Nullable
private String getProcessedObjectIds(@Nullable Object entity) { private Object getProcessedObjectIds(@Nullable Object entity) {
if (entity == null) { if (entity == null) {
return null; return null;
} }
@@ -282,7 +282,7 @@ public final class NestedRelationshipProcessingStateMachine {
/* /*
* Convenience wrapper functions to avoid exposing the System.identityHashCode "everywhere" in this class. * Convenience wrapper functions to avoid exposing the System.identityHashCode "everywhere" in this class.
*/ */
private void storeHashedVersionInProcessedObjectsIds(Object initialObject, String elementId) { private void storeHashedVersionInProcessedObjectsIds(Object initialObject, Object elementId) {
processedObjectsIds.put(System.identityHashCode(initialObject), elementId); processedObjectsIds.put(System.identityHashCode(initialObject), elementId);
} }