2012-04-01 13 views
2

Wenn ich eine bidirektionale Beziehung in meinem DataModel habe, liegt es in meiner Verantwortung, die Referenzen im Java-Code aktuell zu halten.JPA bidirektionale Beziehungen

Was ist der beste Weg, dies zu tun?

Zum Beispiel ein bidir. 1: n-Beziehung zwischen A und B.

@Entity 
class A { 

@ManyToOne 
private B b; 

} 


@Entity 
class B { 

@OneToMany(mappedBy="b") 
private Collection<A> as; 

} 

Wenn ich sage B.addA (b) dies nicht die Variable b in einem Punkt die Referenz wird lassen i hinzugefügt. Und wenn ich A.setB (b) aufrufen, fügt dies keine Referenz von b zur Sammlung in B hinzu.

Eine Möglichkeit wäre, setB UND addA in meinem Anwendungscode aufzurufen.

Die anderen posibility wären die setA (..) Methode wie folgt zu schreiben:

public setB(B b) { 
    this.b = b; 
    if(!b.contains(this) { 
    b.add(this); 
    } 
} 



public addA(A a) { 
    if(!as.conatains(a)) { 
     as.add(a); 
    } 
    a.setB(this); 
    } 

aber das wirft manchmal einige Ausnahmen wie:

org.hibernate.LazyInitializationException: illegal access to loading collection 

ich denke, da das Framework ruft an Irgendeinem dieser setMethod und möchte die "diese" Referenz laden ...?!? Kann mir jemand erklären, warum das passiert? Und was ist der Weg zu gehen, um sicherzustellen, dass ich saubere bidirektionale Beziehungen in meinem Java-Code habe?

thx

UPDATE: hier ist der ursprüngliche Code:

@Entity 
class Cluster{ 

private Grid grid 

//someother fields 

@ManyToOne 
    public Grid getGrid() { 
     return grid; 
    } 

    public void setGrid(Grid grid) { 
     this.grid = grid; 
     if(!grid.getClusters().contains(this)) { //HERE AN EXCEPTION IS THROWN 
      grid.addCluster(this); 
     } 
    } 

} 

@Entity 
class Grid { 

    private Collection<Cluster> clusters = new ArrayList<Cluster>(); 

    //some other fields 

    @OneToMany(mappedBy = "grid", cascade = CascadeType.PERSIST, orphanRemoval = true) 
    public Collection<Cluster> getClusters() { 
     return clusters; 
    } 

    public void setClusters(Collection<Cluster> clusters) { 
     this.clusters = clusters; 
    } 

    public void addCluster(Cluster c) { 
    this.clusters.add(c); 
    c.setGrid(this); 
} 

} 

In einem meiner Abfragen ich die Ausnahme erhalten, die besagt, dass etwas in der setGrid Methode ist falsch ... Wenn Ich entferne die Zeilen alles ist in Ordnung .. aber dann habe ich nicht meine Bidirektionale ...:/

Der Stacktrace:

Exception in thread "main" javax.persistence.PersistenceException: org.hibernate.PropertyAccessException: Exception occurred inside setter of dst1.model.Cluster.grid 
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1214) 
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1147) 
    at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:255) 
    at dst1.Main.dst02b(Main.java:828) 
    at dst1.Main.main(Main.java:38) 
Caused by: org.hibernate.PropertyAccessException: Exception occurred inside setter of dst1.model.Cluster.grid 
    at org.hibernate.property.BasicPropertyAccessor$BasicSetter.set(BasicPropertyAccessor.java:89) 
    at org.hibernate.tuple.entity.AbstractEntityTuplizer.setPropertyValues(AbstractEntityTuplizer.java:583) 
    at org.hibernate.tuple.entity.PojoEntityTuplizer.setPropertyValues(PojoEntityTuplizer.java:229) 
    at org.hibernate.persister.entity.AbstractEntityPersister.setPropertyValues(AbstractEntityPersister.java:3822) 
    at org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:152) 
    at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:982) 
    at org.hibernate.loader.Loader.doQuery(Loader.java:857) 
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:274) 
    at org.hibernate.loader.Loader.loadEntity(Loader.java:2037) 
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:86) 
    at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:76) 
    at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3268) 
    at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:496) 
    at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:477) 
    at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:227) 
    at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:285) 
    at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:152) 
    at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:1090) 
    at org.hibernate.impl.SessionImpl.internalLoad(SessionImpl.java:1038) 
    at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:630) 
    at org.hibernate.type.EntityType.resolve(EntityType.java:438) 
    at org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:139) 
    at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:982) 
    at org.hibernate.loader.Loader.doQuery(Loader.java:857) 
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:274) 
    at org.hibernate.loader.Loader.doList(Loader.java:2533) 
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2276) 
    at org.hibernate.loader.Loader.list(Loader.java:2271) 
    at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:452) 
    at org.hibernate.hql.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:363) 
    at org.hibernate.engine.query.HQLQueryPlan.performList(HQLQueryPlan.java:196) 
    at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1268) 
    at org.hibernate.impl.QueryImpl.list(QueryImpl.java:102) 
    at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:246) 
    ... 2 more 
