@NodeEntity
public class ManagedList {
@Id
@GeneratedValue()
private Long id;
private String name;
@Relationship(type = "CONTAINS")
private List<ListElement> listElement;
}
@NodeEntity
public class ListElement {
@Id
@GeneratedValue()
private Long id;
private int elementOrder;
@Relationship(type = "CONTAINS", direction = Relationship.INCOMING)
private ManagedList managedList;
}
When I add a new ListElement to a ManagedList with my WS, the ListElement in the JSON only has the ManagedList id, not the full object (to avoid sending unecessary informations). So when this JSON is mapped into a ListElement entity, nested ManagedList object only has its id, the other properties like name are == null.
Previously my app used to work with Mysql, and when I saved ListElement this way, it was correctly associated with ManagedList, and ManagedList object still has his name in DB (ManagedList object was not updated in any way in DB when I saved ListElement , no matter what has changed in its properties).
But now with Neo4J DB, when I save ListElement, it also updates the ManagedList object and set the name to null.
Is there any way to specify that when I save ListElement I don't want to update ManagedList, but only use ManagedList id to associate them, like it uses to happen in Mysql? Or this is impossible and I must first get the ManagedList object with the id, set it into my ListElement, and then save?
Short version: I don't think there's a way to exactly accomplish what you want, but the OGM session is smart enough to know that you haven't changed the ManagedList, so if you can load the full ManagedList once and attach it to the ListElements, saving new ListElements will not re-save ManagedList's properties (as far as I understand it)
In JPA cascade types was helping but I am searching similar functionality on Neo4j. I am handling it with writing cypher, however complex objects makes it unpleasant.
There is something upcoming for Spring Data Neo4j 6.1 that solves this problem: Projection based persistence.
This means only properties (and relationships) will get touched on save that are declared as part of the projection.
We have this already in the main branch:
But it does not work, it overwrites all relation data.
My nodes;
@Node("Production")
@Data
public class Production {
@Id
@GeneratedValue(UUIDStringGenerator.class)
private String identity;
@Builder.Default
@Relationship(type = "GENRE_BASED", direction = Direction.OUTGOING)
private List<ProductionGenre> genres = new ArrayList<>();
}
@Node("ProductionGenre")
@Data
public class ProductionGenre {
@Id
@GeneratedValue
private Long identity;
@Builder.Default
@Relationship(type = "TRANSLATED_IN", direction = Direction.OUTGOING)
private List<Translation> names = new ArrayList<>();
}
Normally I was using ReactiveNeo4jRepository but it is also overwriting the data. Can it be related with reactive?
Please be aware that you have to also supply a projection to the template's save method.
neo4jTemplate.saveAs(p, OpenProjection.class);
^^ taken from the example above.
The template describes the limited view on the entity that you want to persist. SDN will than take just its defined properties for the update/save.
I think there is a misunderstanding, I want create relationship between nodes without saving (updating) all nodes.
In my example; I have already saved genres and I am trying to save production with genre relationship only using id of genre. It this case it overwrites genre objects.
Is there any solution to avoid that without writing any cypher?
If you want to do this without any Cypher, you would have at least to fetch all ProductionGenres on the Production to add (or remove) others.
This is needed because SDN does not know about the relationships on Production when you save just with the new one. It assumes strict, that the Java model represents the Graph model and this moment (projections aside). So it will remove the existing relationships first.
And yes, when it processes the relationships it will update the related nodes also.
Because there is no "deep / multi-level projection" and only first level supported, it will also touch the Translations.
Using SDN 7.2.2, I'm observing that both of these template methods appear to modify the entire entity (all properties), rather than modifying only the properties defined in OpenProjection:
Sorry to be dredging up multiple old threads – just curious if I'm doing something wrong, or if I'm misinterpreting the intended behavior of these methods.
No, it does not. The important bit is here spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/Neo4jTemplate.java at e8d506c8c3c4c9190340730b458496aeb125ae37 · spring-projects/spring-data-neo4j · GitHub (and following).
From the projection class, all reachable fields will be derived and used as a field filter to only touch those properties or relationships.
This is true for interface-based projections, not for DTO. Class/DTO projections are still treated as the original entities because they can have fields that are derived from properties, using the @Value(<Spring Expression language expression>) annotation. In this case we cannot determine which properties are really needed. Even though this affects only the read case we decided to use the same logic for the properties detection also for the write case. This is why you may observe this problem on your side.