47
47
import org .neo4j .cypherdsl .core .Statement ;
48
48
import org .neo4j .cypherdsl .core .renderer .Renderer ;
49
49
import org .neo4j .driver .summary .SummaryCounters ;
50
+ import org .neo4j .driver .types .Entity ;
50
51
import org .reactivestreams .Publisher ;
51
52
import org .springframework .beans .BeansException ;
52
53
import org .springframework .beans .factory .BeanClassLoaderAware ;
@@ -307,7 +308,7 @@ public <T, R> Mono<R> saveAs(T instance, Class<R> resultType) {
307
308
308
309
private <T > Mono <T > saveImpl (T instance , @ Nullable List <PropertyDescriptor > includedProperties ) {
309
310
310
- Neo4jPersistentEntity entityMetaData = neo4jMappingContext .getPersistentEntity (instance .getClass ());
311
+ Neo4jPersistentEntity <?> entityMetaData = neo4jMappingContext .getPersistentEntity (instance .getClass ());
311
312
boolean isNewEntity = entityMetaData .isNew (instance );
312
313
return Mono .just (instance ).flatMap (eventSupport ::maybeCallBeforeBind )
313
314
.flatMap (entityToBeSaved -> determineDynamicLabels (entityToBeSaved , entityMetaData )).flatMap (t -> {
@@ -331,9 +332,11 @@ private <T> Mono<T> saveImpl(T instance, @Nullable List<PropertyDescriptor> incl
331
332
});
332
333
}
333
334
334
- Mono <Long > idMono = this .neo4jClient .query (() -> renderer .render (cypherGenerator .prepareSaveOf (entityMetaData , dynamicLabels )))
335
- .bind (entityToBeSaved ).with (binderFunction )
336
- .fetchAs (Long .class ).one ()
335
+ Mono <Entity > idMono = this .neo4jClient .query (() -> renderer .render (cypherGenerator .prepareSaveOf (entityMetaData , dynamicLabels )))
336
+ .bind (entityToBeSaved )
337
+ .with (binderFunction )
338
+ .fetchAs (Entity .class )
339
+ .one ()
337
340
.switchIfEmpty (Mono .defer (() -> {
338
341
if (entityMetaData .hasVersionProperty ()) {
339
342
return Mono .error (() -> new OptimisticLockingFailureException (OPTIMISTIC_LOCKING_ERROR_MESSAGE ));
@@ -342,14 +345,18 @@ private <T> Mono<T> saveImpl(T instance, @Nullable List<PropertyDescriptor> incl
342
345
}));
343
346
344
347
PersistentPropertyAccessor <T > propertyAccessor = entityMetaData .getPropertyAccessor (entityToBeSaved );
345
- return idMono .doOnNext (internalId -> {
348
+ return idMono .doOnNext (newOrUpdatedNode -> {
346
349
if (entityMetaData .isUsingInternalIds ()) {
347
- propertyAccessor .setProperty (entityMetaData .getRequiredIdProperty (), internalId );
350
+ propertyAccessor .setProperty (entityMetaData .getRequiredIdProperty (), newOrUpdatedNode . id () );
348
351
}
349
- }).flatMap (internalId -> processRelations (entityMetaData , instance , internalId , propertyAccessor , isNewEntity , includeProperty ));
352
+ TemplateSupport .updateVersionPropertyIfPossible (entityMetaData , propertyAccessor , newOrUpdatedNode );
353
+ }).map (Entity ::id )
354
+ .flatMap (internalId -> processRelations (entityMetaData , instance , internalId , propertyAccessor , isNewEntity , includeProperty ));
350
355
});
351
356
}
352
357
358
+
359
+
353
360
private <T > Mono <Tuple2 <T , DynamicLabels >> determineDynamicLabels (T entityToBeSaved ,
354
361
Neo4jPersistentEntity <?> entityMetaData ) {
355
362
return entityMetaData .getDynamicLabelsProperty ().map (p -> {
@@ -362,7 +369,7 @@ private <T> Mono<Tuple2<T, DynamicLabels>> determineDynamicLabels(T entityToBeSa
362
369
363
370
if (entityMetaData .hasVersionProperty ()) {
364
371
runnableQuery = runnableQuery
365
- .bind ((Long ) propertyAccessor .getProperty (entityMetaData .getRequiredVersionProperty ()) - 1 )
372
+ .bind ((Long ) propertyAccessor .getProperty (entityMetaData .getRequiredVersionProperty ()))
366
373
.to (Constants .NAME_OF_VERSION_PARAM );
367
374
}
368
375
@@ -775,20 +782,30 @@ private <T> Mono<T> processNestedRelations(Neo4jPersistentEntity<?> sourceEntity
775
782
.flatMap (newRelatedObject -> {
776
783
Neo4jPersistentEntity <?> targetEntity = neo4jMappingContext .getPersistentEntity (relatedObjectBeforeCallbacksApplied .getClass ());
777
784
778
- Mono <Long > queryOrSave ;
785
+ Mono <Tuple2 <Long , Long >> queryOrSave ;
786
+ long noVersion = Long .MIN_VALUE ;
779
787
if (stateMachine .hasProcessedValue (relatedValueToStore )) {
780
- queryOrSave = Mono .just (stateMachine .getInternalId (relatedObjectBeforeCallbacksApplied ));
788
+ queryOrSave = Mono .just (stateMachine .getInternalId (relatedObjectBeforeCallbacksApplied ))
789
+ .map (id -> Tuples .of (id , noVersion ));
781
790
} else {
782
- queryOrSave = saveRelatedNode (newRelatedObject , targetEntity );
791
+ queryOrSave = saveRelatedNode (newRelatedObject , targetEntity )
792
+ .map (entity -> Tuples .of (entity .id (), targetEntity .hasVersionProperty () ?
793
+ entity .get (targetEntity .getVersionProperty ().getPropertyName ())
794
+ .asLong () :
795
+ noVersion ));
783
796
}
784
- return queryOrSave .flatMap (relatedInternalId -> {
797
+ return queryOrSave .flatMap (idAndVersion -> {
798
+ long relatedInternalId = idAndVersion .getT1 ();
785
799
stateMachine .markValueAsProcessed (relatedValueToStore , relatedInternalId );
786
800
// if an internal id is used this must be set to link this entity in the next iteration
787
801
PersistentPropertyAccessor <?> targetPropertyAccessor = targetEntity .getPropertyAccessor (newRelatedObject );
788
802
if (targetEntity .isUsingInternalIds ()) {
789
803
targetPropertyAccessor .setProperty (targetEntity .getRequiredIdProperty (), relatedInternalId );
790
804
stateMachine .markValueAsProcessedAs (newRelatedObject , targetPropertyAccessor .getBean ());
791
805
}
806
+ if (targetEntity .hasVersionProperty () && idAndVersion .getT2 () != noVersion ) {
807
+ targetPropertyAccessor .setProperty (targetEntity .getVersionProperty (), idAndVersion .getT2 ());
808
+ }
792
809
793
810
Object idValue = idProperty != null
794
811
? relationshipContext
@@ -855,18 +872,19 @@ private <T> Mono<T> processNestedRelations(Neo4jPersistentEntity<?> sourceEntity
855
872
856
873
}
857
874
858
- private <Y > Mono <Long > saveRelatedNode (Object relatedNode , Neo4jPersistentEntity <?> targetNodeDescription ) {
875
+ private <Y > Mono <Entity > saveRelatedNode (Object relatedNode , Neo4jPersistentEntity <?> targetNodeDescription ) {
859
876
860
877
return determineDynamicLabels ((Y ) relatedNode , targetNodeDescription )
861
878
.flatMap (t -> {
862
879
Y entity = t .getT1 ();
863
- Class <Y > entityType = (Class <Y >) (( Neo4jPersistentEntity <?>) targetNodeDescription ) .getType ();
880
+ Class <Y > entityType = (Class <Y >) targetNodeDescription .getType ();
864
881
DynamicLabels dynamicLabels = t .getT2 ();
865
882
866
883
return neo4jClient
867
884
.query (() -> renderer .render (cypherGenerator .prepareSaveOf (targetNodeDescription , dynamicLabels )))
868
885
.bind ((Y ) entity ).with (neo4jMappingContext .getRequiredBinderFunctionFor (entityType ))
869
- .fetchAs (Long .class ).one ();
886
+ .fetchAs (Entity .class )
887
+ .one ();
870
888
}).switchIfEmpty (Mono .defer (() -> {
871
889
if (targetNodeDescription .hasVersionProperty ()) {
872
890
return Mono .error (() -> new OptimisticLockingFailureException (OPTIMISTIC_LOCKING_ERROR_MESSAGE ));
0 commit comments