4141import org .neo4j .cypherdsl .core .Node ;
4242import org .neo4j .cypherdsl .core .Statement ;
4343import org .neo4j .cypherdsl .core .renderer .Renderer ;
44+ import org .neo4j .driver .Value ;
4445import org .neo4j .driver .exceptions .NoSuchRecordException ;
4546import org .neo4j .driver .summary .ResultSummary ;
4647import org .neo4j .driver .summary .SummaryCounters ;
@@ -244,15 +245,21 @@ private <T> T saveImpl(T instance, @Nullable String inDatabase) {
244245 .with (neo4jMappingContext .getRequiredBinderFunctionFor ((Class <T >) entityToBeSaved .getClass ()))
245246 .fetchAs (Long .class ).one ();
246247
247- if (entityMetaData .hasVersionProperty () && !optionalInternalId .isPresent ()) {
248- throw new OptimisticLockingFailureException (OPTIMISTIC_LOCKING_ERROR_MESSAGE );
248+
249+ if (!optionalInternalId .isPresent ()) {
250+ if (entityMetaData .hasVersionProperty ()) {
251+ throw new OptimisticLockingFailureException (OPTIMISTIC_LOCKING_ERROR_MESSAGE );
252+ }
253+ // defensive exception throwing
254+ throw new IllegalStateException ("Could not retrieve an internal id while saving." );
249255 }
250256
257+ Long internalId = optionalInternalId .get ();
251258 PersistentPropertyAccessor <T > propertyAccessor = entityMetaData .getPropertyAccessor (entityToBeSaved );
252259 if (entityMetaData .isUsingInternalIds ()) {
253- propertyAccessor .setProperty (entityMetaData .getRequiredIdProperty (), optionalInternalId . get () );
260+ propertyAccessor .setProperty (entityMetaData .getRequiredIdProperty (), internalId );
254261 }
255- processRelations (entityMetaData , instance , propertyAccessor , inDatabase , isEntityNew );
262+ processRelations (entityMetaData , instance , internalId , propertyAccessor , inDatabase , isEntityNew );
256263
257264 return propertyAccessor .getBean ();
258265 }
@@ -452,6 +459,14 @@ private <T> ExecutableQuery<T> createExecutableQuery(Class<T> domainType, String
452459 * @param parentPropertyAccessor The property accessor of the parent, to modify the relationships
453460 * @param isParentObjectNew A flag if the parent was new
454461 */
462+ private <T > T processRelations (Neo4jPersistentEntity <?> neo4jPersistentEntity , T originalInstance , Long internalId ,
463+ PersistentPropertyAccessor <?> parentPropertyAccessor ,
464+ @ Nullable String inDatabase , boolean isParentObjectNew ) {
465+
466+ return processNestedRelations (neo4jPersistentEntity , parentPropertyAccessor , isParentObjectNew , inDatabase ,
467+ new NestedRelationshipProcessingStateMachine (originalInstance , internalId ));
468+ }
469+
455470 private <T > T processRelations (Neo4jPersistentEntity <?> neo4jPersistentEntity , T originalInstance ,
456471 PersistentPropertyAccessor <?> parentPropertyAccessor ,
457472 @ Nullable String inDatabase , boolean isParentObjectNew ) {
@@ -534,22 +549,22 @@ private <T> T processNestedRelations(Neo4jPersistentEntity<?> sourceEntity, Pers
534549 for (Object relatedValueToStore : relatedValuesToStore ) {
535550
536551 // here a map entry is not always anymore a dynamic association
537- Object relatedObjectBeforeCallbacks = relationshipContext .identifyAndExtractRelationshipTargetNode (relatedValueToStore );
538- Neo4jPersistentEntity <?> targetEntity = neo4jMappingContext .getPersistentEntity (relatedObjectBeforeCallbacks .getClass ());
539-
540- boolean isEntityNew = targetEntity .isNew (relatedObjectBeforeCallbacks );
552+ Object relatedObjectBeforeCallbacksApplied = relationshipContext .identifyAndExtractRelationshipTargetNode (relatedValueToStore );
553+ Neo4jPersistentEntity <?> targetEntity = neo4jMappingContext .getPersistentEntity (relatedObjectBeforeCallbacksApplied .getClass ());
541554
542- Object newRelatedObject = eventSupport .maybeCallBeforeBind (relatedObjectBeforeCallbacks );
555+ boolean isEntityNew = targetEntity .isNew (relatedObjectBeforeCallbacksApplied );
556+ Object newRelatedObject = stateMachine .hasProcessedValue (relatedObjectBeforeCallbacksApplied )
557+ ? stateMachine .getProcessedAs (relatedObjectBeforeCallbacksApplied )
558+ : eventSupport .maybeCallBeforeBind (relatedObjectBeforeCallbacksApplied );
543559
544560 Long relatedInternalId ;
545561 // No need to save values if processed
546562 if (stateMachine .hasProcessedValue (relatedValueToStore )) {
547- Object newRelatedObjectForQuery = stateMachine .getProcessedAs (newRelatedObject );
548- relatedInternalId = queryRelatedNode (newRelatedObjectForQuery , targetEntity , inDatabase );
563+ relatedInternalId = stateMachine .getInternalId (relatedObjectBeforeCallbacksApplied );
549564 } else {
550565 relatedInternalId = saveRelatedNode (newRelatedObject , targetEntity , inDatabase );
551566 }
552- stateMachine .markValueAsProcessed (relatedValueToStore );
567+ stateMachine .markValueAsProcessed (relatedValueToStore , relatedInternalId );
553568
554569 Object idValue = idProperty != null
555570 ? relationshipContext
@@ -581,8 +596,8 @@ private <T> T processNestedRelations(Neo4jPersistentEntity<?> sourceEntity, Pers
581596 // if an internal id is used this must be set to link this entity in the next iteration
582597 if (targetEntity .isUsingInternalIds ()) {
583598 targetPropertyAccessor .setProperty (targetEntity .getRequiredIdProperty (), relatedInternalId );
584- stateMachine .markValueAsProcessedAs (newRelatedObject , targetPropertyAccessor .getBean ());
585599 }
600+ stateMachine .markValueAsProcessedAs (relatedObjectBeforeCallbacksApplied , targetPropertyAccessor .getBean ());
586601
587602 if (processState != ProcessState .PROCESSED_ALL_VALUES ) {
588603 processNestedRelations (targetEntity , targetPropertyAccessor , isEntityNew , inDatabase , stateMachine );
@@ -594,7 +609,7 @@ private <T> T processNestedRelations(Neo4jPersistentEntity<?> sourceEntity, Pers
594609 relatedValueToStore ,
595610 targetPropertyAccessor );
596611
597- relationshipHandler .handle (relatedValueToStore , relatedObjectBeforeCallbacks , potentiallyRecreatedNewRelatedObject );
612+ relationshipHandler .handle (relatedValueToStore , relatedObjectBeforeCallbacksApplied , potentiallyRecreatedNewRelatedObject );
598613 }
599614
600615 relationshipHandler .applyFinalResultToOwner (propertyAccessor );
@@ -615,8 +630,9 @@ private <Y> Long queryRelatedNode(Object entity, Neo4jPersistentEntity<?> target
615630 targetNodeDescription .getIdExpression ().isEqualTo (parameter (Constants .NAME_OF_ID )))
616631 .returning (Constants .NAME_OF_INTERNAL_ID )
617632 .build ())
618- )
619- .in (inDatabase ).bindAll (Collections .singletonMap (Constants .NAME_OF_ID , idValue ))
633+ )
634+ .in (inDatabase ).bindAll (Collections .singletonMap (Constants .NAME_OF_ID ,
635+ neo4jMappingContext .getConversionService ().convert (idValue , Value .class )))
620636 .fetchAs (Long .class ).one ().get ();
621637 }
622638
0 commit comments