0

I am following the Neo4j OGM guide at - http://neo4j.com/docs/ogm/java/stable/

For Relationship entities, we need to have a start node and an end node. I've modified the example a little bit (to make it simpler like this) -

@NodeEntity
public class Student extends Entity {
    private String name;

    @Relationship(type= "ENROLLED")
    private Enrollment enrollment = new Enrollment();

    public String getName() {
        return name;
    }

    public Enrollment getEnrollment() {
        return enrollment;
    }

    public void setEnrollment(Enrollment enrollment) {
        this.enrollment = enrollment;
    }

    public Student() {
    }

    public Student(String name) {
        this.name = name;
    }
}

@NodeEntity
public class Course extends Entity {
    private String name;

    public String getName() {
        return name;
    }

    @Relationship(type= "ENROLLED", direction= Relationship.INCOMING)
    private Enrollment enrollment = new Enrollment();

    public Enrollment getEnrollment() {
        return enrollment;
    }

    public void setEnrollment(Enrollment enrollment) {
        this.enrollment = enrollment;
    }

    public Course() {
    }

    public Course(String name) {
        this.name = name;
    }
}

@RelationshipEntity(type = "ENROLLED")
public class Enrollment extends Entity {
    @StartNode
    private Student student;

    @EndNode
    private Course course;

    private Date enrolledDate;

    public Student getStudent() {
        return student;
    }

    public Course getCourse() {
        return course;
    }

    public Date getEnrolledDate() {
        return enrolledDate;
    }

    public Enrollment() {
    }

    public Enrollment(Student student, Course course, Date enrolledDate) {
        this.student = student;
        this.course = course;
        this.enrolledDate = enrolledDate;
    }
}

Now when I try to save this in Neo4j, it works fine. However in my scenario, the type of StartNode and EndNode objects are the same -

@NodeEntity
public class MyObject extends Entity {
    private String name;

    @Relationship(type="CONNECTION")
    private MyConnection startConnection = new MyConnection();

    @Relationship(type="CONNECTION", direction= Relationship.INCOMING)
    private MyConnection endConnection = new MyConnection();

    public String getName() {
        return name;
    }

    public MyConnection getStartConnection() {
        return startConnection;
    }

    public void setStartConnection(MyConnection myConnection) {
        this.startConnection = myConnection;
    }

    public MyConnection getEndConnection() {
        return endConnection;
    }

    public void setEndConnection(MyConnection endConnection) {
        this.endConnection = endConnection;
    }

    public MyObject() {
        super();
    }

    public MyObject(String name) {
        super();
        this.name = name;
    }
}

@RelationshipEntity(type="CONNECTION")
public class MyConnection extends Entity {
    @StartNode
    private MyObject start;

    @EndNode
    private MyObject end;

    private String name;

    public String getName() {
        return name;
    }

    public MyConnection() {
        super();
    }

    public MyConnection(MyObject start, MyObject end, String name) {
        super();
        this.start = start;
        this.end = end;
        this.name = name;
    }
}

When I try to save these using -

public class Main {

    public static void main(String[] args) {
        Session session = Neo4jSessionFactory.getInstance().getNeo4jSession();

        Student s = new Student("manoj");
        Course c = new Course("physics");
        Enrollment e = new Enrollment(s, c, new Date());
        s.setEnrollment(e);
        c.setEnrollment(e);

        MyObject startObj = new MyObject("Start Object");
        MyObject endObj = new MyObject("End Object");
        MyConnection conn = new MyConnection(startObj, endObj, "Connection");
        startObj.setStartConnection(conn);
        endObj.setEndConnection(conn);

        try(Transaction tx = session.beginTransaction()) {
            session.save(s);
            session.save(c);
            session.save(e);

            session.save(startObj);
            session.save(endObj);
            session.save(conn);

            tx.commit();
        }
    }
}

The student, course and enrollments objects get saved, but the two MyObject and the MyConnection objects don't get saved and I get the following exception -

Exception in thread "main" java.lang.NullPointerException
    at org.neo4j.ogm.metadata.MetaData.classInfo(MetaData.java:76)
    at org.neo4j.ogm.mapper.EntityGraphMapper.mapRelationshipEntity(EntityGraphMapper.java:389)
    at org.neo4j.ogm.mapper.EntityGraphMapper.link(EntityGraphMapper.java:319)
    at org.neo4j.ogm.mapper.EntityGraphMapper.mapEntityReferences(EntityGraphMapper.java:262)
    at org.neo4j.ogm.mapper.EntityGraphMapper.mapEntity(EntityGraphMapper.java:154)
    at org.neo4j.ogm.mapper.EntityGraphMapper.mapRelatedEntity(EntityGraphMapper.java:528)
    at org.neo4j.ogm.mapper.EntityGraphMapper.mapRelationshipEntity(EntityGraphMapper.java:420)
    at org.neo4j.ogm.mapper.EntityGraphMapper.link(EntityGraphMapper.java:319)
    at org.neo4j.ogm.mapper.EntityGraphMapper.mapEntityReferences(EntityGraphMapper.java:262)
    at org.neo4j.ogm.mapper.EntityGraphMapper.mapEntity(EntityGraphMapper.java:154)
    at org.neo4j.ogm.mapper.EntityGraphMapper.map(EntityGraphMapper.java:87)
    at org.neo4j.ogm.session.delegates.SaveDelegate.save(SaveDelegate.java:65)
    at org.neo4j.ogm.session.delegates.SaveDelegate.save(SaveDelegate.java:41)
    at org.neo4j.ogm.session.Neo4jSession.save(Neo4jSession.java:370)
    at neo4j.ogm.ex.Main.main(Main.java:37)