Caused by: java.lang.reflect.InvocationTargetException 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) 
    at java.lang.reflect.Method.invoke(Unknown Source) 
    at org.hibernate.property.BasicPropertyAccessor$BasicSetter.set(BasicPropertyAccessor.java:66) 
    ... 35 more 
Caused by: org.hibernate.PropertyAccessException: Exception occurred inside setter of dst1.model.Cluster.grid 
    at org.hibernate.property.BasicPropertyAccessor$BasicSetter.set(BasicPropertyAccessor.java:89) 
    at org.hibernate.tuple.entity.AbstractEntityTuplizer.setPropertyValues(AbstractEntityTuplizer.java:583) 
    at org.hibernate.tuple.entity.PojoEntityTuplizer.setPropertyValues(PojoEntityTuplizer.java:229) 
    at org.hibernate.persister.entity.AbstractEntityPersister.setPropertyValues(AbstractEntityPersister.java:3822) 
    at org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:152) 
    at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:982) 
    at org.hibernate.loader.Loader.doQuery(Loader.java:857) 
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:274) 
    at org.hibernate.loader.Loader.loadCollection(Loader.java:2166) 
    at org.hibernate.loader.collection.CollectionLoader.initialize(CollectionLoader.java:62) 
    at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:627) 
    at org.hibernate.event.def.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:83) 
    at org.hibernate.impl.SessionImpl.initializeCollection(SessionImpl.java:1863) 
    at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:369) 
    at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:111) 
    at org.hibernate.collection.AbstractPersistentCollection.readElementExistence(AbstractPersistentCollection.java:167) 
    at org.hibernate.collection.PersistentBag.contains(PersistentBag.java:262) 
    at dst1.model.Cluster.setGrid(Cluster.java:114) 
    ... 40 more 
Caused by: java.lang.reflect.InvocationTargetException 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) 
    at java.lang.reflect.Method.invoke(Unknown Source) 
    at org.hibernate.property.BasicPropertyAccessor$BasicSetter.set(BasicPropertyAccessor.java:66) 
    ... 57 more 
Caused by: org.hibernate.LazyInitializationException: illegal access to loading collection 
    at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:366) 
    at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:111) 
    at org.hibernate.collection.AbstractPersistentCollection.readElementExistence(AbstractPersistentCollection.java:167) 
    at org.hibernate.collection.PersistentBag.contains(PersistentBag.java:262) 
    at dst1.model.Cluster.setGrid(Cluster.java:114) 
    ... 62 more 
+0

hast du noch kein feedback? –

+0

