T save(T instance);
+ /**
+ * Saves an instance of an entity, using the provided predicate to shape the stored graph. One can think of the predicate
+ * as a dynamic projection. If you want to save or update properties of associations (aka related nodes), you must include
+ * the association property as well (meaning the predicate must return {@literal true} for that property, too).
+ *
+ * Be careful when reusing the returned instance for further persistence operations, as it will most likely not be
+ * fully hydrated and without using a static or dynamic projection, you will most likely cause data loss.
+ *
+ * @param instance the entity to be saved. Must not be {@code null}.
+ * @param includeProperty A predicate to determine the properties to save.
+ * @param the type of the entity.
+ * @return the saved instance.
+ * @since 6.3
+ */
+ default T saveAs(T instance, BiPredicate includeProperty) {
+ throw new UnsupportedOperationException();
+ }
+
/**
* Saves an instance of an entity, including the properties and relationship defined by the projected {@code resultType}.
*
@@ -213,6 +233,24 @@ default R saveAs(T instance, Class resultType) {
*/
List saveAll(Iterable instances);
+ /**
+ * Saves several instances of an entity, using the provided predicate to shape the stored graph. One can think of the predicate
+ * as a dynamic projection. If you want to save or update properties of associations (aka related nodes), you must include
+ * the association property as well (meaning the predicate must return {@literal true} for that property, too).
+ *
+ * Be careful when reusing the returned instances for further persistence operations, as they will most likely not be
+ * fully hydrated and without using a static or dynamic projection, you will most likely cause data loss.
+ *
+ * @param instances the instances to be saved. Must not be {@code null}.
+ * @param includeProperty A predicate to determine the properties to save.
+ * @param the type of the entity.
+ * @return the saved instances.
+ * @since 6.3
+ */
+ default List saveAllAs(Iterable instances, BiPredicate includeProperty) {
+ throw new UnsupportedOperationException();
+ }
+
/**
* Saves an instance of an entity, including the properties and relationship defined by the project {@code resultType}.
*
diff --git a/src/main/java/org/springframework/data/neo4j/core/Neo4jTemplate.java b/src/main/java/org/springframework/data/neo4j/core/Neo4jTemplate.java
index c11e42eba5..045f649d57 100644
--- a/src/main/java/org/springframework/data/neo4j/core/Neo4jTemplate.java
+++ b/src/main/java/org/springframework/data/neo4j/core/Neo4jTemplate.java
@@ -32,6 +32,7 @@
import java.util.Optional;
import java.util.Set;
import java.util.function.BiFunction;
+import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -343,6 +344,16 @@ public T save(T instance) {
return saveImpl(instance, Collections.emptyMap(), null);
}
+ @Override
+ public T saveAs(T instance, BiPredicate includeProperty) {
+
+ if (instance == null) {
+ return null;
+ }
+
+ return saveImpl(instance, TemplateSupport.computeIncludedPropertiesFromPredicate(this.neo4jMappingContext, instance.getClass(), includeProperty), null);
+ }
+
@Override
public R saveAs(T instance, Class resultType) {
@@ -451,10 +462,10 @@ private DynamicLabels determineDynamicLabels(T entityToBeSaved, Neo4jPersist
@Override
public List saveAll(Iterable instances) {
- return saveAllImpl(instances, Collections.emptyMap());
+ return saveAllImpl(instances, Collections.emptyMap(), null);
}
- private List saveAllImpl(Iterable instances, Map includedProperties) {
+ private List saveAllImpl(Iterable instances, @Nullable Map includedProperties, @Nullable BiPredicate includeProperty) {
Set> types = new HashSet<>();
List entities = new ArrayList<>();
@@ -469,13 +480,19 @@ private List saveAllImpl(Iterable instances, Map 1;
Class> domainClass = types.iterator().next();
+
+ Map pps = includeProperty == null ?
+ includedProperties :
+ TemplateSupport.computeIncludedPropertiesFromPredicate(this.neo4jMappingContext, domainClass,
+ includeProperty);
+
Neo4jPersistentEntity> entityMetaData = neo4jMappingContext.getRequiredPersistentEntity(domainClass);
if (heterogeneousCollection || entityMetaData.isUsingInternalIds() || entityMetaData.hasVersionProperty()
|| entityMetaData.getDynamicLabelsProperty().isPresent()) {
log.debug("Saving entities using single statements.");
NestedRelationshipProcessingStateMachine stateMachine = new NestedRelationshipProcessingStateMachine(neo4jMappingContext);
- return entities.stream().map(e -> saveImpl(e, includedProperties, stateMachine)).collect(Collectors.toList());
+ return entities.stream().map(e -> saveImpl(e, pps, stateMachine)).collect(Collectors.toList());
}
class Tuple3 {
@@ -497,7 +514,7 @@ class Tuple3 {
// Save roots
@SuppressWarnings("unchecked") // We can safely assume here that we have a humongous collection with only one single type being either T or extending it
Function> binderFunction = neo4jMappingContext.getRequiredBinderFunctionFor((Class) domainClass);
- binderFunction = TemplateSupport.createAndApplyPropertyFilter(includedProperties, entityMetaData, binderFunction);
+ binderFunction = TemplateSupport.createAndApplyPropertyFilter(pps, entityMetaData, binderFunction);
List