views:

68

answers:

4

I'm currently moving a (working) app from using EclipseLink to Hibernate JPA, mostly it's gone quite smoothly, but I'm finding one thing that I can't explain, and also can't think of any good search terms!

Basically, I have four entities, with one-to-many relationships forming a chain:

EntityA has a list of EntityB's, each of which has a list of EntityC's, each of which have a list of EntityD's

each of those then has a many-to-one relationship going the other way, so:

EntityD has an EntityC, which has an EntityB, which has an EntityA.

That is (heavily reduced for clarity):

@Entity
public class EntityA {
  @OneToMany (cascade = CascadeType.All, mappedBy = "entityA")
  private List<EntityB> entityBList;
  ...
}

@Entity
public class EntityB {
  @OneToMany (cascade = CascadeType.All, mappedBy = "entityB")
  private List<EntityC> entityCList;

  @JoinColumn (name = "ENTITY_A", referencedColumnName = "ENTITY_A_ID")
  @ManyToOne (cascade = CascadeType.PERSIST, optional = false)
  private EntityA entityA;
}

@Entity
public class EntityC {
  @OneToMany (cascade = CascadeType.ALL, mappedBy = "entityC")
  private List<EntityD> entityDList;

  @JoinColumn (name = "ENTITY_B", referencedColumnName = "ENTITY_B_ID")
  @ManyToOne (cascade = CascadeType.PERSIST, optional = false)
  private EntityB entityB;
}

@Entity
public class EntityD {
  @JoinColumn (name = "ENTITY_C", referencedColumnName = "ENTITY_C_ID")
  @ManyToOne (cascade = CascadeType.PERSIST, optional = false)
  private EntityC entityC;
}

I get an EntityA from the database (looking it up by its primary key), and thus get a nicely populated EntityA instance, with a PersistentBag for my List<EntityB>. I see a lazy load happening when I dereference that List<EntityB>, and the same repeated for getting EntityCs from EntityB.

At this point, everything is as I expect, I have an EntityA, B and C all fully populated with the values from the database, but then I try to get my EntityD, from EntityC, and find that it's null.

My entity manager is still open and active at this point, and even if I look at it in the debugger immediately after getting the EntityA, I can walk through the relationships, as far as EntityC, and again see the 'entityDList' as null.

The only solution I've found so far is to use:

EntityManager.refresh(entityC);

which populates all its elements including a lazily-loaded PersistentBag for the entityDList.

So, my guess is that Hibernate is only populating the references 2 levels deep (or 3, depending on how you count), and giving up after that, although I don't really understand why that would be. Does that make sense to anyone?

Is there any solution other than the .refresh? Some kind of config or annotation value that will make Hibernate populate the references all the way down?

+1  A: 

This is funny indeed. One way to work around it would be to query object A left join-fetching to ->B->C->D which is also faster if your going to traverse down to object D anyway. It would be something like this.

"from A left join fetch B left join fetch C left join fetch D"

Have you also tried making the relationship from C->D eager? Curious what will happen then...

Albert
Thanks Albert, I tried both suggestions, to no avail :( Making the relationship eager had no effect, so I tried making all 3 relationships (A->B->C->D) eager, but Hibernate complained with "cannot simultaneously fetch multiple bags". Adding left joins to the JPQL had the same result.I've found something elsewhere suggesting that that's because the collections I'm eagerly fetching are Lists, and that it would work if I made them Sets, but I'm too lazy to do that now, so I think I'll just put up with a refresh call!
DaveyDaveDave
+1  A: 

What's the value set for the hibernate configuration parameter "max_fetch_depth"?

Matt Brock
+1  A: 

The hibernate docs say that you can set it with the hibernate.max_fetch_depth property. The default is 3. You can find it in the "Hibernate Reference Documentation" on page 47.

ambassador86
Thanks for this - do you know if the hibernate.properties file is also used by Hibernate JPA, I can't find an answer anywhere in the Hibernate docs? I've tried changing it to 4 and also 0, and it appears to have no effect...
DaveyDaveDave
OK, ignore the above, it does seem to be being used, because by setting max_fetch_depth to 0 has broken other parts of my app :) Setting it to a high number though (I've tried 4 and 10), doesn't seem to have any effect; I guess because it's just a maximum, and something else is persuading Hibernate to stop at 3 levels...?
DaveyDaveDave
A: 

Thanks to the suggestions from people here, which are probably relevant, but didn't help my specific case.

If you're reading this experiencing the same problem, it's probably worth trying the max_fetch_depth suggestion, but for some reason it didn't work for me (I'd love suggestions as to why?).

Likewise, if your @OneToMany's are Sets, rather than Lists, doing an eager fetch or a left join, as suggested by Albert might work, but apparently Hibernate only lets you have a maximum of 1 List that is eagerly fetched, if you need more than that, your collections should be Sets. I didn't try it, but I suspect that it might have solved the problem.

Unless anyone has a better suggestion, I'll stick with calling refresh, which actually probably makes more sense for my application anyway.

DaveyDaveDave