Search code examples
javaspring-bootspring-data-neo4jspring-data-neo4j-4neo4j-ogm

Unable to change or delete relationship between nodes with Neo4j OGM and Spring Boot Data


I’m having problems with removing or changing existing relationships between two nodes using Spring Boot (v1.5.10) and Neo4j OGM (v2.1.6, with Spring Data Neo4j v4.2.10). I have found a few traces of similar problems reported by people using older Neo4j OGM versions (like 1.x.something) but, I think, it should be long gone with 2.1.6 and latest Spring Boot v1 release. Therefore, I don’t know whether that’s a regression or I am not using the API in the correct way.

So, my node entities are defined as follows:

@NodeEntity
public class Task {

    @GraphId
    private Long id;
    private String key;

    @Relationship(type = "HAS_STATUS")
    private Status status;

    public Task() {
    }

    public Long getId() {
        return id;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public Status getStatus() {
        return status;
    }

    public void setStatus(Status status) {
        this.status = status;
    }
}

@NodeEntity
public class Status {

    @GraphId
    private Long id;
    private String key;

    @Relationship(type = "HAS_STATUS", direction = Relationship.INCOMING)
    private Set<Task> tasks;

    public Status() {
        tasks = new HashSet<>();
    }

    public Long getId() {
        return id;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public Set<Task> getTasks() {
        return tasks;
    }

    public void addTask(Task task) {
        tasks.add(task);
    }

    public boolean removeTask(Task task) {
        if(this.hasTask(task)) {
            return this.tasks.remove(task);
        }
        return false;
    }

    public boolean hasTask(Task task) {
        return this.tasks.contains(task);
    }
}

This is how it can be represented in Cypher-like style:

(t:Task)-[:HAS_STATUS]->(s:Status)

Here is the Service method that tries to update the task’s statuses:

public void updateTaskStatus(Task task, Status status) {
    Status prevStatus = task.getStatus();
    if(prevStatus != null) {
        prevStatus.removeTask(task);
        this.saveStatus(prevStatus);
    }

    task.setStatus(status);
    if(status != null) {
        status.addTask(task);
        this.saveStatus(status);
    }

    this.saveTask(task);
}

As a result of an update, I get two HAS_STATUS relationships to two different Status nodes (old and new one), or, if I try to remove existing relationship, nothing happens (the old relationship remains)

The complete demo that illustrates the problem can be found on the GitHub here:

https://github.com/ADi3ek/neo4j-spring-boot-demo

Any clues or suggestions that can help me resolve that issue are more than welcome! :-)


Solution

  • If you would annotate your commands with @Transactional (because this is where the entities got loaded) it will work. The underlying problem is that if you load an entity it will open a new transaction with a new session (context), find the relationships and cache the information about them in the context. The transaction (and session) will then get closed because the operation is done.

    The subsequent save/update does not find an opened transaction and will then, as a consequence, open a new one (with new session/ session context). When executing the save it looks at the entity in the current state and does not see the old relationship anymore.

    Two answers:

    1. it is a bug ;(

    EDIT: After a few days thinking about this, I revert the statement above. It is not a real bug but more like unexpected behaviour. There is nothing wrong in SDN. It uses two sessions (one for each operation) to do the work and since nobody told it to do the work in one transaction the loaded object is not 'managed' or 'attached' (as in JPA) to a session context.

    1. you can work around this by using an explicit transaction for your unit of work

    I will close the issue for SDN and try to migrate all the information to one of the two issues on GitHub because it is a OGM problem.