@@ -799,6 +799,11 @@ private <T> T processNestedRelations(
799799 Neo4jPersistentProperty relationshipProperty = association .getInverse ();
800800
801801 RelationshipHandler relationshipHandler = RelationshipHandler .forProperty (relationshipProperty , rawValue );
802+ List <Object > plainRelationshipRows = new ArrayList <>();
803+ List <Map <String , Object >> relationshipPropertiesRows = new ArrayList <>();
804+ List <Map <String , Object >> newRelationshipPropertiesRows = new ArrayList <>();
805+ List <Object > updateRelatedValuesToStore = new ArrayList <>();
806+ List <Object > newRelatedValuesToStore = new ArrayList <>();
802807
803808 for (Object relatedValueToStore : relatedValuesToStore ) {
804809
@@ -847,28 +852,60 @@ private <T> T processNestedRelations(
847852
848853 Object idValue = idProperty != null
849854 ? relationshipContext
850- .getRelationshipPropertiesPropertyAccessor (relatedValueToStore ).getProperty (idProperty )
855+ .getRelationshipPropertiesPropertyAccessor (relatedValueToStore ).getProperty (idProperty )
851856 : null ;
852857
858+ Map <String , Object > properties = new HashMap <>();
859+ properties .put (Constants .FROM_ID_PARAMETER_NAME , convertIdValues (sourceEntity .getRequiredIdProperty (), fromId ));
860+ properties .put (Constants .TO_ID_PARAMETER_NAME , relatedInternalId );
861+ properties .put (Constants .NAME_OF_KNOWN_RELATIONSHIP_PARAM , idValue );
853862 boolean isNewRelationship = idValue == null ;
854-
855- CreateRelationshipStatementHolder statementHolder = neo4jMappingContext .createStatement (
856- sourceEntity , relationshipContext , relatedValueToStore , isNewRelationship );
857-
858- Optional <Long > relationshipInternalId = neo4jClient .query (renderer .render (statementHolder .getStatement ()))
859- .bind (convertIdValues (sourceEntity .getRequiredIdProperty (), fromId )) //
860- .to (Constants .FROM_ID_PARAMETER_NAME ) //
861- .bind (relatedInternalId ) //
862- .to (Constants .TO_ID_PARAMETER_NAME ) //
863- .bind (idValue ) //
864- .to (Constants .NAME_OF_KNOWN_RELATIONSHIP_PARAM ) //
865- .bindAll (statementHolder .getProperties ())
866- .fetchAs (Long .class ).one ();
867-
868- if (idProperty != null && isNewRelationship ) {
869- relationshipContext
870- .getRelationshipPropertiesPropertyAccessor (relatedValueToStore )
871- .setProperty (idProperty , relationshipInternalId .get ());
863+ if (relationshipDescription .isDynamic ()) {
864+ // create new dynamic relationship properties
865+ if (relationshipDescription .hasRelationshipProperties () && isNewRelationship && idProperty != null ) {
866+ CreateRelationshipStatementHolder statementHolder = neo4jMappingContext .createStatementForSingleRelationship (
867+ sourceEntity , relationshipDescription , relatedValueToStore , true );
868+
869+ List <Object > row = Collections .singletonList (properties );
870+ statementHolder = statementHolder .addProperty (Constants .NAME_OF_RELATIONSHIP_LIST_PARAM , row );
871+ Optional <Long > relationshipInternalId = neo4jClient .query (renderer .render (statementHolder .getStatement ()))
872+ .bind (convertIdValues (sourceEntity .getRequiredIdProperty (), fromId )) //
873+ .to (Constants .FROM_ID_PARAMETER_NAME ) //
874+ .bind (relatedInternalId ) //
875+ .to (Constants .TO_ID_PARAMETER_NAME ) //
876+ .bind (idValue ) // always null
877+ .to (Constants .NAME_OF_KNOWN_RELATIONSHIP_PARAM ) //
878+ .bindAll (statementHolder .getProperties ())
879+ .fetchAs (Long .class ).one ();
880+ assignIdToRelationshipProperties (relationshipContext , relatedValueToStore , idProperty , relationshipInternalId .get ());
881+ } else { // plain (new or to update) dynamic relationship or dynamic relationships with properties to update
882+ CreateRelationshipStatementHolder statementHolder = neo4jMappingContext .createStatementForSingleRelationship (
883+ sourceEntity , relationshipDescription , relatedValueToStore , false );
884+
885+ List <Object > row = Collections .singletonList (properties );
886+ statementHolder = statementHolder .addProperty (Constants .NAME_OF_RELATIONSHIP_LIST_PARAM , row );
887+ neo4jClient .query (renderer .render (statementHolder .getStatement ()))
888+ .bind (convertIdValues (sourceEntity .getRequiredIdProperty (), fromId )) //
889+ .to (Constants .FROM_ID_PARAMETER_NAME ) //
890+ .bind (relatedInternalId ) //
891+ .to (Constants .TO_ID_PARAMETER_NAME ) //
892+ .bind (idValue )
893+ .to (Constants .NAME_OF_KNOWN_RELATIONSHIP_PARAM ) //
894+ .bindAll (statementHolder .getProperties ())
895+ .run ();
896+ }
897+ } else if (relationshipDescription .hasRelationshipProperties () && isNewRelationship && idProperty != null ) {
898+ newRelationshipPropertiesRows .add (properties );
899+ newRelatedValuesToStore .add (relatedValueToStore );
900+ } else if (relationshipDescription .hasRelationshipProperties ()) {
901+ neo4jMappingContext .getEntityConverter ().write (
902+ ((MappingSupport .RelationshipPropertiesWithEntityHolder ) relatedValueToStore ).getRelationshipProperties (),
903+ properties );
904+
905+ relationshipPropertiesRows .add (properties );
906+ } else {
907+ // non-dynamic relationship or relationship with properties
908+ plainRelationshipRows .add (properties );
872909 }
873910
874911 if (processState != ProcessState .PROCESSED_ALL_VALUES ) {
@@ -883,6 +920,37 @@ private <T> T processNestedRelations(
883920
884921 relationshipHandler .handle (relatedValueToStore , relatedObjectBeforeCallbacksApplied , potentiallyRecreatedNewRelatedObject );
885922 }
923+ // batch operations
924+ if (!(relationshipDescription .hasRelationshipProperties () || relationshipDescription .isDynamic () || plainRelationshipRows .isEmpty ())) {
925+ CreateRelationshipStatementHolder statementHolder = neo4jMappingContext .createStatementForImperativeSimpleRelationshipBatch (
926+ sourceEntity , relationshipDescription , plainRelationshipRows );
927+ statementHolder = statementHolder .addProperty (Constants .NAME_OF_RELATIONSHIP_LIST_PARAM , plainRelationshipRows );
928+ neo4jClient .query (renderer .render (statementHolder .getStatement ()))
929+ .bindAll (statementHolder .getProperties ())
930+ .run ();
931+ } else if (relationshipDescription .hasRelationshipProperties ()) {
932+ if (!relationshipPropertiesRows .isEmpty ()) {
933+ CreateRelationshipStatementHolder statementHolder = neo4jMappingContext .createStatementForImperativeRelationshipsWithPropertiesBatch (false ,
934+ sourceEntity , relationshipDescription , updateRelatedValuesToStore , relationshipPropertiesRows );
935+ statementHolder = statementHolder .addProperty (Constants .NAME_OF_RELATIONSHIP_LIST_PARAM , relationshipPropertiesRows );
936+
937+ neo4jClient .query (renderer .render (statementHolder .getStatement ()))
938+ .bindAll (statementHolder .getProperties ())
939+ .run ();
940+ } else if (!newRelatedValuesToStore .isEmpty ()) {
941+ CreateRelationshipStatementHolder statementHolder = neo4jMappingContext .createStatementForImperativeRelationshipsWithPropertiesBatch (true ,
942+ sourceEntity , relationshipDescription , newRelatedValuesToStore , newRelationshipPropertiesRows );
943+
944+ List <Long > all = new ArrayList <>(neo4jClient .query (renderer .render (statementHolder .getStatement ()))
945+ .bindAll (statementHolder .getProperties ())
946+ .fetchAs (Long .class ).all ());
947+ // assign new ids
948+ for (int i = 0 ; i < all .size (); i ++) {
949+ Long aLong = all .get (i );
950+ assignIdToRelationshipProperties (relationshipContext , newRelatedValuesToStore .get (i ), idProperty , aLong );
951+ }
952+ }
953+ }
886954
887955 relationshipHandler .applyFinalResultToOwner (propertyAccessor );
888956 });
@@ -892,6 +960,12 @@ private <T> T processNestedRelations(
892960 return finalSubgraphRoot ;
893961 }
894962
963+ private void assignIdToRelationshipProperties (NestedRelationshipContext relationshipContext , Object relatedValueToStore , Neo4jPersistentProperty idProperty , Long relationshipInternalId ) {
964+ relationshipContext
965+ .getRelationshipPropertiesPropertyAccessor (relatedValueToStore )
966+ .setProperty (idProperty , relationshipInternalId );
967+ }
968+
895969 private Entity saveRelatedNode (Object entity , NodeDescription <?> targetNodeDescription , PropertyFilter includeProperty , PropertyFilter .RelaxedPropertyPath currentPropertyPath ) {
896970
897971 DynamicLabels dynamicLabels = determineDynamicLabels (entity , (Neo4jPersistentEntity <?>) targetNodeDescription );
0 commit comments