Hibernate Problem mit Lazy Loading bei @OneToMany Collections

  • Themenstarter Themenstarter HQLNeuling
  • Beginndatum Beginndatum
H

HQLNeuling

Gast
Hallo zusammen,

ich bin absoluter Neuling in Hibernate und habe ein Problem beim Laden von Objekten aus der Datenbank. Ich habe als Beispiel in meiner Klasse Service eine Collection vom Typ ServExport und in meiner Klasse ServExport ein Objekt vom Typ Service:

Klasse Service
Java:
@OneToMany(mappedBy = "service")
private Collection<ServExport> exports = new HashSet<ServExport>();

Klasse ServImport
Java:
@ManyToOne
@JoinColumn
private Service service;

Lege ich nun Service-Objekte in die Datenbank ab und versuche sie an späterer Stelle wieder auszulesen und auf die Collections zuzugreifen, kommt die Fehlermeldung:

Java:
[2012-09-03 13:43:27,143] ERROR [main] org.slf4j.impl.JCLLoggerAdapter: failed to lazily initialize a collection of role: com.test.serviceAnwendung.db.hibernate.mapping.Service.exports, no session or session was closed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.test.serviceAnwendung.db.hibernate.mapping.Service.exports, no session or session was closed
	at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
	at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372)
	at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:365)
	at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:108)
	at org.hibernate.collection.PersistentBag.iterator(PersistentBag.java:272)
	at com.test.serviceAnwendung.db.hibernate.utils.HibernateTransactionHandler.getExports(HibernateTransactionHandler.java:509)
	at com.test.serviceAnwendung.TestAnwendung.getExports(TestAnwendung.java:114)
	at test.test.TestZugriff.main(TestZugriff.java:51)
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.test.serviceAnwendung.db.hibernate.mapping.Service.exports, no session or session was closed
	at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
	at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372)
	at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:365)
	at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:108)
	at org.hibernate.collection.PersistentBag.iterator(PersistentBag.java:272)
	at com.test.serviceAnwendung.db.hibernate.utils.HibernateTransactionHandler.getExports(HibernateTransactionHandler.java:509)
	at com.test.serviceAnwendung.TestAnwendung.getExports(TestAnwendung.java:114)
	at test.test.TestZugriff.main(TestZugriff.java:51)

Ich habe mich im Internet ein bisschen zum Lazy Loading schlau gemacht und weiß das an dieser Stelle die Inhalte der Collection noch nachträglich aus der DB gelesen werden sollen. Verstehe nicht warum das nicht funktioniert, da ich eine Session geöffnet habe. Hatte auch schonmal versucht das Lazy Loading auszuschalten (
Code:
fetch = FetchType.EAGER
), aber das hat bei mehreren Collections in einer Klasse nicht funktioniert und will ich eigentlich auch nicht wegen der Performance.

Würde mich freuen wenn mir vllt jemand einen Tipp geben könnte! 🙂
 
Ist es denn die selbe Session, mit der die Entity ursprünglich geladen wurde? Sonst ist natürlich der PersistenceContext futsch und es fliegt die LazyInitializationException. Ich kann mich auch dunkel erinnern, dass sich Sets bei dem Thema anders verhalten als Listen, ob das was damit zu tun hat, ka.
 
Hallo Danke erstmal für die Antwort! 🙂

Nein, es ist nicht die gleiche Session, denn wenn ich bei meiner Transaction die Methode commit() aufrufe, wird diese automatisch geschlossen!
Muss ich die Transaction die gesamte Zeit offen lassen solange bis ich die Collection benötige??
Weil ich lade mir ein Service Objekt aus der DB und möchte dann irgendwann (!) mal mit ner getterMethode auf die Collection zugreifen:

Java:
Service service = getService(serviceBezeichnung);
Collection<ServExport> exporte = service.getExports();
for(ServExport exp : export){ // An dieser Stelle wird die Exception geworfen
	// [...]
}

