如果我的DataModel中有双向关系,我的应用程序有责任在java代码中使引用保持最新。
这样做的最佳方式是什么?
例如bidir。 1:A和B之间的N关系。
@Entity
class A {
@ManyToOne
private B b;
}
@Entity
class B {
@OneToMany(mappedBy="b")
private Collection<A> as;
}
如果我说B.addA(b)这不会让A中的变量b指向我添加的Reference。 如果我调用A.setB(b),则不会将B的引用添加到B中的集合中。
一种可能的方法是在我的应用程序代码中调用setB AND addA。
另一个可能性是写这样的setA(..)方法:
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代码中有干净的双向关系的方法?
THX
更新: 这是原始代码:
@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方法中的内容是错误的...如果我删除行一切都很好..但是我没有我的双向...:/
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 :(得分:1)
Hibernate和其他基于JPA的ORM用于加载在需要时定义关系的集合(延迟加载)。我知道当你试图修改一个尚未加载或者是中间状态的集合时,Hibernate会抛出异常。
Hibernate使用代理来处理实体,它知道在调用特定集合的get方法时要使用集合。
我会真正实现您的setGrid
方法,但首先您的实体需要实施方法equals
和hashCode
。其他修改将是:
将您的群集集合更改为一组。集合不包含重复的实例,因此在向集合中添加任何元素之前,您不需要执行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);
}
}
最后,改变一下Grid类中addCluster
方法的实现:
public void addCluster(Cluster c) {
//this.clusters.add(c); -- no needed anymore
c.setGrid(this);
}
希望这有帮助