OGM Not deleting relationship on save()

Hi guys,

I'm trying to remove a relationship from an @NodeEntity, when using debugger, the breakpoints indicates that the object isn't containing any relation in outgoingRelations and incomingRelations but when the transaction is comitted, it refills the outgoingRelations with the relation I just removed... I don't understand why, but after a long debug, I found that the OGM never identified that the relations had changed, the only statement being executed in the DB is the one to update the Node body only.

Do you guys have any idea on how to fix that ? here's my code :

//BaseNode.java
public class BaseNode extends BaseGraphObject {
    @Properties
    protected Map<String, Object> attributes;
    @Relationship(type = "RELATION")
    protected List<BaseRelation> outgoingRelations;
    @Relationship(type = "RELATION", direction = Relationship.INCOMING)
    protected List<BaseRelation> incomingRelations;
    protected Long x;
    protected Long y;

    public BaseNode() {
        super();
        this.attributes = new HashMap<>();
        this.outgoingRelations = new ArrayList<>();
        this.incomingRelations = new ArrayList<>();
    }

public List<BaseRelation> getIncomingRelations() {...}
public List<BaseRelation> getOutgoingRelations() {...}
//BaseNodeUtil.java
public static void updateRelations(BaseNode node, Map<Object, Object> json, Session session) throws ApiException {
// [...]
for(int i = 0 ; i < node.getOutgoingRelations().size(); i++) {
        BaseRelation relation = node.getOutgoingRelations().get(i);

        if(!relation.getType().equals(RelationType.GROUP.toString()))
                node.getOutgoingRelations().remove(i);
    }
}
//BaseNodeEditHandler.java
public class BaseNodeEditHandler extends AsyncHandler {
    @Override
    public boolean asyncTask(ApiCompletableFuture api, Context context, Session session) throws Exception {
        BaseDefinition definition = repository.find(Long.parseLong(context.pathParam("id")));

        definition.setIcon(Icon.valueOf(json.get("icon").toString()));
        definition.setX(Long.valueOf(json.get("x").toString()));
        definition.setY(Long.valueOf(json.get("y").toString()));
        BaseNodeUtil.updateRelations(definition, json, session);
        definition.validate();

        definition = repository.createOrUpdate(definition, session);

        return api.success("Definition has been successfully edited", GraphUtil.format(repository.find(definition.getId())));
    }
}

Edit: I also noticed that removing the session param from the createOrUpdate, fixed the problem, but I need it to work in a transaction that I can rollback on Exception

@gerrit.meier Sorry for the ping, but I think you're the only one who can help me on this :/

As the relation I tried to remove is a RelationshipEntity, I directly did session.delete(relation) which is working fine, but if you have the time, I would be glad to understand why my first attempt didn't worked

I wonder what your repository look like, or at least what repository.createOrUpdate(definition, session) and repository.find(Long.parseLong(context.pathParam("id"))) do.
I assume that your are not using only using the Spring Data repository but something else because there is no createOrUpdate method in there.

I assume that the loading of the definition in the BaseNodeEditHandler takes place in another session then the createOrUpdate which makes Neo4j-OGM unaware of the already existing relationships and cannot decide if it needs to get deleted or not.

Here is the createOrUpdate method, its inside a GenericCrud<T> (from the GenericService in this example: https://neo4j.com/docs/ogm-manual/current/tutorial/)

public abstract class GenericCrud<T> implements Crud<T> {
    private static final int DEPTH_LIST = 0;
    private static final int DEPTH_ENTITY = -1;
    protected Session session = HedgeSessionFactory.getInstance().getSession();

    public abstract Class<T> getEntityType();

    @Override
    public T find(Long id) {
        Class<T> entityType = getEntityType();
        return this.session.load(entityType, id, entityType.equals(BaseRelation.class) ? 1 : DEPTH_ENTITY);
    }
    @Override
    public T find(Long id, Session session) {
        Class<T> entityType = getEntityType();
        return session.load(entityType, id, entityType.equals(BaseRelation.class) ? 1 : DEPTH_ENTITY);
    }

    @Override
    public T createOrUpdate(T entity) {
        this.session.save(entity, DEPTH_ENTITY);
        return find(((BaseGraphObject) entity).getId());
    }
    @Override
    public T createOrUpdate(T entity, Session session) {
        session.save(entity, DEPTH_ENTITY);
        return find(((BaseGraphObject) entity).getId());
    }
}

I think my assumption is still valid. the createOrUpdate uses the GenericCrud class' session but the find operates with a provided one. If those Sessions are not the same, they won't share a cache and the one that is responsible for the save does not know about the changes that happened in the entity but only "sees" the current state.

1 Like