3

I've recently moved a project that was using native Hibernate entities and session management over to Spring, with annotation driven dependency injection and transaction management.

I have an Entity structure like this:

@Entity
@Table(name = "PARENT",uniqueConstraints=@UniqueConstraint(columnNames={"parentName"}))
public class Parent implements Serializable, {
    static final long serialVersionUID = 1L;

    @Id
    private int id;


    @OneToMany(mappedBy = "parent", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    private Set<Child> child = new HashSet<Child>();
}



@Entity
@Table(name = "CHILD", uniqueConstraints = @UniqueConstraint(columnNames = {
        "childName", "parent_id" }))
@SequenceGenerator(name = "CHILD_SEQ", allocationSize = 1, sequenceName = "CHILD_SEQ")
public class Child implements Serializable {

    static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CHILD_SEQ")
    private int id;
    private String childName = ""; //$NON-NLS-1$

    @ManyToOne
    @JoinColumn(name = "parent_id")
    private Parent parent;
}

Here's what happens.

  • I get all the parent objects which contain individual lists of child objects. This works quickly, I have the full object tree in memory in my debugger I can see every Child object is loaded correctly.

    public List<Parent> getParents() {
        return em.createQuery("from Parent",Parent.class).getResultList();
    }
    
  • I then try and get every Child object and the application freezes my PC slows down and after a few hours I get the Out on memory exception.

    public List<Child> getChildren() {
        return em.createQuery("from Child",Child.class).getResultList();
    }
    

I have looked at the generated SQL and for the first method it seems to logically break down the calls in to individual sets of objects and for the seconds it seems to construct a monster sized query that I can't really follow and that query doesn't appear to be returning, although I can't really tell.

What I cannot understand is why the query to the parent works so quickly and gives me every single object but the child query just breaks.

Bond - Java Bond
  • 3,972
  • 6
  • 36
  • 59
Link19
  • 586
  • 1
  • 18
  • 47
  • can you share the persisting logic – Amer Qarabsa Apr 13 '17 at 08:30
  • Was having issues with formatting. – Link19 Apr 13 '17 at 08:38
  • try em.persist instead of merge since the persisted object wont be managed, if that does not work share the code calling saveChild() – Amer Qarabsa Apr 13 '17 at 09:01
  • 1
    Any specific reason for `FetchType.EAGER`? Unless there is a very good reason, it should be `FetchType.LAZY` as `EAGER` has the risk of loading a very large object graph, which could cause an `OutOfMemory` exception. – manish Apr 13 '17 at 11:48
  • So how many children do you really have in the database? – ThomasRS Apr 19 '17 at 13:18
  • I dont really like the mismatch between field names and database table names. – ThomasRS Apr 19 '17 at 13:19
  • Are you sure you're not getting all Child actually also gets all the Parents which again has all Children eagerly? In other words chained eagerly resolvement. – ThomasRS Apr 19 '17 at 13:25
  • Currently I have 11 Parents, 1 parent has 9 Childs and the others have 1 or 2 each. If I retrieve all the Parents I get the whole tree back in 11ms. If I try and just retrieve all Children it never returns. – Link19 Apr 19 '17 at 13:26
  • I assumed Hibernate wouldn't get itself in to that situation. I mean when i get all Parents each child has a parent so i'd have thought it would work the same wither way? – Link19 Apr 19 '17 at 13:30
  • I agree with manish; by using `FetchType.EAGER` may cause the full DB loaded in memory; in any case.. did you try to change the `CascadeType` form `CascadeType.ALL` to `CascadeType.PERSIST`? – Angelo Immediata Apr 19 '17 at 14:29
  • Getting all the Parents loads the entire DB in to memory in 11 ms. It's ok it's an embedded DB that stores relatively few relations and I want them to be eager. I am trying to work out why getting parents works quickly and getting children never returns when they should be bringing back the same data. – Link19 Apr 19 '17 at 14:35
  • @Link19 as I suggested... did you ty to change the `CascadeType` from `CascadeType.ALL` to `CascadeType.PERSIST`? Maybe for some reason the `CascadeType.ALL` causes a loop...did you put the log level to trace in order to see what it's executed when the query never returns? – Angelo Immediata Apr 19 '17 at 14:39
  • Yes that hasn't done anything. I'm loathe to just change properties to see if it helps though. – Link19 Apr 19 '17 at 14:52
  • The Query it runs seems to be one big table of results with lots of joins, the Child entity has 3 Element Collections and it doesn't seem to be correctly joining those element collections. – Link19 Apr 19 '17 at 14:55
  • well since there are a lot of joins... it seems a kind of loop; try to change the cascadetype and see if the situation is the sam – Angelo Immediata Apr 19 '17 at 15:57
  • It works when I get the parent. This is the issue I have. The logic of it working for one thing and not another suggests that i have some fundamental problem, I see so often people using hibernate and treat it like perfecting a recipe rather than an exact science, are we not computer scientists? Do we not strive to understand why rather than just accepting you don't understand why as long as tweaking an thingybob made it work? – Link19 Apr 19 '17 at 16:26
  • have you enabled second level caching? are you accessing data across different transactions? – Akshay Khopkar Apr 20 '17 at 13:48
  • "the Child entity has 3 Element Collections and it doesn't seem to be correctly joining those element collections." Well remove them and add back one by one till you find the problem. – Alan Hay Apr 21 '17 at 08:10

2 Answers2

4

When you start with the parents Hibernate can read all parents (simple) and join with the child table (again, simple). Each child has exactly one parent, which is the one we join with, so that is enough. While the entire database is read into memory that is no problem as it is small.

When you start with the children on the other hand Hibernate must first fetch the parent for each child. That means one join with the parent table. However, those parents have children, so we must join with the child table again in order to find them.

Your classes are not complete, parent name is missing and you mention other collections. If the other collections are eager as well they would have to be fetched with joins for the first child level and the second child level as well, adding two more joins per collection. That makes for a very complex query!

ewramner
  • 5,810
  • 2
  • 17
  • 33
  • Lazy Fetching and manually controlling when to initialise these collections works. My original problem though is that when moving from using native hibernate with manual control over to JPA / Spring the problem appeared even though all data structures were the same and all functions were performing equivalent operations. So it seems like the native hibernate calls can actually work out these loops better than their implementation of JPA can, which I find surprising. – Link19 Apr 21 '17 at 14:45
  • Eager specifies that JPA must load the children as the parent is loaded, but not how. I believe Hibernate defaults to a join (fetch) which is what give you the problem. I think EclipseLink will do a sub-qeury for each Parent, so it wont have an issue. Hibernate allows you to specify a `FetchMode` if you need control the behavior. http://stackoverflow.com/questions/32984799/fetchmode-join-vs-subselect – Klaus Groenbaek Apr 21 '17 at 23:13
-1

It might be a memory leak causing this. You can increase your heap space using.

-Xms<size> set initial Java heap size

-Xmx<size> set maximum Java heap size

http://javarevisited.blogspot.com.ng/2011/09/javalangoutofmemoryerror-permgen-space.html

However, It's good to know what could be the cause of java.lang.OutOfMemoryError: Java heap space. Using a profiler can help you figure out what's eating the heap size. If you are using Netbeans, you can use https://profiler.netbeans.org/ or find profiler compatible with your IDE.

  • The error is happening when I call em.merge() before it returns, any memory issue would Shirley be Hibernates problem and not something i should or could have to address. – Link19 Apr 13 '17 at 12:09