Hallo und vielen Dank für Ihre Hilfe, aber leider hat es das Problem nicht gelöst. Wann immer ich die Sammlung einer anderen Entität aus einer set/add -Methode einer verwandten Entität herausholen möchte, bekomme ich diese LazyInitializationException:/Ich werde versuchen, meine Lösung auseinander zu nehmen und zu sehen, ob ich weitere Details bekommen kann, warum das passiert :(danke Sie für Ihre Hilfe – Moonlit

+0

vielleicht sollten Sie im Hibernate Forum fragen, werden sie besser die Interna der Implementierung wissen. –

Antwort

1

Hibernate und andere JPA-basierte ORMs laden Sammlungen, die Beziehungen nur bei Bedarf definieren (Lazy Loading). Ich verstehe, dass Hibernate diese Ausnahme auslöst, wenn Sie versuchen, eine Sammlung zu ändern, die noch nicht geladen wurde oder die ein Zwischenstatus ist.

Hibernate verwendet Proxys, um die Entitäten zu verarbeiten, und es versteht, dass Sie beim Aufrufen der get-Methode für diese bestimmte Sammlung eine Sammlung verwenden möchten.

Ich würde Ihre setGrid Methode wirklich anders implementieren, aber zuerst müssen Ihre Entitäten die Methoden equals und hashCode implementieren. Andere Änderungen wären:

Ändern Sie Ihre Sammlungen von Clustern zu einem Set.Ein Satz nicht enthält Instanzen kopieren und damit werden Sie nicht brauchen, dass contains Prüfung zu tun, bevor irgendein Element der Sammlung hinzugefügt:

Set<Cluster> clusters = new HashSet<Cluster>(); 

dann Ihre setGrid Methode ändern, damit es die add Methode der Sammlung nennt sich statt des einen hatte man erklärt:

setGrid(Grid grid) { 
    Grid oldGrid = this.grid; 
    this.grid = grid; 
    if (oldGrid != null) { 
     oldGrid.getClusters().remove(this); 
    } 
    if (grid != null) { 
     grid.getClusters().add(this); 
    } 
} 

und schließlich ein bisschen die Umsetzung Ihrer addCluster Methode in der Grid-Klasse ändern:

public void addCluster(Cluster c) { 
    //this.clusters.add(c); -- no needed anymore 
    c.setGrid(this); 
} 

Hoffe, das hilft

+0

Vielen Dank für Ihre Antwort. Ich habe einen Fehler gemacht .. in der Methode SetB sollte es sein, wenn (! b.getAs () .contains (this)) ... also benutze ich schon die Methode get. AND Diese Zeile erscheint im Stacktrace der Exception – Moonlit

+0

Können Sie bitte Ihre Frage mit diesen Informationen aktualisieren ?, sollten Sie einen Link finden, der es erlaubt Sie können es bearbeiten. (Das Stacktrace wird wirklich nützlich sein, wenn Sie es post) –

+0

Es muss nicht faul sein - Sie können den Standard-Lade-Modus angeben. Siehe javax.persistence.FetchType. –

1

Dies ist eine Idee.

Ich verwende zwei Schichten, "Persistence Model Layer" und "Domain Model Layer".

Klassen der "Persistenzmodellschicht" haben einige JPA-Anmerkungen, aber keine Anwendungsregeln.
Klassen von "Domänenmodellschicht" haben keine JPA-Anmerkungen.

JPA/Hibernate kennt Klassen von "Persistenzmodellschicht", kennt aber Klassen von "Domänenmodellschicht" nicht.

Klassen in "Persistenzmodellschicht" sind sehr einfach für JPA/Hibernate.
Also, Probleme wie diese Frage werden wahrscheinlich nicht auftreten.

Klassen in "Domänenmodell Schicht", in diesem Fall haben die Verantwortung, bidirektionale Beziehung zwischen A und B zu halten (A # setb, B # Adda)
Es gibt keine Notwendigkeit, über ORM Auswirkungen zu befürchten ist.

Es gibt Beispielcode.
"Persistenzmodellschicht" enthält A und B.
"Domänenmodellschicht" enthält MA und MB.
Eine Instanz von MA hat eine Instanz von A, und MA seinen Zustand A.

/** persistence model layer */ 
import javax.persistence.Entity; 
import javax.persistence.GeneratedValue; 
import javax.persistence.Id; 
import javax.persistence.ManyToOne; 
@Entity 
public class A { 
    private Long id; 
    private B b; 
    public A(){ 
    } 
    @Id 
    @GeneratedValue 
    public Long getId() { 
     return id; 
    } 
    public void setId(Long id) { 
     this.id = id; 
    } 
    @ManyToOne 
    public B getB() { 
     return b; 
    } 
    public void setB(B b) { 
     this.b = b; 
    } 
} 
import java.util.ArrayList; 
import java.util.Collection; 
import javax.persistence.CascadeType; 
import javax.persistence.Entity; 
import javax.persistence.FetchType; 
import javax.persistence.GeneratedValue; 
import javax.persistence.Id; 
import javax.persistence.OneToMany; 
@Entity 
public class B { 
    private Long id; 
    private Collection<A> as = new ArrayList<A>(); 
    public B(){ 
    } 
    @Id 
    @GeneratedValue 
    public Long getId() { 
     return id; 
    } 
    public void setId(Long id) { 
     this.id = id; 
    } 
    @OneToMany(cascade=CascadeType.ALL, mappedBy="b", fetch=FetchType.LAZY) 
    public Collection<A> getAs() { 
     return as; 
    } 
    public void setAs(Collection<A> as) { 
     this.as = as; 
    } 
} 
/** domain model layer */ 
public class MA { 
    private A entity; 
    public MA(A a){ 
     this.entity = a; 
    } 
    public A getEntity(){ 
     return this.entity; 
    } 
    public MB getB(){ 
     return new MB(entity.getB()); 
    } 
    public void setB(MB mb){ 
     if (mb != null && this.entity.getB() != mb.getEntity()){ 
       this.entity.setB(mb.getEntity()); 
       mb.addA(this); 
     } 
     return; 
    } 
} 
import java.util.ArrayList; 
import java.util.List; 
public class MB { 
    private B entity; 
    public MB(B b){ 
     this.entity = b; 
    } 
    public B getEntity(){ 
     return this.entity; 
    } 
    public void addA(MA ma){ 
     if (ma != null && ! this.getEntity().getAs().contains(ma.getEntity())){ 
      this.entity.getAs().add(ma.getEntity()); 
      ma.setB(this); 
     } 
     return; 
    } 
    public List<MA> getAs(){ 
     List<MA> resultList = new ArrayList<MA>(); 
     for(A a : entity.getAs()){ 
      resultList.add(new MA(a)); 
     } 
     return resultList; 
    } 
} 

Es ist besser zu delegieren equals/HashCode Verfahren zu implementieren.
Ich hoffe, Sie werden ein Hinweis sein.

Verwandte Themen