Imagine simple oriented graph you want to store using JPA (e.g. Hibernate implementation) with help of
cascades - so
you are not forced to store nodes one by one. I'm using bi-directional relations because I want to traverse graph not only by orientation. I simply want to retrieve one node and know what nodes are going from it and what nodes are going to it.
No-arg constructor and some other fields and annotations are omitted not to confuse you during reading:
Node
Code:
... class Node implements Serializable {
@OneToMany(mappedBy = "id.source", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<OrientedEdge> outgoingEdges = new HashSet<>();
@OneToMany(mappedBy = "id.target", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<OrientedEdge> incomingEdges = new HashSet<>();
public Node withEdge(Node target) {
OrientedEdge edge = new OrientedEdge(this, target);
outgoingEdges.add(edge);
target.incomingEdges.add(edge);
return this;
}
}
Oriented edge - class uses @EmbeddedId because I want those two @ManyToOne fields to be composite key.
Code:
... class OrientedEdge implements Serializable {
@EmbeddedId
private OrientedEdgeId id;
public OrientedEdge(Node source, Node target) {
this.id = new OrientedEdgeId(source, target);
}
}
OrientedEdgeId
Code:
@Embeddable
public class OrientedEdgeId implements Serializable {
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "SOURCE_NODE_ID")
private Node source;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "TARGET_NODE_ID")
private Node target;
public OrientedEdgeId(Node source, Node target) {
this.source = source;
this.target = target;
}
}
Code for persisting graph:
Code:
Node node1 = new Node();
Node node2 = new Node()
.withEdge(node1);
nodesRepository.save(node2);
What happens is that cascade is not working as I would expect. I just wanted to store one node and let cascades to do the rest of the work. I know there is a circular dependency between nodes but I still somehow hoped that hibernate will be clever enought to take care of it. Sadly the exception gets raised. Is there any way I store this object structure to DB by storing one entity and beeing forced to store one by one with help of cascades?
The exception when save operation is called:
Code:
java.lang.NullPointerException: null
at org.hibernate.type.descriptor.java.AbstractTypeDescriptor.extractHashCode(AbstractTypeDescriptor.java:84)
at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:216)
at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:220)
at org.hibernate.type.EntityType.getHashCode(EntityType.java:391)
at org.hibernate.type.ComponentType.getHashCode(ComponentType.java:258)
at org.hibernate.engine.spi.EntityKey.generateHashCode(EntityKey.java:76)
at org.hibernate.engine.spi.EntityKey.<init>(EntityKey.java:71)
at org.hibernate.internal.AbstractSessionImpl.generateEntityKey(AbstractSessionImpl.java:327)