Neo4j - Unable to create Relationship entities - java

I am trying to insert the relationships between two nodes in Neo4j. I am using the Neo4J(2.1.8 Community) & spring-data-neo4j(3.3.0.RELEASE).
I am using trying to create the Employee-Manager relationship. This relationship entity is Report. (Both class are given below)
But when I am trying to save the relation ship
Employee manager = new Employee("Dev_manager", "Management");
Employee developer = new Employee("Developer", "Development");
developer.setReportsTo(manager);
developer.relatedTo(manager, "REPORTS_TO")
employeeRepository.save(developer);
I am getting exception as
Exception in thread "main" org.springframework.dao.DataRetrievalFailureException: RELATIONSHIP[0] has no property with propertyKey="type".; nested exception is org.neo4j.graphdb.NotFoundException: RELATIONSHIP[0] has no property with propertyKey="type".
Can any one please help me that what is exactly wrong in this code.
The same code works after I change the type of relations in Employee as
#RelatedToVia(type = "REPORT_TO", elementClass = Report.class, direction = Direction.INCOMING)
Note: I am using this reference for this tutorial.
Employee.java class
#NodeEntity
public class Employee {
#GraphId
private Long id;
private String name;
private String department;
#Fetch
#RelatedTo(type = "REPORTS_TO")
private Employee reportsTo; //Employee reports to some employee (i.e. Manager).
#Fetch
#RelatedTo(type = "REPORTS_TO", direction = Direction.INCOMING)
Set<Employee> directReport; //All the employees who reports to this particular this employee.
#Fetch
#RelatedToVia(type = "REPORTS_TO", elementClass = Report.class, direction = Direction.INCOMING)
Set<Report> relations = new HashSet<Report>(); // All the incoming relationship entities.
//*** Constructor, Getter-setters and other methods...
}
Report.java class
#RelationshipEntity(type = "REPORTS_TO")
public class Report {
#GraphId
private Long id;
private String title;
#Fetch
#StartNode
private Employee child;
#Fetch
#EndNode
private Employee parent;
//*** Constructor, Getter-setters and other methods...
}
**Update: **
I have created 2 relations using this class structure. And I got the below result.
It looks like it creates 2 relations between the node. 1 is empty relation using reportsTo(i.e. REPORTS_TO) and another relation using the relations(i.e. REPORT_TO). Can anyone please update why this is happening?

What's the different between: relations and directReport?
I think SDN is just confused with all the duplicate listing of relationships?
Esp. if they are once declared as light relationships without type and once as relationship-entities.
I think for this case it is much clearer and easier to use
template.createRelationshipBetween(employee, manager, "REPORTS_TO");
Or just create, populate and save the relationship-entity Report.
Otherwise you have to make sure that all collections on all sides are consistent with each other.

Related

Spring and Hibernate mapping issue One-to-Many

I have a form that tracks employee projects and the fields are:
employee name (the project requestor), employee assigned to complete the task, task description, and a field with the option to reassign the task to someone else.
I have a SQL employee table with the name and email of all employees. Then I have a table for the projects. I have it set up so all three columns refer to the employee table via Foreign keys.
I'm experiencing a mapping issue with Spring JPA. One project can have many employees because there are three employee related fields. Is this right?
Or should it be a many to many because an employee can have many projects and each project could have three employess stored (same or different) that it should be a many to many relationship.
The one to many seems right but then also is confusing because there will only be one employee listed in the employee name, one in the assigned to and one in the reassigned to field. Is one-to-many right?
public class ProjectTracker {
#ManyToMany
#JoinColumn(name = "employeeName")
private Employee employeeName;
private String taskDetails;
#ManyToMany
#JoinColumn(name = "employeeName")
private Employee assignedTo;
#ManyToMany
#JoinColumn(name = "employeeName")
private Employee reassignedTo;
}
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "employeeId")
private Long employeeId;
private String employeeName;
private String emailAddress;
private Boolean isAssignee=false;
private Boolean isRequestor=true;
}
One project can have many employees because there are three employee related
fields. Is this right?
No.
The number of fields is not relevant. Here you have three different associations. Given an employee you need an association for the project they have requested, an association for the projects the've been assigned to and an association for the projects they've been reassigned to. All these associations are all one-to-many because an employee can be the requester for many different projects and the same is true for the other roles.
A single project, a row on the db, can only have one requester, one employee it has been assigned to and one person it has been reassigned to. Each one of them is a many-to-one.
ProjectTracker should probably look something like:
public class ProjectTracker {
#Id
private Long id;
#ManyToOne
#JoinColumn(name = "requestedBy_id")
private Employee employeeName;
private String taskDetails;
#ManyToOne
#JoinColumn(name = "assignedTo_id")
private Employee assignedTo;
#ManyToOne
#JoinColumn(name = "reassignedTo_id")
private Employee reassignedTo;
}
Given a project tracker, you should have all the information you need.
I would suggest to read the Hibernate ORM documentation chapter about associations. It contains many examples of all the different mappings.

How to store string lists in database without a join table?

