Will man Spring zusammen mit Hibernate 3 verwenden, so muss man beachten, dass die DataAccessObjects (DAOs) nicht ohne weiteres mit Spring zusammenarbeiten. Eine gute Spring-Integration ist aber wünschenswert, weil man nur so in den Genuss der Spring-Features wie z.B. deklaratives Transaction-Management oder JTA-Transactions kommt.
Ausserdem begegnet man bei der Verwendung von Hibernate 3 mit Sicherheit auch dem Lazy Loading, das seit Hibernate 3 per Default aktiviert ist. Dieses Feature sorgt dafür, dass nicht sofort benötigte Datenbankinhalte beim "Load" gar nicht geladen werden, sondern erst dann wenn man sie tatsächlich braucht.
Das funktioniert aber nur, wenn zu einem späteren Zeitpunkt – wenn dann auf die Datenbank zugegriffen wird – die Hibernate-Session auch noch offen ist …
Session Management
HibernateSynchronizer kennt von Hause aus nur zwei verschiedene Arten des Session-Lifecycle:
- Implizite Sessions: Der RootBaseDAO verwaltet eine "current" Session und öffnet und schließt diese bei Bedarf.
- Implizite Session mit "Deferred Close": durch Überschreiben der "closeSession" Methode im RootDAO wird das automatische Schliessen der Session verhindert.
Die 2. Methode ist empfohlen, wenn "Lazy Loading" verwendet wird. Allerdings muss später die Session explizit durch "closeCurrentSession" geschlossen werden.
Beide Modi 1 und 2 bieten allerdings keine Zusammenarbeit mit Spring-Managed-Transactions hier muss ein eigener RootBaseDAO integriert werden.
HibernateSychronizer bietet diese Option an, wenn man in den Projekt-Einstellungen folgende Änderungen vornimmt:
Der neue RootDAO
Die Implementierung des neuen RootDAO stützt sich auf die ursprüngliche Implementierung, die HibernateSynchronizer mitbringt und die Klasse "SessionFactoryUtils" von Spring ab.
Alle Aufrufe aus dem ursprünglichen RootDAO, die Sessions öffnen oder schliessen, werden nun an die Klasse SessionFactoryUtils delegiert.
Dieser Code-Ausschnitt zeigt die betreffenden Methoden:
public Session getSession() {
if( deferredClose && currentSession.get() != null ) {
// reuse session
return currentSession.get().session;
}
if( session != null ) {
return session;
} else {
Session s = SessionFactoryUtils.getSession(sessionFactory,true);
if( deferredClose ) {
SessionFactoryUtils.initDeferredClose(sessionFactory);
currentSession.set(new SessionInfo(s,sessionFactory));
}
return s;
}
}
public void setSession (Session session) {
this.session = session;
}
/**
* Set the session factory
*/
public void setSessionFactory (SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* Return the SessionFactory that is to be used by these DAOs. Change this
* and implement your own strategy if you, for example, want to pull the SessionFactory
* from the JNDI tree.
*/
public SessionFactory getSessionFactory() {
return sessionFactory;
}
/**
* Close all sessions for the current thread
*/
public static void closeCurrentSession () {
SessionInfo si = currentSession.get();
if (null != si ) {
SessionFactoryUtils.processDeferredClose(si.sessionFactory);
currentSession.set(null);
}
}
/**
* Close the session
*/
public void closeSession (Session s) {
if( session == null ) {
SessionFactoryUtils.releaseSession(s,sessionFactory);
}
}
Damit nehmen alle DAO-Session automatisch an evtl. laufenden Transaktionen teil. Eine Lösung für das "Lazy Loading" ergibt sich so aber nur, wenn tatsächlich Transaktional gearbeitet wird. Hier bleibt die Session selbstverständlich während der Transaktion offen.
Und ohne Transaktionen
Nun will man aber nicht unbedingt transaktional arbeiten aber vielleicht dennoch "Lazy Loading" verwenden. Hierzu muss die Session aber offen bleiben. Ach hierfür bietet die Spring-Klasse Support an: initDeferredClose und processDeferredClose.
Welche Art des Session-Handlings man benutzen will, kann man nun wie bei HibernateSynchronizer durch Überschreiben einer Methode klären, die das eine oder andere Verhalten festlegt.
Alternativ kann man das Session-Handling auch zur Eigenschaft der DAOs machen, um so maximale Flexibilität zu erhalten.
So lässt sich einstellen, ob ..
- Sessions implizit durch die Transaktion definiert werden.
- Sessions implizit geöffnet und (bald) wieder geschlossen werden.
- Sessions lange offen gehalten werden (für Lazy-Loading) und später explizit geschlossen werden müssen.