2012-04-01 4 views
2

Если у меня есть двунаправленная связь в моей DataModel, то моя заявка несет ответственность за обновление ссылок в Java-коде.JPA двунаправленные отношения

Каков наилучший способ для этого?

Например, бидир. 1: N соотношение между А и В.

@Entity 
class A { 

@ManyToOne 
private B b; 

} 


@Entity 
class B { 

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

} 

Если я скажу B.addA (б) это не позволяет переменной Ь в точке к опорному я добавил. И если я вызываю A.setB (b), это не добавляет ссылку на b в коллекцию в B.

Одним из возможных способов было бы вызвать setB AND addA в моем коде приложения.

Другой вешать было бы написать метод множество А (..), как это:

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 

я думаю, потому что структура вызовов в somepoint this setMethod и хотите загрузить «эту» ссылку ...?!? может кто-нибудь объяснить мне, почему это происходит? И каков путь, чтобы гарантировать, что у меня есть чистые взаимозависимые отношения в моем java-коде?

ТНХ

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 метод неправильно ... Если я удаляю линии, все в порядке .. но тогда у меня нет моей двунаправленной ...:/

Стекло:

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

У вас еще нет ни одного отзыва? –

+0

Привет и большое спасибо за вашу помощь, но, к сожалению, это не решило проблему. Всякий раз, когда я хочу получить коллекцию другого объекта из метода set/add связанного объекта, я получаю это LazyInitializationException:/Я попытаюсь разобрать свое решение и посмотреть, могу ли я получить более подробную информацию о том, почему это происходит :(thank вы за вашу помощь – Moonlit

+0

Возможно, вам стоит задать вопрос на форуме Hibernate, они будут лучше знать внутренности реализации. –

ответ

1

Hibernate и другие ORM на основе JPA используют для загрузки коллекций, которые определяют отношения только при необходимости (ленивая загрузка). Я понимаю, что 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); 
    } 
} 

И, наконец, немного изменить реализацию вашего метода addCluster в классе Сетка:

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

Надеюсь, что это поможет

+0

Спасибо за ваш ответ. Я допустил ошибку .. в методе setB это должно быть, если (! b.getAs () .contains (this)) ... поэтому я уже использую метод get. ANd эта строка отображается в стеке исключения. – Moonlit

+0

вы можете обновить свой вопрос с помощью этой информации ?, вы должны найти ссылку, которая позволит вы должны отредактировать его. (что stacktrace будет очень полезно, если вы опубликуете его) –

+0

Это не должно быть ленивым - вы можете указать режим загрузки по умолчанию. См. javax.persistence.FetchType. –

1

Это идея.

Я использую два слоя: «слой модели настойчивости» и «слой модели домена».

В классах «слоя модели настойчивости» есть некоторые примечания JPA, но у них нет правил применения.
В классах «слой модели домена» нет никаких аннотаций JPA.

JPA/Hibernate знает классы «слой модели сохранения», , но не знает классов «слой модели домена».

Занятия в стиле «настойчивость модели» очень просты для JPA/Hibernate.
Таким образом, проблемы, подобные этому вопросу, вряд ли произойдут.

Классы в «модели домена слоя», в этом случае обязаны поддерживать двунаправленную связь между A и B. (A # SETB, B # Адда)
Там нет необходимости беспокоиться о влиянии ОРМ.

Есть код exsample.
«слой модели настойчивости» содержит A и B.
«Уровень модели домена» содержит MA и MB.
Экземпляр MA имеет экземпляр A, и MA делегировать свое состояние на А.

/** 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.
Надеюсь, вы станете намеком.

 Смежные вопросы

  • Нет связанных вопросов^_^