# Hibernate Session closed nach Transaction commit?



## eliot (6. Jul 2009)

Hallo,

ich nutze hier Hibernate 3.3.0.SP1 und habe ein Problem mitt meiner Hibernate Session.
Ich habe eine Klasse Unternehmen eine Eine Klasse Fahrer.
Ein Unternehmen hate eine Liste von Fahrern (bidirektional, LAZY).

Ich persistiere ein Unternehmen mit mehreren Fahrern in einer Transaktion.

sf.getCurrentSession().beginTransaction();
...do some persist...
sf.getCurrentSession().getTransacrtion.commit();

So weit, so gut;
Ich setzte das Unternehmen auf null und hole es mir erneut aus der DB.
Ich habe die Session nicht geschlossen!
Wenn ich nun durch die Fahrer iterieren will,
bekomme ich eine  org.hibernate.LazyInitializationException, obwohl die Session vorhanden
und nicht geschlossen ist?!
Überprüft habe ich dieses vor dem Aufruf des Iterierens mit session.isOpen().

Der Session Kontext ist auf thread gestellt und alle Operation erfolgen im gleichen Thread.

Habe ich jetzt einen Denkfehler oder paßt da in hibernate etwas nicht?


----------



## SlaterB (6. Jul 2009)

> Überprüft habe ich dieses vor dem Aufruf des Iterierens mit session.isOpen().

prüfe dies auch während des Iterierens bzw. im catch der Lazy-Exception,
vergewissere dich auch ganz genau, um welches Objekt es aktuell geht und auf welche Session du schaust,
am besten mit hashCode() und einem Log a la

System.out.println("lade Unternehmen "+u.hashCode()+", in Session "+s.hashCode());

...

System.out.println("iteriere nun Unternehmen "+u.hashCode()+", Session ist "+s.hashCode()+", offen? "+s.isOpen());
usw.

dann gibts noch 
 System.out.println(getSession().toString());
mit Ausgabe des Caches, also z.B.
"SessionImpl(PersistenceContext[entityKeys=[EntityKey[package.Klassenname#1373], EntityKey[package.Klassenname#1374].."

das bringt zwar nicht den genauen HashCode, aber zumindest die gecachten Objekte mit Id,
wenn da nix drinsteht, dann sind Lazy-Exceptions wahrscheinlich

getSession().contains(objekt)
hilft auch, wenn das am Anfang direkt nach dem Laden true ergibt und bei der Exception false,
dann rufe das nach dem Laden alle x Befehle auf, dann siehst du, ab welcher Stelle es nicht mehr klappt


----------



## eliot (6. Jul 2009)

Hallo,

so ich habe das ganze nochmal nachvollzogen, hier ein kleines Beispiel (Pseudo Code):

facade.beginTransaction();
facade.persist(company);
facade.commitTransation;

company=null;

facade.beginTransaction();
company = facade.getCompany(); //ist mitlerweile nötig, keine Selects mehr außerhalb einer Session, sehr merkwürdig
facade.getCurrrentSession().contains(company); //gibt true;

for(Company c:company.getVehicles()){
System.out.println(c);
}
facade.commitTransaction(); //alles ok soweit

//jetzt kommt das Interessante:

company=null;

facade.beginTransaction();
company = facade.getCompany(); 
facade.getCurrrentSession().contains(company); //gibt true;
facade.commitTransaction(); 

facade.getCurrrentSession().contains(company); //gibt false;

for(Company c:company.getVehicles()){
System.out.println(c);
} //lazy loading exception


Die Session ist die ganze Zeit über das selbe Objekt!

Fazit:
Select sind nur noch innerhalb einer Transaktion möglich?!
Lazy loading kann nur innerhalb einer Transaction aufgelöst werden.

Ja bin ich denn deppert?
Eine kleine google Session sagt das glieche: Selects nur in Transaction,
somit kann das lazy loading nicht aufgelöst werde, wenn keine Transaction offen ist.

damit hätten Transaktionen den gliechen Stellenwert wie Sessions.
Nun steht das aber krassen Gegensatz zu dem was ich mal gelernt habe:

Transkation: wenn Änderungen in der DB vorgenommen werden, sperrt Zeilen einer Tabelle, möglichst kurz
Session: für selects, speert keine Zeilen Tabelle, pro Thread.

Was, wieso, warum?
Ich versteh nichts mehr, ...


----------



## SlaterB (6. Jul 2009)

wußte ich jetzt auch gar nicht so genau, scheint als gegeben hinzunehmen zu sein

vielleicht deswegen:


> Because Hibernate can't bind the "current session" to a transaction, as it does in a JTA environment, it binds it to the current Java thread. It is opened when getCurrentSession() is called for the first time, but in a "proxied" state that doesn't allow you to do anything except start a transaction. When the transaction ends, either through commit or roll back, the "current" Session is closed automatically.


https://www.hibernate.org/42.html#A6
bei anderen Sessions (Pool) vielleicht weniger restriktiv

