JPA双向关系

时间:2012-04-01 11:51:44

标签: java jpa relationship bidirectional one-to-many

如果我的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

1 个答案:

答案 0 :(得分: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);
   }
}

最后,改变一下Grid类中addCluster方法的实现:

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

希望这有帮助