3636import java .util .List ;
3737import java .util .Map ;
3838import java .util .Objects ;
39+ import java .util .Optional ;
3940import java .util .Set ;
4041import java .util .concurrent .ConcurrentHashMap ;
4142import java .util .concurrent .atomic .AtomicReference ;
@@ -446,10 +447,11 @@ private <T> Mono<T> saveImpl(T instance, @Nullable Collection<PropertyFilter.Pro
446447
447448 PersistentPropertyAccessor <T > propertyAccessor = entityMetaData .getPropertyAccessor (entityToBeSaved );
448449 return idMono .doOnNext (newOrUpdatedNode -> {
449- IdentitySupport .updateInternalId (entityMetaData , propertyAccessor , newOrUpdatedNode );
450+ var elementId = IdentitySupport .getElementId (newOrUpdatedNode );
451+ TemplateSupport .setGeneratedIdIfNecessary (entityMetaData , propertyAccessor , elementId , Optional .of (newOrUpdatedNode ));
450452 TemplateSupport .updateVersionPropertyIfPossible (entityMetaData , propertyAccessor , newOrUpdatedNode );
451- finalStateMachine .markValueAsProcessed (instance , IdentitySupport . getInternalId ( newOrUpdatedNode ) );
452- }).map (IdentitySupport ::getInternalId )
453+ finalStateMachine .markEntityAsProcessed (instance , elementId );
454+ }).map (IdentitySupport ::getElementId )
453455 .flatMap (internalId -> processRelations (entityMetaData , propertyAccessor , isNewEntity , finalStateMachine , binderFunction .filter ));
454456 });
455457 }
@@ -582,15 +584,15 @@ private <T> Flux<T> saveAllImpl(Iterable<T> instances, @Nullable Collection<Prop
582584 .query (() -> renderer .render (cypherGenerator .prepareSaveOfMultipleInstancesOf (entityMetaData )))
583585 .bind (boundedEntityList ).to (Constants .NAME_OF_ENTITY_LIST_PARAM )
584586 .fetchAs (Tuple2 .class )
585- .mappedBy ((t , r ) -> Tuples .of (r .get (Constants .NAME_OF_ID ), r .get (Constants .NAME_OF_INTERNAL_ID ). asLong ()))
587+ .mappedBy ((t , r ) -> Tuples .of (r .get (Constants .NAME_OF_ID ), r .get (Constants .NAME_OF_ELEMENT_ID ). asString ()))
586588 .all ()
587- .collectMap (m -> (Value ) m .getT1 (), m -> (Long ) m .getT2 ());
589+ .collectMap (m -> (Value ) m .getT1 (), m -> (String ) m .getT2 ());
588590 }).flatMapMany (idToInternalIdMapping -> Flux .fromIterable (entitiesToBeSaved )
589591 .flatMap (t -> {
590592 PersistentPropertyAccessor <T > propertyAccessor = entityMetaData .getPropertyAccessor (t .getT3 ());
591593 Neo4jPersistentProperty idProperty = entityMetaData .getRequiredIdProperty ();
592594 Object id = convertIdValues (idProperty , propertyAccessor .getProperty (idProperty ));
593- Long internalId = idToInternalIdMapping .get (id );
595+ String internalId = idToInternalIdMapping .get (id );
594596 return processRelations (entityMetaData , propertyAccessor , t .getT2 (), new NestedRelationshipProcessingStateMachine (neo4jMappingContext , t .getT1 (), internalId ),
595597 TemplateSupport .computeIncludePropertyPredicate (pps , entityMetaData ));
596598 }))
@@ -709,9 +711,9 @@ private Mono<NodesAndRelationshipsByIdStatementProvider> createNodesAndRelations
709711 return Mono .deferContextual (ctx -> {
710712 Class <?> rootClass = entityMetaData .getUnderlyingClass ();
711713
712- Set <Long > rootNodeIds = ctx .get ("rootNodes" );
713- Set <Long > processedRelationshipIds = ctx .get ("processedRelationships" );
714- Set <Long > processedNodeIds = ctx .get ("processedNodes" );
714+ Set <String > rootNodeIds = ctx .get ("rootNodes" );
715+ Set <String > processedRelationshipIds = ctx .get ("processedRelationships" );
716+ Set <String > processedNodeIds = ctx .get ("processedNodes" );
715717 return Flux .fromIterable (entityMetaData .getRelationshipsInHierarchy (queryFragments ::includeField ))
716718 .flatMap (relationshipDescription -> {
717719
@@ -723,16 +725,19 @@ private Mono<NodesAndRelationshipsByIdStatementProvider> createNodesAndRelations
723725 usedParameters .putAll (statement .getCatalog ().getParameters ());
724726 return neo4jClient .query (renderer .render (statement ))
725727 .bindAll (usedParameters )
726- .fetchAs (TupleOfLongsHolder .class )
728+ .fetchAs (Tuple2 .class )
727729 .mappedBy ((t , r ) -> {
728- Collection <Long > rootIds = r .get (Constants .NAME_OF_SYNTHESIZED_ROOT_NODE ).asList (Value ::asLong );
730+ Collection <String > rootIds = r .get (Constants .NAME_OF_SYNTHESIZED_ROOT_NODE ).asList (Value ::asString );
729731 rootNodeIds .addAll (rootIds );
730- Collection <Long > newRelationshipIds = r .get (Constants .NAME_OF_SYNTHESIZED_RELATIONS ).asList (Value ::asLong );
731- Collection <Long > newRelatedNodeIds = r .get (Constants .NAME_OF_SYNTHESIZED_RELATED_NODES ).asList (Value ::asLong );
732- return TupleOfLongsHolder . with ( Tuples .of (newRelationshipIds , newRelatedNodeIds ) );
732+ Collection <String > newRelationshipIds = r .get (Constants .NAME_OF_SYNTHESIZED_RELATIONS ).asList (Value ::asString );
733+ Collection <String > newRelatedNodeIds = r .get (Constants .NAME_OF_SYNTHESIZED_RELATED_NODES ).asList (Value ::asString );
734+ return Tuples .of (newRelationshipIds , newRelatedNodeIds );
733735 })
734736 .one ()
735- .map (TupleOfLongsHolder ::get )
737+ .map ((t ) -> {
738+ //noinspection unchecked
739+ return (Tuple2 <Collection <String >, Collection <String >>) t ;
740+ })
736741 .expand (iterateAndMapNextLevel (relationshipDescription , queryFragments , rootClass , PropertyPathWalkStep .empty ()));
737742 })
738743 .then (Mono .fromSupplier (() -> new NodesAndRelationshipsByIdStatementProvider (rootNodeIds , processedRelationshipIds , processedNodeIds , queryFragments )));
@@ -744,23 +749,7 @@ private Mono<NodesAndRelationshipsByIdStatementProvider> createNodesAndRelations
744749
745750 }
746751
747- static class TupleOfLongsHolder {
748- private final Tuple2 <Collection <Long >, Collection <Long >> content ;
749-
750- static TupleOfLongsHolder with (Tuple2 <Collection <Long >, Collection <Long >> content ) {
751- return new TupleOfLongsHolder (content );
752- }
753-
754- private TupleOfLongsHolder (Tuple2 <Collection <Long >, Collection <Long >> content ) {
755- this .content = content ;
756- }
757-
758- Tuple2 <Collection <Long >, Collection <Long >> get () {
759- return content ;
760- }
761- }
762-
763- private Flux <Tuple2 <Collection <Long >, Collection <Long >>> iterateNextLevel (Collection <Long > relatedNodeIds ,
752+ private Flux <Tuple2 <Collection <String >, Collection <String >>> iterateNextLevel (Collection <String > relatedNodeIds ,
764753 RelationshipDescription sourceRelationshipDescription , QueryFragments queryFragments ,
765754 Class <?> rootClass , PropertyPathWalkStep currentPathStep ) {
766755
@@ -786,43 +775,46 @@ private Flux<Tuple2<Collection<Long>, Collection<Long>>> iterateNextLevel(Collec
786775
787776 Statement statement = cypherGenerator
788777 .prepareMatchOf (target , relDe , null ,
789- Functions .id (node ).in (Cypher .parameter (Constants .NAME_OF_ID )))
778+ Functions .elementId (node ).in (Cypher .parameter (Constants .NAME_OF_ID )))
790779 .returning (cypherGenerator .createGenericReturnStatement ()).build ();
791780
792781 return neo4jClient .query (renderer .render (statement ))
793782 .bindAll (Collections .singletonMap (Constants .NAME_OF_ID , relatedNodeIds ))
794- .fetchAs (TupleOfLongsHolder .class )
783+ .fetchAs (Tuple2 .class )
795784 .mappedBy ((t , r ) -> {
796- Collection <Long > newRelationshipIds = r .get (Constants .NAME_OF_SYNTHESIZED_RELATIONS ).asList (Value ::asLong );
797- Collection <Long > newRelatedNodeIds = r .get (Constants .NAME_OF_SYNTHESIZED_RELATED_NODES ).asList (Value ::asLong );
785+ Collection <String > newRelationshipIds = r .get (Constants .NAME_OF_SYNTHESIZED_RELATIONS ).asList (Value ::asString );
786+ Collection <String > newRelatedNodeIds = r .get (Constants .NAME_OF_SYNTHESIZED_RELATED_NODES ).asList (Value ::asString );
798787
799- return TupleOfLongsHolder . with ( Tuples .of (newRelationshipIds , newRelatedNodeIds ) );
788+ return Tuples .of (newRelationshipIds , newRelatedNodeIds );
800789 })
801790 .one ()
802- .map (TupleOfLongsHolder ::get )
791+ .map ((t ) -> {
792+ //noinspection unchecked
793+ return (Tuple2 <Collection <String >, Collection <String >>) t ;
794+ })
803795 .expand (object -> iterateAndMapNextLevel (relDe , queryFragments , rootClass , nextPathStep ).apply (object ));
804796 });
805797
806798 }
807799
808800 @ NonNull
809- private Function <Tuple2 <Collection <Long >, Collection <Long >>,
810- Publisher <Tuple2 <Collection <Long >, Collection <Long >>>> iterateAndMapNextLevel (
801+ private Function <Tuple2 <Collection <String >, Collection <String >>,
802+ Publisher <Tuple2 <Collection <String >, Collection <String >>>> iterateAndMapNextLevel (
811803 RelationshipDescription relationshipDescription , QueryFragments queryFragments , Class <?> rootClass , PropertyPathWalkStep currentPathStep ) {
812804
813805 return newRelationshipAndRelatedNodeIds ->
814806 Flux .deferContextual (ctx -> {
815- Set <Long > relationshipIds = ctx .get ("processedRelationships" );
816- Set <Long > processedNodeIds = ctx .get ("processedNodes" );
807+ Set <String > relationshipIds = ctx .get ("processedRelationships" );
808+ Set <String > processedNodeIds = ctx .get ("processedNodes" );
817809
818- Collection <Long > newRelationshipIds = newRelationshipAndRelatedNodeIds .getT1 ();
819- Set <Long > tmpProcessedRels = ConcurrentHashMap .newKeySet (newRelationshipIds .size ());
810+ Collection <String > newRelationshipIds = newRelationshipAndRelatedNodeIds .getT1 ();
811+ Set <String > tmpProcessedRels = ConcurrentHashMap .newKeySet (newRelationshipIds .size ());
820812 tmpProcessedRels .addAll (newRelationshipIds );
821813 tmpProcessedRels .removeAll (relationshipIds );
822814 relationshipIds .addAll (newRelationshipIds );
823815
824- Collection <Long > newRelatedNodeIds = newRelationshipAndRelatedNodeIds .getT2 ();
825- Set <Long > tmpProcessedNodes = ConcurrentHashMap .newKeySet (newRelatedNodeIds .size ());
816+ Collection <String > newRelatedNodeIds = newRelationshipAndRelatedNodeIds .getT2 ();
817+ Set <String > tmpProcessedNodes = ConcurrentHashMap .newKeySet (newRelatedNodeIds .size ());
826818 tmpProcessedNodes .addAll (newRelatedNodeIds );
827819 tmpProcessedNodes .removeAll (processedNodeIds );
828820 processedNodeIds .addAll (newRelatedNodeIds );
@@ -904,14 +896,14 @@ private <T> Mono<T> processNestedRelations(Neo4jPersistentEntity<?> sourceEntity
904896 // This avoids the usage of cache but might have significant impact on overall performance
905897 if (!isParentObjectNew && !stateMachine .hasProcessedRelationship (fromId , relationshipDescription )) {
906898
907- List <Long > knownRelationshipsIds = new ArrayList <>();
899+ List <Object > knownRelationshipsIds = new ArrayList <>();
908900 if (idProperty != null ) {
909901 for (Object relatedValueToStore : relatedValuesToStore ) {
910902 if (relatedValueToStore == null ) {
911903 continue ;
912904 }
913905
914- Long id = ( Long ) relationshipContext
906+ Object id = relationshipContext
915907 .getRelationshipPropertiesPropertyAccessor (relatedValueToStore )
916908 .getProperty (idProperty );
917909 if (id != null ) {
@@ -950,44 +942,38 @@ private <T> Mono<T> processNestedRelations(Neo4jPersistentEntity<?> sourceEntity
950942 .flatMap (newRelatedObject -> {
951943 Neo4jPersistentEntity <?> targetEntity = neo4jMappingContext .getRequiredPersistentEntity (relatedObjectBeforeCallbacksApplied .getClass ());
952944
953- Mono <Tuple2 <AtomicReference <Long >, AtomicReference <Entity >>> queryOrSave ;
945+ Mono <Tuple2 <AtomicReference <String >, AtomicReference <Entity >>> queryOrSave ;
954946 if (stateMachine .hasProcessedValue (relatedValueToStore )) {
955- AtomicReference <Long > relatedInternalId = new AtomicReference <>();
956- Long possibleValue = stateMachine .getInternalId (relatedValueToStore );
947+ AtomicReference <String > relatedInternalId = new AtomicReference <>();
948+ String possibleValue = stateMachine .getObjectId (relatedValueToStore );
957949 if (possibleValue != null ) {
958950 relatedInternalId .set (possibleValue );
959951 }
960952 queryOrSave = Mono .just (Tuples .of (relatedInternalId , new AtomicReference <>()));
961953 } else {
962954 queryOrSave = saveRelatedNode (newRelatedObject , targetEntity , includeProperty , currentPropertyPath )
963- .map (entity -> Tuples .of (new AtomicReference <>(IdentitySupport .getInternalId (entity )), new AtomicReference <>(entity )))
964- .doOnNext (entity -> {
965- stateMachine .markValueAsProcessed (relatedValueToStore , entity .getT1 ().get ());
955+ .map (entity -> Tuples .of (new AtomicReference <>(IdentitySupport .getElementId (entity )), new AtomicReference <>(entity )))
956+ .doOnNext (t -> {
957+ var relatedInternalId = t .getT1 ().get ();
958+ stateMachine .markEntityAsProcessed (relatedValueToStore , relatedInternalId );
966959 if (relatedValueToStore instanceof MappingSupport .RelationshipPropertiesWithEntityHolder ) {
967- Object value = ((MappingSupport .RelationshipPropertiesWithEntityHolder ) relatedValueToStore ).getRelatedEntity ();
968- stateMachine .markValueAsProcessedAs ( value , entity . getT1 (). get () );
960+ Object entity = ((MappingSupport .RelationshipPropertiesWithEntityHolder ) relatedValueToStore ).getRelatedEntity ();
961+ stateMachine .markAsAliased ( entity , relatedInternalId );
969962 }
970963 });
971964 }
972965 return queryOrSave .flatMap (idAndEntity -> {
973- Long relatedInternalId = idAndEntity .getT1 ().get ();
966+ String relatedInternalId = idAndEntity .getT1 ().get ();
974967 Entity savedEntity = idAndEntity .getT2 ().get ();
975968 Neo4jPersistentProperty requiredIdProperty = targetEntity .getRequiredIdProperty ();
976969 PersistentPropertyAccessor <?> targetPropertyAccessor = targetEntity .getPropertyAccessor (newRelatedObject );
977- Object actualRelatedId = targetPropertyAccessor .getProperty (requiredIdProperty );
978- // if an internal id is used this must be set to link this entity in the next iteration
979- if (targetEntity .isUsingInternalIds ()) {
980- if (relatedInternalId == null && actualRelatedId != null ) {
981- relatedInternalId = (Long ) targetPropertyAccessor .getProperty (requiredIdProperty );
982- } else if (actualRelatedId == null ) {
983- targetPropertyAccessor .setProperty (requiredIdProperty , relatedInternalId );
984- }
985- }
970+ Object possibleInternalLongId = targetPropertyAccessor .getProperty (requiredIdProperty );
971+ relatedInternalId = TemplateSupport .retrieveOrSetRelatedId (targetEntity , targetPropertyAccessor , Optional .ofNullable (savedEntity ), relatedInternalId );
986972 if (savedEntity != null ) {
987973 TemplateSupport .updateVersionPropertyIfPossible (targetEntity , targetPropertyAccessor , savedEntity );
988974 }
989- stateMachine .markValueAsProcessedAs (relatedObjectBeforeCallbacksApplied , targetPropertyAccessor .getBean ());
990- stateMachine .markRelationshipAsProcessed (actualRelatedId == null ? relatedInternalId : actualRelatedId ,
975+ stateMachine .markAsAliased (relatedObjectBeforeCallbacksApplied , targetPropertyAccessor .getBean ());
976+ stateMachine .markRelationshipAsProcessed (possibleInternalLongId == null ? relatedInternalId : possibleInternalLongId ,
991977 relationshipDescription .getRelationshipObverse ());
992978
993979 Object idValue = idProperty != null
@@ -1016,8 +1002,8 @@ private <T> Mono<T> processNestedRelations(Neo4jPersistentEntity<?> sourceEntity
10161002 .bind (idValue ) //
10171003 .to (Constants .NAME_OF_KNOWN_RELATIONSHIP_PARAM ) //
10181004 .bindAll (statementHolder .getProperties ())
1019- .fetchAs (Long .class )
1020- .mappedBy (IdentitySupport :: getInternalId )
1005+ .fetchAs (Object .class )
1006+ .mappedBy (( t , r ) -> IdentitySupport . mapperForRelatedIdValues ( idProperty ). apply ( r ) )
10211007 .one ()
10221008 .flatMap (relationshipInternalId -> {
10231009 if (idProperty != null && isNewRelationship ) {
0 commit comments