In der Methode getService(String serviceBezeichnung) lade ich das Objekt vorher:

Java:
private Service getService(String serviceBezeichnung){
	Service service = null;
        openSession();
	Transaction tx = session.beginTransaction();
	Query query = session.createQuery("select s from Service as s where s.bezeichnung = :bez");
	query.setParameter("bez", serviceBezeichnung);
	List<Service> services = query.list();
        if(services!=null){
               if(services.size()==1){
                    service = services.get(0);
               }
        }
	tx.commit(); // Hier wird die Session geschlossen
	return service;
}

Hier noch die Methode openSession():

Java:
private Session session;

public Session openSession() {
	if(session==null){
		session = HibernateSessionFactory.getInstance().getCurrentSession();
	}else if(session.isOpen()==false){
		session = HibernateSessionFactory.getInstance().getCurrentSession();
	}
	return session;
}

Das ist nicht die einzige 1:n Beziehung die ich so verdrahtet habe, die jeweiligen Objekte gebe ich an verschiedenen Stellen bis hoch zum Frontend bevor ich sie auslese! Jedes mal kommt dann die Exception...
 
Hier noch meine Hibernate Einstellungen, vllt hilft es ja:

[XML]
<hibernate-configuration>
<session-factory>

<!-- Datenbank Connection Einstellungen -->
<property name="connection.driver_class">oracle.jdbc.OracleDriver</property>
<property name="connection.url">url</property>
<property name="connection.username">user</property>
<property name="connection.password">pw</property>

<property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>

<!-- Zusaetsliche Hibernate-Einstellungen -->
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<property name="hibernate.current_session_context_class">thread</property>

<property name="c3p0.acquire_increment">3</property>
<property name="c3p0.idle_test_period">300</property>
<property name="c3p0.max_size">15</property>
<property name="c3p0.max_statements">0</property>
<property name="c3p0.min_size">3</property>
<property name="c3p0.timeout">8000</property>

<!-- Auflistung der gemappten Klassen -->
<mapping class="com.test.serviceAnwendung.db.hibernate.mapping.Service"></mapping>
<mapping class="com.test.serviceAnwendung.db.hibernate.mapping.ServExport"></mapping>
<!-- [...] Andere gemappte Klassen [...] -->

</session-factory>
</hibernate-configuration>
[/XML]
 
Persistente Entities durchlaufen verschiedene Zustände. Nachdem eine Entity geladen und die zugehörige Session geschlossen wurde, ist sie im Detached Zustand. In diesem Zustand ist es nicht möglich, lazy geladene Members abzufragen. Ein entsprechender Versuch wird mit der von Dir beobachten Exception quitiert. Du musst also dafür sorgen, dass die Entity attached ist, wenn Du auf lazy loaded Members (wie Collections) zugreifen willst. Dafür gibt es verschiedene Ansätze:
-Du hälst die Session länger offen. Ist nur eingeschränkt zu empfehlen. Eine Session ist als kurzlebiges Wegschmeiß-Objekt gedacht. Bei zu ausgiebiger Nutzung langlebiger Sessions skaliert Deine Anwendung auch nicht mehr sehr gut.
-Du öffnest eine neue Session und (re-)attachest die Entity in die neue Session bevor Du auf Members zugreifst. Das geht entweder, indem Du mit der ID der Entity diese einfach neu lädst oder indem Du ein merge aufrufst.
 
-Du öffnest eine neue Session und (re-)attachest die Entity in die neue Session bevor Du auf Members zugreifst. Das geht entweder, indem Du mit der ID der Entity diese einfach neu lädst oder indem Du ein merge aufrufst.

Danke für den Tipp, ich rufe nun session.update(service) auf bevor ich auf die Elemente von session.getExports() zugreife! Das verhindert die Exception und löst das Problem. 🙂 Jetzt muss ich nur noch schauen wie ich das schlau in meinen Code an die richtigen Stellen einbaue.

Vielen Dank! 🙂
 

Neue Themen


Zurück
Oben