I want to store a List<String> in a postgres DB.
#ElementCollection
private List<String> products;
Hibernate will therefore create a join table. Is it possible to prevent this?
One workaround would be to create an explicit class with bidirectional mapping as follows:
#Entity
public class MainEntity {
#OneToMany(mappedBy = "main")
private List<Product> products;
}
#Entity
public class Product {
#Id
private long id;
#ManyToOne
private MainEntity main;
private String text;
}
But I feel this is a bit over the top for just storing a string list, isn't it?
If you don't find anything better, try this:
Mark your List with #Transient so that it is never persisted directly.
Prepare additional field for persisting your list, of a type that is "persistable" directly by JPA (concatenated, delimetered String seems to be quite natural).
Use methods annotated with #PostLoad and #PrePersist to move data between those two fields, converting from List to String and back.
i'm not sure but could you remove :
#ManyToOne
private MainEntity main;
in class product.
I think it might works properly without this.
Do you want to handle your list from MainEntity or from Product?

Hibernate - Save Child if not persist

i have a threading hibernate problem.
I have a ManyToOne Relation with a track whitch include two city objects.
Through the thread it is possible that my trackDao get many trackEntitys to persist witch include the same city (zipcode is unique).
After the trackDao persist the first entity i get a "duplicate entity exception".
Is it possible to configure via annotaition to force a get bevore save to get the existing childId and resume parrent persisting?
Thx
thx for your time.
I try a manual get before i try to save a city to get a possibly already existing city (with the same zipcode) from db.
Now a trip includes two cities.
One city witch is allready stored in db (objectId != null)
and an other city witch have to get persist (objectId == null)
Although hibernate now have to know that city1 is already persist an only city2 have to get persist in city- table i get a "duplicate entity exception". Hibernate will store the existing city again.
To slove that I overrive the generic save method of my tripDao and handle the city persisting manual. -> just save city if city.getId() == null.
So I get a not stored trip object witch includes two already stored citys. If i try to persist that trip object I get the hibernate message
"Error while persist trackjavax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: de....CityEntity"
I belive I had something configured totaly wrong.
Pls help.
I dont want to overrive the save method and store realations manualy. In my opinion that have to works by default, or not?
#Entity
#Table(name = "tracks", uniqueConstraints = #UniqueConstraint(columnNames = { "city1_id", "city2_id" }))
#SessionScoped
public class TrackEntity extends BaseEntity {
private static final long serialVersionUID = 4696847902782174082L;
#ManyToOne(targetEntity = CityEntity.class, fetch = FetchType.EAGER, cascade = CascadeType.PERSIST, optional=false)
#JoinColumn(name="city1_id")
private CityEntity city1;
#ManyToOne(targetEntity = CityEntity.class, fetch = FetchType.EAGER, cascade = CascadeType.PERSIST, optional=false)
#JoinColumn(name="city2_id", referencedColumnName="id")
private CityEntity city2;
private Integer distance;
#Entity
#Table(name = "cities")
#SessionScoped
public class CityEntity extends BaseEntity {
private static final long serialVersionUID = 8823384603378561475L;
private String name;
#Column(unique=true)
private String zipcode;
private String latitude;
private String longitude;
I think a track has two identical city . I had a same problem with Order ->* Product. But this was a bug in application where a Order can not have duplicate product. I don't know what is allowed in your case but I suggest you to Investigate City class and look for equals method. If you have java.util.List in Track class to hold cities then it is possible that that list have two element of same object (==).

Many-to-one unidirectional relation in DataNucleus

For the context, client-side I use the MVP pattern, so the view with the One list knows only the ID, and when my new Many is received on the server, I want to be able to just update the One's foreign key, with a "setOneId" or an empty One object with an ID set to the wanted value.
So I try to create a many-to-one unidirectional in DataNucleus, and I'm struggling a bit. I'm ok to use JDO or JPA, I don't really care. In JPA, I tried this :
#Entity
public class Many {
#Id
String id;
#ManyToOne
#Join(name = "idOne")
One one;
}
#Entity
public class One {
#Id
String id;
}
It's almost what I want. The one-to-many is created but with a join table. I want to have a direct relation. And when I insert/update a Many, I don't want to insert/update the related One, just update the idOne with the good id in my Many object.
I found this blogpost, but it's with Hibernate, and I think it still use a join table :
#Entity
public class Many {
#Id
public String id;
#Column(name="idOne")
private String idOne;
#ManyToOne
#JoinColumn(name="idOne", nullable=false, insertable=false, updatable=false)
private One one;
}
I tried it, but I got exactly this error.
I don't understand how I am struggling with that. My goal is to have a table that keep some reference data (like a list of country as the class One), and a list of "working item" (like a town as the class Many) that I create/update without create/update the reference data, just its foreign key in the Many object.
If its a unidirectional association, and Many is the owning side (as per your second example), you are heading in the wrong direction. It doesn't make much sense to delegate the update and insert responsibility on the owning side of a unidirectional relationship (as done with the insertable=false and updateable=false).
EDIT: updated answer
So what you want is a many-to-one, with a foreign key column on the owning side. Try this
#Entity
public class Many {
#Id
String id;
#ManyToOne
#JoinColumn(name = "foreignKeyColumn")
One one;
}
#Entity
public class A {
#Id
String id;
#OneToOne(cascade=CascadeType.ALL)
B b;
}
#Entity
public class B {
#Id
String id;
}
and then if you persisted initial objects as
tx.begin();
A a = new A("FirstA");
B b1 = new B("FirstB");
B b2 = new B("SecondB");
a.setB(b1);
em.persist(a);
em.persist(b2);
tx.commit();
... (some time later)
tx.begin();
A a = em.find(A.class, "FirstA");
B b2 = em.getReference(B.class, "SecondB");
// update the B in A to the second one
a.setB(b2);
tx.commit();
This updates the FK between A and B. Can't get simpler

GAE Datastore: persisting referenced objects

I am trying to persist Java objects to the GAE datastore.
I am not sure as to how to persist object having ("non-trivial") referenced object.
That is, assume I have the following.
public class Father {
String name;
int age;
Vector<Child> offsprings; //this is what I call "non-trivial" reference
//ctor, getters, setters...
}
public class Child {
String name;
int age;
Father father; //this is what I call "non-trivial" reference
//ctor, getters, setters...
}
The name field is unique in each type domain, and is considered a Primary-Key.
In order to persist the "trivial" (String, int) fields, all I need is to add the correct annotation. So far so good.
However, I don't understand how should I persist the home-brewed (Child, Father) types referenced.
Should I:
Convert each such reference to hold the Primary-Key (a name String, in this example) instead of the "actual" object, so Vector<Child> offsprings; becomes Vector<String> offspringsNames;? If that is the case, how do I handle the object at run-time? Do I just query for the Primary-Key from Class.getName, to retrieve the refrenced objects?
Convert each such reference to hold the actual Key provided to me by the Datastore upon the proper put() operation? That is, Vector<Child> offsprings; becomes Vector<Key> offspringsHashKeys;?
I have read all the offical relevant GAE docs/example. Throughout, they always persist "trivial" references, natively supported by the Datastore (e.g. in the Guestbook example, only Strings, and Longs).
Please see google appengine docs following sections for more clear understanding (Relationships, Transactions)
Also read about detachable objects in JDO
For querying selective columns (or fields), read about fetchgroups in JDO
For your question You have several options:
Owned One to many relationship (the objects will be in same entity group) Here you can have a list of Child in your parent (Father class). This will place all objects in the same entity group. If you do not want to fetch the children every time you fetch Father, you can remove the children from the "default fetch group"
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Father {
#PrimaryKey
#Persistent
private String name;
#Persistent
private int age;
#Persistent(mappedBy = "father", defaultFetchGroup = "false")
private List childern;
}
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Child {
#Persistent
#PrimaryKey
private String name;
#Persistent
private Father dad;
}
Unowned relationships where you store the keys instead of references:
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Father {
#PrimaryKey
#Persistent
private String name;
#Persistent
private int age;
#Persistent
private List childern;
}
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Child {
#Persistent
#PrimaryKey
private String name;
#Persistent
private Key dad;
}
In this case you will have to manage the referential integrity and also make sure they are in the same entity group if you have to update/add them in a single transaction
IMO, if I were modeling a real-world (Father-children) scenario, I'd go the "Owned relatinship" route, since, really, how many children a guy can have ;). Of course there is an additional question of how many fathers are you going to update at a time?
Hope this helps, cheers!
I have examples of creating parent/child relationships using GAE/JPA in my jappstart project. Take a look at how the authentication related entities are related to each other here.
One-to-One (see UserAccount.java and PersistentUser.java):
// parent
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private PersistentUser persistentUser;
// child
#OneToOne(mappedBy = "persistentUser", fetch = FetchType.LAZY)
private UserAccount userAccount;
One-to-Many (see PersistentUser.java) :
#OneToMany(mappedBy = "persistentUser", cascade = CascadeType.ALL)
private Collection<PersistentLogin> persistentLogins;
Many-to-One (see PersistentLogin.java):
#ManyToOne(fetch = FetchType.LAZY)
private PersistentUser persistentUser;
Also, note in the constructors how KeyFactory is used for entities with a parent versus without a parent.
#Id
private Key key;
// this entity has a parent
public PersistentUser(final Key key, final String username) {
this.key = KeyFactory.createKey(key, getClass().getSimpleName(), username);
...
}
// this entity does not have a parent
public UserAccount(final String username) {
this.key = KeyFactory.createKey(getClass().getSimpleName(), username);
....
}
Hopefully, this is helpful for you. I couldn't tell from the question whether you were using JPA or JDO.
If you have a reference to a Father in the Child and to the Children in the Father than you have the potential for inconsistency assuming that the relationship between Father and Child is two-way (ie. every Child's father should be in the list of Children for that Father). Only one of the two references is necessary.
Both solutions will work, but keeping the list of children in the father has two disadvantages:
Every access to the Father object will download the list keys to the child object. If there are many keys, this could cause unnecessary overhead.
I believe that GAE limits the size of a list to 5,000 items.

Categories

Resources