-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 1 post ] 
Author Message
 Post subject: Persisting and loading a large Graph structure
PostPosted: Mon Jan 12, 2015 10:07 am 
Newbie

Joined: Sun May 22, 2011 7:06 am
Posts: 6
Hello everyone,

I'm currently working with Hibernate 4.3.7. and JPA 2.1. What I need to do is to persist the following graph structure to a database via Hibernate:

  • A Graph consists of a (lazy) set of Nodes
  • A Node consists of a (lazy) set of neighbor nodes (and some primitive data which is irrelevant here)

You can find the source code at the very end of this post.

The lazy part is working well. However, I have two problems:

1) When I create a new Graph in my application with 1000 nodes or more and save the graph, I run into a StackOverflowError. Some cascades seem to run into a cycle. However, it doesn't work without them.

2) In some scenarios, I need to load the entire graph into memory, including all lazy references. How can I do this efficiently without executing N+1 SQL statements? I tried using a HQL fetch query, but I still get N+1 selects. I also tried to apply a FetchProfile, with the same result.


EDIT:

It turns out that for problem 1, this only happens if I call EntityManager.merge(graph) first. EntityManager.persist(graph) works fine.


EDIT 2:

The thing that actually causes trouble is a large graph with a cyclic reference somewhere, like A->B->C->...->XYZ->A. That seems to cause the merge stack overflow error.


Any help would be much appreciated, I'm really depending on being able to persist this structure using Hibernate.


Thank you,


Alan



The following is the source code that I am using.

Graph.java
Code:
@Entity
public class Graph extends PersistableObject {

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "graph")
    private Set<Node> nodes = new HashSet<Node>();

    // getters, setters...
}


Node.java
Code:
@Entity
public class Node extends PersistableObject {

    @ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.MERGE, CascadeType.PERSIST })
    private Set<Node> neighbors = new HashSet<Node>();

    @ManyToOne(fetch = FetchType.EAGER, cascade = { CascadeType.MERGE })
    private Graph graph;

    // getters, setters...
}


PersistableObject.java
Code:
@MappedSuperclass
public abstract class PersistableObject {

   @Id
   private String id = UUID.randomUUID().toString();

   public String getId() {
      return this.id;
   }

   public void setId(final String id) {
      this.id = id;
   }

   @Override
   public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + (this.id == null ? 0 : this.id.hashCode());
      return result;
   }

   @Override
   public boolean equals(final Object obj) {
      if (this == obj) {
         return true;
      }
      if (obj == null) {
         return false;
      }
      if (this.getClass() != obj.getClass()) {
         return false;
      }
      PersistableObject other = (PersistableObject) obj;
      if (this.id == null) {
         if (other.id != null) {
            return false;
         }
      } else if (!this.id.equals(other.id)) {
         return false;
      }
      return true;
   }

}


For your convenience, here is a GraphFactory that is capable of creating random graphs of given sizes.
Code:
public class GraphFactory {

   public static Graph createRandomGraph(final int numberOfNodes, final int edgesPerNode) {
      Graph graph = new Graph();
      // we use this list for random index access
      List<Node> nodes = new ArrayList<Node>();
      for (int nodeIndex = 0; nodeIndex < numberOfNodes; nodeIndex++) {
         Node node = new Node();
         node.setGraph(graph);
         graph.getNodes().add(node);
         nodes.add(node);
      }
      Random random = new Random();
      for (Node node : nodes) {
         for (int edgeIndex = 0; edgeIndex < edgesPerNode; edgeIndex++) {
            int randomTargetNodeIndex = random.nextInt(nodes.size());
            Node targetNode = nodes.get(randomTargetNodeIndex);
            node.getNeighbors().add(targetNode);
         }
      }
      return graph;
   }
}



The Stack Trace of the StackOverflowError repeatedly contains the following sequence (directly one after the other):
Code:
   at org.hibernate.engine.spi.CascadingActions$6.cascade(CascadingActions.java:277) ~[hibernate-core-4.3.7.Final.jar:4.3.7.Final]
   at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:350) ~[hibernate-core-4.3.7.Final.jar:4.3.7.Final]
   at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:293) ~[hibernate-core-4.3.7.Final.jar:4.3.7.Final]
   at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:161) ~[hibernate-core-4.3.7.Final.jar:4.3.7.Final]
   at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:118) ~[hibernate-core-4.3.7.Final.jar:4.3.7.Final]
   at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:432) ~[hibernate-core-4.3.7.Final.jar:4.3.7.Final]
   at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:248) ~[hibernate-core-4.3.7.Final.jar:4.3.7.Final]
   at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:317) ~[hibernate-core-4.3.7.Final.jar:4.3.7.Final]
   at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:186) ~[hibernate-core-4.3.7.Final.jar:4.3.7.Final]
   at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:886) ~[hibernate-core-4.3.7.Final.jar:4.3.7.Final]
   at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:868) ~[hibernate-core-4.3.7.Final.jar:4.3.7.Final]


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 1 post ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.