@@ -341,15 +341,21 @@ private <T> T saveImpl(T instance, @Nullable List<PropertyDescriptor> includedPr
341
341
.with (binderFunction )
342
342
.fetchAs (Long .class ).one ();
343
343
344
- if (entityMetaData .hasVersionProperty () && !optionalInternalId .isPresent ()) {
345
- throw new OptimisticLockingFailureException (OPTIMISTIC_LOCKING_ERROR_MESSAGE );
344
+ if (!optionalInternalId .isPresent ()) {
345
+ if (entityMetaData .hasVersionProperty ()) {
346
+ throw new OptimisticLockingFailureException (OPTIMISTIC_LOCKING_ERROR_MESSAGE );
347
+ }
348
+ // defensive exception throwing
349
+ throw new IllegalStateException ("Could not retrieve an internal id while saving." );
346
350
}
347
351
352
+ Long internalId = optionalInternalId .get ();
353
+
348
354
PersistentPropertyAccessor <T > propertyAccessor = entityMetaData .getPropertyAccessor (entityToBeSaved );
349
355
if (entityMetaData .isUsingInternalIds ()) {
350
- propertyAccessor .setProperty (entityMetaData .getRequiredIdProperty (), optionalInternalId . get () );
356
+ propertyAccessor .setProperty (entityMetaData .getRequiredIdProperty (), internalId );
351
357
}
352
- processRelations (entityMetaData , instance , propertyAccessor , isEntityNew , includeProperty );
358
+ processRelations (entityMetaData , instance , internalId , propertyAccessor , isEntityNew , includeProperty );
353
359
354
360
return propertyAccessor .getBean ();
355
361
}
@@ -581,6 +587,14 @@ private <T> ExecutableQuery<T> createExecutableQuery(Class<T> domainType, String
581
587
* @param isParentObjectNew A flag if the parent was new
582
588
* @param includeProperty A predicate telling to include a relationship property or not
583
589
*/
590
+ private <T > T processRelations (Neo4jPersistentEntity <?> neo4jPersistentEntity , T originalInstance , Long internalId ,
591
+ PersistentPropertyAccessor <?> parentPropertyAccessor ,
592
+ boolean isParentObjectNew , Predicate <String > includeProperty ) {
593
+
594
+ return processNestedRelations (neo4jPersistentEntity , parentPropertyAccessor , isParentObjectNew ,
595
+ new NestedRelationshipProcessingStateMachine (originalInstance , internalId ), includeProperty );
596
+ }
597
+
584
598
private <T > T processRelations (Neo4jPersistentEntity <?> neo4jPersistentEntity , T originalInstance ,
585
599
PersistentPropertyAccessor <?> parentPropertyAccessor ,
586
600
boolean isParentObjectNew , Predicate <String > includeProperty ) {
@@ -666,22 +680,23 @@ private <T> T processNestedRelations(Neo4jPersistentEntity<?> sourceEntity, Pers
666
680
for (Object relatedValueToStore : relatedValuesToStore ) {
667
681
668
682
// here a map entry is not always anymore a dynamic association
669
- Object relatedObjectBeforeCallbacks = relationshipContext .identifyAndExtractRelationshipTargetNode (relatedValueToStore );
670
- Neo4jPersistentEntity <?> targetEntity = neo4jMappingContext .getPersistentEntity (relatedObjectBeforeCallbacks .getClass ());
683
+ Object relatedObjectBeforeCallbacksApplied = relationshipContext .identifyAndExtractRelationshipTargetNode (relatedValueToStore );
684
+ Neo4jPersistentEntity <?> targetEntity = neo4jMappingContext .getPersistentEntity (relatedObjectBeforeCallbacksApplied .getClass ());
671
685
672
- boolean isEntityNew = targetEntity .isNew (relatedObjectBeforeCallbacks );
686
+ boolean isEntityNew = targetEntity .isNew (relatedObjectBeforeCallbacksApplied );
673
687
674
- Object newRelatedObject = eventSupport .maybeCallBeforeBind (relatedObjectBeforeCallbacks );
688
+ Object newRelatedObject = stateMachine .hasProcessedValue (relatedObjectBeforeCallbacksApplied )
689
+ ? stateMachine .getProcessedAs (relatedObjectBeforeCallbacksApplied )
690
+ : eventSupport .maybeCallBeforeBind (relatedObjectBeforeCallbacksApplied );
675
691
676
692
Long relatedInternalId ;
677
693
// No need to save values if processed
678
694
if (stateMachine .hasProcessedValue (relatedValueToStore )) {
679
- Object newRelatedObjectForQuery = stateMachine .getProcessedAs (newRelatedObject );
680
- relatedInternalId = queryRelatedNode (newRelatedObjectForQuery , targetEntity );
695
+ relatedInternalId = stateMachine .getInternalId (relatedObjectBeforeCallbacksApplied );
681
696
} else {
682
697
relatedInternalId = saveRelatedNode (newRelatedObject , targetEntity );
683
698
}
684
- stateMachine .markValueAsProcessed (relatedValueToStore );
699
+ stateMachine .markValueAsProcessed (relatedValueToStore , relatedInternalId );
685
700
686
701
Object idValue = idProperty != null
687
702
? relationshipContext
@@ -713,8 +728,8 @@ private <T> T processNestedRelations(Neo4jPersistentEntity<?> sourceEntity, Pers
713
728
// if an internal id is used this must be set to link this entity in the next iteration
714
729
if (targetEntity .isUsingInternalIds ()) {
715
730
targetPropertyAccessor .setProperty (targetEntity .getRequiredIdProperty (), relatedInternalId );
716
- stateMachine .markValueAsProcessedAs (newRelatedObject , targetPropertyAccessor .getBean ());
717
731
}
732
+ stateMachine .markValueAsProcessedAs (relatedObjectBeforeCallbacksApplied , targetPropertyAccessor .getBean ());
718
733
719
734
if (processState != ProcessState .PROCESSED_ALL_VALUES ) {
720
735
processNestedRelations (targetEntity , targetPropertyAccessor , isEntityNew , stateMachine , s -> true );
@@ -726,7 +741,7 @@ private <T> T processNestedRelations(Neo4jPersistentEntity<?> sourceEntity, Pers
726
741
relatedValueToStore ,
727
742
targetPropertyAccessor );
728
743
729
- relationshipHandler .handle (relatedValueToStore , relatedObjectBeforeCallbacks , potentiallyRecreatedNewRelatedObject );
744
+ relationshipHandler .handle (relatedValueToStore , relatedObjectBeforeCallbacksApplied , potentiallyRecreatedNewRelatedObject );
730
745
}
731
746
732
747
relationshipHandler .applyFinalResultToOwner (propertyAccessor );
@@ -735,22 +750,6 @@ private <T> T processNestedRelations(Neo4jPersistentEntity<?> sourceEntity, Pers
735
750
return (T ) propertyAccessor .getBean ();
736
751
}
737
752
738
- private <Y > Long queryRelatedNode (Object entity , Neo4jPersistentEntity <?> targetNodeDescription ) {
739
-
740
- Neo4jPersistentProperty requiredIdProperty = targetNodeDescription .getRequiredIdProperty ();
741
- PersistentPropertyAccessor <Object > targetPropertyAccessor = targetNodeDescription .getPropertyAccessor (entity );
742
- Object idValue = targetPropertyAccessor .getProperty (requiredIdProperty );
743
-
744
- return neo4jClient .query (() ->
745
- renderer .render (cypherGenerator .prepareMatchOf (targetNodeDescription ,
746
- targetNodeDescription .getIdExpression ().isEqualTo (parameter (Constants .NAME_OF_ID )))
747
- .returning (Constants .NAME_OF_INTERNAL_ID )
748
- .build ())
749
- )
750
- .bindAll (Collections .singletonMap (Constants .NAME_OF_ID , idValue ))
751
- .fetchAs (Long .class ).one ().get ();
752
- }
753
-
754
753
private <Y > Long saveRelatedNode (Object entity , NodeDescription targetNodeDescription ) {
755
754
756
755
DynamicLabels dynamicLabels = determineDynamicLabels (entity , (Neo4jPersistentEntity ) targetNodeDescription );
0 commit comments