---

oder das ist auch generell nicht so schlecht, zu transaktionsgesicherten Daten später noch was ohne Transaktion dazuladen..,
na ich möchte da nicht zuviel philosophieren


----------



## byte (6. Jul 2009)

eliot hat gesagt.:


> Transkation: wenn Änderungen in der DB vorgenommen werden, sperrt Zeilen einer Tabelle, möglichst kurz
> Session: für selects, speert keine Zeilen Tabelle, pro Thread.



Dann hast Du offenbar beim Thema Transaction Isolation gepennt. 

Hibernate braucht immer eine offene Transaktion. Aber das ist ja auch gut so. Die Alternative wäre Auto-Commit und das sollte man nun wirklich vermeiden.

Natürlich werden bei offener Transaktion nicht perse Tabellenzeilen gesperrt! Was die DB genau macht, hängt von der Art der DB und der realisierten Isolation der Transaktionen ab. Wenn Deine DB wirklich die Zeilen während der Transaktion lockt, würde ich mir mal auf ne vernünftige DB umsteigen. 


Wenn Du nicht möchtest, dass Deine Session mit dem Commit geflusht und geschlossen wird, dann darfst Du sie nicht per getCurrentSession() holen sondern per openSession(). Dann musst Du Dich aber auch manuell ums flushen und closen kümmern. IdR ist das unnötig.

Wenn Du Lazy Init Exceptions bekommst, musst Du das Objekt vorher entweder wieder an eine neue Session binden oder Du lädst im Idealfall die nötigen Daten per FETCH JOIN direkt vor. Dann sparst Du Dir auch das zusätzliche SELECT.


----------



## eliot (7. Jul 2009)

Hallo,

das würde den Unterschied erklären, da ich früher tatsächlich über OpenSession gearbeitet habe
und erst für kurzem auf getCurrentSession umgestiegen bin.
Das Objekt erneut an die DB binden? D.H. Transkation öffnen, danach eine resfresh?

Das Problem ist folgendes: Ich habe mehrere Threads, jeder soll seine eigene Session haben.
Genau dieses bietet mir getCurrentSession. Wenn ich mit openSession Arbeite müßte ich
ich eigene Strategien entwickeln um für jeden Thread eine Session zu haben, oder?

regards
eliot

EDIT: Was ich allerdings nicht verstehe: Wieso brauch Hibernate eine Transaktion beim Select auf eine Tabelle?
Und wieso nur über getCurrentSession()? und bei openSession nicht? Wo liegt der Unterschied?



byto hat gesagt.:


> Dann hast Du offenbar beim Thema Transaction Isolation gepennt.
> 
> Hibernate braucht immer eine offene Transaktion. Aber das ist ja auch gut so. Die Alternative wäre Auto-Commit und das sollte man nun wirklich vermeiden.
> 
> ...


----------



## byte (7. Jul 2009)

Du kannst das Scope der Current Session konfigurieren durch den Parameter _hibernate.current_session_context_class_. Der Default Wert ist jta, wodurch die Session an die aktuelle Transaktion gebunden ist. Wenn Du den Wert auf thread machst, sollte die Session beim Commit nicht geschlossen werden, bin mir da aber nicht sicher. Was hast Du da konfiguriert?

Es ist auf jeden Fall nicht anzuraten, die Session zu lange offen zu lassen. Du kannst Dir jeder Zeit eine neue Session holen und eine Detached Entity über die neue Session speichern (per update() oder merge(), siehe Abschnitt 10.6 in der Referenz Doku).


----------



## eliot (7. Jul 2009)

Hallo,

mein Scope ist Thread.
ich habe es jetzt wie folgt verstanden und im Programm nachvollzogen:

getCurrentSession():

Liefert pro Thread die gleiche Session (bei Scope Thread), Transaktion nötig, Managed by Hibernate,
Session wird nicht geschlossen, allerdings müssen die Entities mit resrech wieder an die Session
gebunden werden, bei einer neuen Transaktion.

openSession():

Neue Session, ich muss mich selber um flush und close kümmern.

Vielen Dank für eure Hilfe!

Je nach Situation ist das eine oder das andere zu verwenden, mischen scheint kein Problem
darzustellen.



byto hat gesagt.:


> Du kannst das Scope der Current Session konfigurieren durch den Parameter _hibernate.current_session_context_class_. Der Default Wert ist jta, wodurch die Session an die aktuelle Transaktion gebunden ist. Wenn Du den Wert auf thread machst, sollte die Session beim Commit nicht geschlossen werden, bin mir da aber nicht sicher. Was hast Du da konfiguriert?
> 
> Es ist auf jeden Fall nicht anzuraten, die Session zu lange offen zu lassen. Du kannst Dir jeder Zeit eine neue Session holen und eine Detached Entity über die neue Session speichern (per update() oder merge(), siehe Abschnitt 10.6 in der Referenz Doku).


----------