Can you please help me to solve this - 1) Is it necessary that the StartNode and EndNode objects be of different types? 2) Is there some problem with my code or is it a shortcoming of the Neo4j OGM?

Thanks in advance,

Manoj.

Update after trying Luanne's suggestion -

Thanks Luanne. I tried your suggestion, though I had to specify the URL differently. I used - http://m2.neo4j.org/content/repositories/snapshots because by default it used https and I was getting some security exception and this dependency was not getting downloaded.

Anyways, with the 1.1.1-SNAPSHOT version, I still get the below error -

Exception in thread "main" java.lang.NullPointerException
    at org.neo4j.ogm.metadata.MetaData.classInfo(MetaData.java:80)
    at org.neo4j.ogm.mapper.EntityGraphMapper.haveRelationEndsChanged(EntityGraphMapper.java:391)
    at org.neo4j.ogm.mapper.EntityGraphMapper.getRelationshipBuilder(EntityGraphMapper.java:362)
    at org.neo4j.ogm.mapper.EntityGraphMapper.link(EntityGraphMapper.java:325)
    at org.neo4j.ogm.mapper.EntityGraphMapper.mapEntityReferences(EntityGraphMapper.java:276)
    at org.neo4j.ogm.mapper.EntityGraphMapper.mapEntity(EntityGraphMapper.java:157)
    at org.neo4j.ogm.mapper.EntityGraphMapper.mapRelatedEntity(EntityGraphMapper.java:571)
    at org.neo4j.ogm.mapper.EntityGraphMapper.mapRelationshipEntity(EntityGraphMapper.java:473)
    at org.neo4j.ogm.mapper.EntityGraphMapper.link(EntityGraphMapper.java:329)
    at org.neo4j.ogm.mapper.EntityGraphMapper.mapEntityReferences(EntityGraphMapper.java:276)
    at org.neo4j.ogm.mapper.EntityGraphMapper.mapEntity(EntityGraphMapper.java:157)
    at org.neo4j.ogm.mapper.EntityGraphMapper.map(EntityGraphMapper.java:90)
    at org.neo4j.ogm.session.delegates.SaveDelegate.save(SaveDelegate.java:67)
    at org.neo4j.ogm.session.delegates.SaveDelegate.save(SaveDelegate.java:43)
    at org.neo4j.ogm.session.Neo4jSession.save(Neo4jSession.java:376)
    at neo4j.ogm.ex.Main.main(Main.java:37)
Luanne
  • 19,145
  • 1
  • 39
  • 51
mmwaikar
  • 655
  • 4
  • 14

2 Answers2

1

Is your intent to model MyObject to contain a single outgoing relationship type CONNECTION and a single incoming relationship type CONNECTION where each relationship has a property name?

Sample subgraph

So if we're looking at EndObject, then startConnection is the relationship with name conn2 and endConnection is the relationship with name conn1?

If so, we might have a problem here. https://jira.spring.io/browse/DATAGRAPH-728

Update: This wasn't a bug after all. The problem is the initialization you have in MyObject:

@Relationship(type="CONNECTION")
private MyConnection startConnection = new MyConnection();

@Relationship(type="CONNECTION", direction= Relationship.INCOMING)
private MyConnection endConnection = new MyConnection();

You've initialized both startConnection and endConnection to an invalid relationship entity i.e. one with no start and no end node.

In your test, you set the startConnection on startObj but not the end. In effect, the endConnection is represented by the invalid relationship entity.

Remove the initialization and it should work as you expect.

Luanne
  • 19,145
  • 1
  • 39
  • 51
0

Thanks Luanne for your answer / suggestion. I came across your excellent article The essence of Spring Data Neo4j 4 and realized that my need is very similar to the Ingredient-Pairing example (mentioned in your article). For me, there can be a connection (MyConnection) between two objects (MyObject). So my modified code looks like this -

package neo4j.ogm.ex.domain;

import java.util.HashSet;
import java.util.Set;

import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.Relationship;

@NodeEntity
public class MyObject extends Entity {
    private String name;

    @Relationship(type="CONNECTION", direction= Relationship.UNDIRECTED)
    private Set<MyConnection> connections = new HashSet<>();

    public String getName() {
        return name;
    }

    public Set<MyConnection> getConnections() {
        return connections;
    }

    public void addConnection(MyConnection myConnection) {
        myConnection.getStart().getConnections().add(myConnection);
        myConnection.getEnd().getConnections().add(myConnection);
    }

    public MyObject() {
        super();
    }

    public MyObject(String name) {
        super();
        this.name = name;
    }
}

package neo4j.ogm.ex.domain;

import org.neo4j.ogm.annotation.EndNode;
import org.neo4j.ogm.annotation.RelationshipEntity;
import org.neo4j.ogm.annotation.StartNode;

@RelationshipEntity(type="CONNECTION")
public class MyConnection extends Entity {
    @StartNode
    private MyObject start;

    @EndNode
    private MyObject end;

    private String name;

    public String getName() {
        return name;
    }

    public MyObject getStart() {
        return start;
    }

    public MyObject getEnd() {
        return end;
    }

    public MyConnection() {
        super();
    }

    public MyConnection(MyObject start, MyObject end, String name) {
        super();
        this.start = start;
        this.end = end;
        this.name = name;
    }
}

And it works perfectly. I'll also try out your other suggestion of removing the invalid initialization.

Thanks again,

Manoj.

mmwaikar
  • 655
  • 4
  • 14