2012-04-01 131 views
2

如果我在DataModel中具有雙向關係,那麼我的應用程序有責任將引用保持在Java代碼中。JPA雙向關係

這樣做的最好方法是什麼?

例如bidir。 1:A和B.

@Entity 
class A { 

@ManyToOne 
private B b; 

} 


@Entity 
class B { 

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

} 

如果我說B.addA(b)本不讓變量A中的B點至i添加的參考之間的N個關係。 如果我打電話給A.setB(b),這不會將b的引用添加到B中的集合中。

一種可能的方法是在我的應用程序代碼中調用setB AND addA。

其他posibility是寫這樣的組A(..)方法:

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

但是這有時會拋出一些例外,如:

org.hibernate.LazyInitializationException: illegal access to loading collection 

我猜是因爲框架來電某點這個setMethod並且想要加載「this」引用...?!?有人可以解釋爲什麼會發生這種情況嗎? 什麼是保證我在我的java代碼中有乾淨的bidrectional關係的方式?

THX

UPDATE: 這裏是原代碼:

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

} 

在我的查詢之一,我得到它說,setGrid方法IST內部出了差錯例外...如果我刪除線一切都很好..但後來我沒有我的雙向...:/

堆棧跟蹤:

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

還沒有你反饋? – 2012-04-02 18:26:43

+0

嗨,非常感謝您的幫助,但不幸的是,這並沒有解決問題。每當我想從一個相關實體的set/add方法中獲得另一個實體的集合時,我得到這個LazyInitializationException:/我將嘗試拆分我的解決方案,看看我能得到更多細節爲什麼會發生這種情況:(謝謝你在尋求幫助 – Moonlit 2012-04-02 20:46:43

+0

也許你應該在Hibernate論壇上提問,他們會更好地瞭解實現的內部結構 – 2012-04-02 21:10:41

回答

1

Hibernate和其他基於JPA的ORM用於加載只在需要時定義關係的集合(延遲加載)。我知道當你嘗試修改尚未加載的集合或者是一箇中間狀態時,Hibernate會拋出這個異常。

Hibernate使用代理來處理實體,它理解您希望在調用該特定集合的get方法時使用集合。

我會實施您的setGrid方法真的不同,但首先您的實體需要實施方法equalshashCode。其他修改如下:

將您的集羣集合更改爲集合。一組不包含重複的情況下,這樣的話你就不需要添加任何元素添加到集合之前做contains檢查:

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

然後修改setGrid方法,因此調用集合本身的add方法,而不是一個你曾宣稱:

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

最後,改一下你的addCluster方法在網格類的實現:

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

希望這可以幫助

+0

謝謝你的回覆。我犯了一個錯誤..在setB方法中它應該是if(!b.getAs( ).contains(this))...所以我已經使用了get方法,並且這條線出現在例外的堆棧跟蹤中。 – Moonlit 2012-04-01 12:39:55

+0

你能否用這些信息更新你的問題?,你應該找到一個鏈接,允許你可以編輯它(如果你發佈stacktrace會非常有用) – 2012-04-01 12:45:37

+0

它不一定是懶惰的 - 你可以指定默認的加載模式,參見javax.persistence.FetchType。 – 2012-04-01 12:54:27

1

這是一個想法。

我使用兩層,「持久性模型層」和「域模型層」。

「持久化模型層」的類有一些JPA註釋,但沒有任何應用程序規則。
「域模型層」的類沒有任何JPA註釋。

JPA/Hibernate知道「持久性模型層」的類, 但不知道「域模型層」的類。在「持久性模型層」

類是用於JPA /休眠非常簡單。
所以,像這個問題的問題將不太可能發生。在「域模型層」

類,在這種情況下,有責任保持A和B之間的雙向關係(A#組B,B#ADDA)
沒有必要擔心ORM的影響。

有exsample代碼。
「持久性模型層」包含A和B.
「域模型層」包含MA和MB。
MA的實例有一個實例,馬委託其狀態爲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; 
    } 
} 

這是更好地實現equals/hashCode方法。
我希望你能成爲一個提示。