40
40
import org .neo4j .cypherdsl .core .Statement ;
41
41
import org .neo4j .cypherdsl .core .renderer .Renderer ;
42
42
import org .neo4j .driver .exceptions .NoSuchRecordException ;
43
- import org .neo4j .driver .summary .ResultSummary ;
44
43
import org .neo4j .driver .summary .SummaryCounters ;
45
44
import org .springframework .beans .BeansException ;
46
45
import org .springframework .beans .factory .BeanFactory ;
@@ -232,7 +231,7 @@ private <T> Mono<T> saveImpl(T instance, @Nullable String inDatabase) {
232
231
Statement saveStatement = cypherGenerator .prepareSaveOf (entityMetaData , dynamicLabels );
233
232
234
233
Mono <Long > idMono = this .neo4jClient .query (() -> renderer .render (saveStatement )).in (inDatabase )
235
- .bind (( T ) entity ).with (neo4jMappingContext .getRequiredBinderFunctionFor ((Class <T >) entity .getClass ()))
234
+ .bind (entity ).with (neo4jMappingContext .getRequiredBinderFunctionFor ((Class <T >) entity .getClass ()))
236
235
.fetchAs (Long .class ).one ().switchIfEmpty (Mono .defer (() -> {
237
236
if (entityMetaData .hasVersionProperty ()) {
238
237
return Mono .error (() -> new OptimisticLockingFailureException (OPTIMISTIC_LOCKING_ERROR_MESSAGE ));
@@ -440,26 +439,34 @@ private Mono<Void> processRelations(Neo4jPersistentEntity<?> neo4jPersistentEnti
440
439
new NestedRelationshipProcessingStateMachine ());
441
440
}
442
441
443
- private Mono <Void > processNestedRelations (Neo4jPersistentEntity <?> neo4jPersistentEntity , Object parentObject ,
442
+ private Mono <Void > processNestedRelations (Neo4jPersistentEntity <?> sourceEntity , Object parentObject ,
444
443
boolean isParentObjectNew , @ Nullable String inDatabase , NestedRelationshipProcessingStateMachine stateMachine ) {
445
444
446
445
return Mono .defer (() -> {
447
- PersistentPropertyAccessor <?> propertyAccessor = neo4jPersistentEntity .getPropertyAccessor (parentObject );
448
- Object fromId = propertyAccessor .getProperty (neo4jPersistentEntity .getRequiredIdProperty ());
446
+ PersistentPropertyAccessor <?> propertyAccessor = sourceEntity .getPropertyAccessor (parentObject );
447
+ Object fromId = propertyAccessor .getProperty (sourceEntity .getRequiredIdProperty ());
449
448
List <Mono <Void >> relationshipCreationMonos = new ArrayList <>();
450
449
451
- neo4jPersistentEntity .doWithAssociations ((AssociationHandler <Neo4jPersistentProperty >) association -> {
450
+ sourceEntity .doWithAssociations ((AssociationHandler <Neo4jPersistentProperty >) association -> {
452
451
453
452
// create context to bundle parameters
454
453
NestedRelationshipContext relationshipContext = NestedRelationshipContext .of (association , propertyAccessor ,
455
- neo4jPersistentEntity );
454
+ sourceEntity );
456
455
457
456
Collection <?> relatedValuesToStore = MappingSupport .unifyRelationshipValue (relationshipContext .getInverse (),
458
457
relationshipContext .getValue ());
459
458
460
459
RelationshipDescription relationshipDescription = relationshipContext .getRelationship ();
461
460
RelationshipDescription relationshipDescriptionObverse = relationshipDescription .getRelationshipObverse ();
462
461
462
+ Neo4jPersistentProperty idProperty ;
463
+ if (!relationshipDescription .hasInternalIdProperty ()) {
464
+ idProperty = null ;
465
+ } else {
466
+ Neo4jPersistentEntity <?> relationshipPropertiesEntity = (Neo4jPersistentEntity <?>) relationshipDescription .getRelationshipPropertiesEntity ();
467
+ idProperty = relationshipPropertiesEntity .getIdProperty ();
468
+ }
469
+
463
470
// break recursive procession and deletion of previously created relationships
464
471
ProcessState processState = stateMachine .getStateOf (relationshipDescriptionObverse , relatedValuesToStore );
465
472
if (processState == ProcessState .PROCESSED_ALL_RELATIONSHIPS ) {
@@ -469,13 +476,32 @@ private Mono<Void> processNestedRelations(Neo4jPersistentEntity<?> neo4jPersiste
469
476
// remove all relationships before creating all new if the entity is not new
470
477
// this avoids the usage of cache but might have significant impact on overall performance
471
478
if (!isParentObjectNew ) {
472
- Statement relationshipRemoveQuery = cypherGenerator .prepareDeleteOf (neo4jPersistentEntity ,
473
- relationshipDescription );
479
+
480
+ List <Long > knownRelationshipsIds = new ArrayList <>();
481
+ if (idProperty != null ) {
482
+ for (Object relatedValueToStore : relatedValuesToStore ) {
483
+ if (relatedValueToStore == null ) {
484
+ continue ;
485
+ }
486
+
487
+ Long id = (Long ) relationshipContext
488
+ .getRelationshipPropertiesPropertyAccessor (relatedValueToStore )
489
+ .getProperty (idProperty );
490
+ if (id != null ) {
491
+ knownRelationshipsIds .add (id );
492
+ }
493
+ }
494
+ }
495
+
496
+ Statement relationshipRemoveQuery = cypherGenerator .prepareDeleteOf (sourceEntity , relationshipDescription );
474
497
475
498
relationshipCreationMonos .add (
476
499
neo4jClient .query (renderer .render (relationshipRemoveQuery )).in (inDatabase )
477
- .bind (convertIdValues (neo4jPersistentEntity .getIdProperty (), fromId ))
478
- .to (Constants .FROM_ID_PARAMETER_NAME ).run ().checkpoint ("delete relationships" ).then ());
500
+ .bind (convertIdValues (sourceEntity .getIdProperty (), fromId )) //
501
+ .to (Constants .FROM_ID_PARAMETER_NAME ) //
502
+ .bind (knownRelationshipsIds ) //
503
+ .to (Constants .NAME_OF_KNOWN_RELATIONSHIPS_PARAM ) //
504
+ .run ().checkpoint ("delete relationships" ).then ());
479
505
}
480
506
481
507
// nothing to do because there is nothing to map
@@ -487,37 +513,47 @@ private Mono<Void> processNestedRelations(Neo4jPersistentEntity<?> neo4jPersiste
487
513
488
514
for (Object relatedValueToStore : relatedValuesToStore ) {
489
515
490
- Object valueToBeSavedPreEvt = relationshipContext .identifyAndExtractRelationshipTargetNode (relatedValueToStore );
516
+ Object relatedNodePreEvt = relationshipContext .identifyAndExtractRelationshipTargetNode (relatedValueToStore );
491
517
492
- Mono <Void > createRelationship = eventSupport .maybeCallBeforeBind (valueToBeSavedPreEvt )
493
- .flatMap (valueToBeSaved -> {
494
- Neo4jPersistentEntity <?> targetNodeDescription = neo4jMappingContext
495
- .getPersistentEntity (valueToBeSavedPreEvt .getClass ());
496
- return Mono .just (targetNodeDescription .isNew (valueToBeSaved )).flatMap (isNew ->
497
- saveRelatedNode (valueToBeSaved , relationshipContext .getAssociationTargetType (),
498
- targetNodeDescription , inDatabase ).flatMap (relatedInternalId -> {
518
+ Mono <Void > createRelationship = eventSupport .maybeCallBeforeBind (relatedNodePreEvt )
519
+ .flatMap (relatedNode -> {
520
+ Neo4jPersistentEntity <?> targetEntity = neo4jMappingContext
521
+ .getPersistentEntity (relatedNodePreEvt .getClass ());
522
+ return Mono .just (targetEntity .isNew (relatedNode )).flatMap (isNew ->
523
+ saveRelatedNode (relatedNode , relationshipContext .getAssociationTargetType (),
524
+ targetEntity , inDatabase ).flatMap (relatedInternalId -> {
499
525
500
526
// if an internal id is used this must get set to link this entity in the next iteration
501
- if (targetNodeDescription .isUsingInternalIds ()) {
502
- PersistentPropertyAccessor <?> targetPropertyAccessor = targetNodeDescription
503
- .getPropertyAccessor (valueToBeSaved );
504
- targetPropertyAccessor .setProperty (targetNodeDescription .getRequiredIdProperty (),
527
+ if (targetEntity .isUsingInternalIds ()) {
528
+ PersistentPropertyAccessor <?> targetPropertyAccessor = targetEntity
529
+ .getPropertyAccessor (relatedNode );
530
+ targetPropertyAccessor .setProperty (targetEntity .getRequiredIdProperty (),
505
531
relatedInternalId );
506
532
}
507
533
508
534
CreateRelationshipStatementHolder statementHolder = neo4jMappingContext .createStatement (
509
- neo4jPersistentEntity , relationshipContext , relatedInternalId , relatedValueToStore );
535
+ sourceEntity , relationshipContext , relatedValueToStore );
510
536
511
537
// in case of no properties the bind will just return an empty map
512
- Mono <ResultSummary > relationshipCreationMonoNested = neo4jClient
538
+ Mono <Long > relationshipCreationMonoNested = neo4jClient
513
539
.query (renderer .render (statementHolder .getStatement ())).in (inDatabase )
514
- .bind (convertIdValues (targetNodeDescription .getRequiredIdProperty (), fromId ))
515
- .to (Constants .FROM_ID_PARAMETER_NAME )
516
- .bindAll (statementHolder .getProperties ()).run ();
540
+ .bind (convertIdValues (targetEntity .getRequiredIdProperty (), fromId )) //
541
+ .to (Constants .FROM_ID_PARAMETER_NAME ) //
542
+ .bind (relatedInternalId ) //
543
+ .to (Constants .TO_ID_PARAMETER_NAME ) //
544
+ .bindAll (statementHolder .getProperties ())
545
+ .fetchAs (Long .class ).one ()
546
+ .doOnNext (relationshipInternalId -> {
547
+ if (idProperty != null ) {
548
+ relationshipContext
549
+ .getRelationshipPropertiesPropertyAccessor (relatedValueToStore )
550
+ .setProperty (idProperty , relationshipInternalId );
551
+ }
552
+ });
517
553
518
554
if (processState != ProcessState .PROCESSED_ALL_VALUES ) {
519
555
return relationshipCreationMonoNested .checkpoint ().then (
520
- processNestedRelations (targetNodeDescription , valueToBeSaved ,
556
+ processNestedRelations (targetEntity , relatedNode ,
521
557
isNew , inDatabase , stateMachine ));
522
558
} else {
523
559
return relationshipCreationMonoNested .checkpoint ().then ();
0 commit comments