2010-06-29 17 views
5

Ich bin gerade dabei, eine (funktionierende) App von EclipseLink nach Hibernate JPA zu verschieben, meistens ist es ziemlich flüssig, aber ich finde eine Sache, die ich nicht erklären kann und kann Denk an gute Suchbegriffe!Hibernate JPA - ManyToOne-Beziehung nicht ausgefüllt

Grundsätzlich ich vier Einheiten haben, mit einer Eins-zu-viele-Beziehungen eine Kette bilden:

Entität hat eine Liste der Entität ist, von denen jede eine Liste von EntityC der hat, von denen jede eine Liste von EntityD die haben

jeder von denen hat dann eine viele-zu-eins-Beziehung in die andere Richtung, so:

EntityD eine EntityC hat, die eine Entität hat, die eine Entität hat.

Das heißt (stark für Klarheit reduziert):

@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; 
} 

ich eine Entität aus der Datenbank erhalten (suchen sie durch ihre Primärschlüssel) und damit eine schön bevölkerten Entität Instanz erhalten, mit einem PersistentBag für mein List<EntityB>. Ich sehe eine Lazy Load passiert, wenn ich das List<EntityB> dereferenzieren, und das gleiche wiederholt, um EntityCs von EntityB zu bekommen.

An diesem Punkt ist alles wie erwartet, ich habe eine EntityA, B und C alle vollständig mit den Werten aus der Datenbank gefüllt, aber dann versuche ich meine EntityD, von EntityC, und finden Sie, dass es null ist.

Mein Entity-Manager ist zu diesem Zeitpunkt noch offen und aktiv, und selbst wenn ich ihn im Debugger direkt nach dem Erwerb von EntityA anschaue, kann ich die Beziehungen bis EntityC durchgehen und wieder 'entityDList' sehen 'als null.

Die einzige Lösung, die ich bisher gefunden habe, ist, zu verwenden:

EntityManager.refresh(entityC); 

der alle Elemente, die eine träge belastete PersistentBag für die entityDList auffüllt.

Also, meine Vermutung ist, dass Hibernate nur die Referenzen 2 Ebenen tief (oder 3, je nachdem, wie Sie zählen) bevölkern, und danach aufgeben, obwohl ich nicht wirklich verstehe, warum das wäre. Macht das irgendjemanden Sinn?

Gibt es eine andere Lösung als die .refresh? Eine Art Konfigurations- oder Annotationswert, der Hibernate dazu bringt, die Referenzen vollständig aufzufüllen?

+0

Welcher Wert ist für den Hibernate-Konfigurationsparameter "max_fetch_depth" eingestellt? –

Antwort

2

Dank den Anregungen von Menschen hier, die wahrscheinlich relevant sind, aber nicht meinem spezifischen Fall geholfen.

Wenn Sie lesen, dass das gleiche Problem auftritt, ist es wahrscheinlich wert, den Max_fetch_depth Vorschlag zu versuchen, aber aus irgendeinem Grund hat es nicht für mich funktioniert (Ich würde gerne Vorschläge, warum?).

Ebenso, wenn Ihre @ OneToMany's Sets sind, anstatt Listen, machen Sie einen eifrigen Abruf oder einen linken Join, wie von Albert vorgeschlagen, aber scheinbar nur Hibernate lässt Sie maximal 1 Liste, die eifrig abgerufen wird, Wenn Sie mehr benötigen, sollten Ihre Sammlungen Sets sein. Ich habe es nicht versucht, aber ich vermute, dass es das Problem gelöst haben könnte.

Wenn niemand einen besseren Vorschlag hat, bleibe ich beim Aufruf Refresh, was eigentlich sowieso mehr Sinn für meine Anwendung macht.

1

Das ist in der Tat lustig. Eine Möglichkeit, dies zu umgehen, wäre, das Objekt A linker Join-Fetching nach -> B-> C-> D abzufragen, was auch schneller ist, wenn Sie trotzdem auf das Objekt D herunterfahren. Es wäre so etwas.

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

Haben Sie auch versucht, die Beziehung von C-> D eifrig zu machen? Neugierig, was dann passieren wird ...

+0

Dank Albert, habe ich beide Vorschläge ausprobiert, vergeblich :(Die Beziehung eifrig zu machen hatte keine Wirkung, also habe ich versucht, alle 3 Beziehungen (A-> B-> C-> D) eifrig zu machen, aber Hibernate beschwerte sich mit "kann nicht gleichzeitig hole mehrere Taschen. "Das Hinzufügen linker Joins zum JPQL hatte das gleiche Ergebnis. Ich habe etwas anderswo gefunden, was darauf hindeutet, dass die Sammlungen, die ich eifrig lese, Listen sind, und dass es funktionieren würde, wenn ich Sets mache, aber Ich bin zu faul, das jetzt zu tun, also denke ich, dass ich gerade einen Auffrischanruf ertragen werde! – DaveyDaveDave

1

Die Hibernate-Dokumentation besagt, dass Sie es mit der Eigenschaft hibernate.max_fetch_depth festlegen können. Der Standardwert ist 3. Sie können es in der "Hibernate Reference Documentation" auf Seite 47 finden.

+0

Danke dafür - wissen Sie, ob die Hibernate.properties-Datei auch von Hibernate JPA verwendet wird, kann ich nicht finden eine Antwort irgendwo in den Hibernate-Dokumenten? Ich habe versucht, es in 4 und auch 0 zu ändern, und es scheint keinen Effekt zu haben ... – DaveyDaveDave

+0

OK, ignoriere das oben genannte, es scheint, verwendet zu werden, weil Max_fetch_depth zu setzen 0 ist kaputt gegangen andere Teile meiner App :) Setzen Sie es auf eine hohe Zahl obwohl (ich habe versucht, 4 und 10), scheint keine Wirkung zu haben; Ich schätze, weil es nur ein Maximum ist, und etwas anderes überzeugt Hibernate, auf 3 Ebenen zu stoppen ...? – DaveyDaveDave