# (Spring+Hibernate) Verwendung von HibernateTemplate und persistentes Objekt?



## DarthShader (2. August 2009)

Hallo,

ich verwende Spring in Kombination mit Hibernate, und die Hilfsklassen, die Spring zur Verfügung stellt, wie z.B. HibernateDaoSupport und HibernateTemplate.

Wenn ich eine ganz einfach DAO verwende, die so aussieht:


```
public class HibernateProtocolDaoImpl extends HibernateDaoSupport implements ProtocolDao {
	@Override
	public void saveProtocol( Protocol protocol ) {
		getHibernateTemplate().saveOrUpdate( protocol );
	}
}
```

dann ist es ja so, das Spring für mich das Öffnen und Schließen der Hibernate Session übernimmt. Bedeutet das aber, dass ich nach dem Aufruf von "getHibernateTemplate().saveOrUpdate" kein persistentes Objekt haben? Denn das hier geht anscheinend nicht:


```
public class HibernateProtocolDaoImpl extends HibernateDaoSupport implements ProtocolDao {
	@Override
	public void saveProtocol( Protocol protocol ) {
		getHibernateTemplate().saveOrUpdate( protocol );
		
		protocol.setIrgendwas( true );
	}
}
```

Das Feld "irgendwas" landet dann nicht in der Datenbank, wird von Hibernate also nicht automatisch aktualisiert, auch nicht wenn ich ein "flush()" mache.

Habe ich es also richtig verstanden, dass mit dem "getHibernateTemplate().saveOrUpdate(..)" nach dem Aufruf die Hibernate Session geschlossen ist, und ich kein persistentes Objekt habe? So müsste ich in vielen Fällen ja auf den Komfort von HibernateTemplate verzichten, um "richtig" mit den Objekten (im Hibernate-Sinne) arbeiten zu können.


Über Eure Hilfe würde ich mich sehr freuen


Vielen Dank!


----------



## Oliver Gierke (3. August 2009)

Wie hast du denn die Transaktionen konfiguriert? Gibt es da überhaupt eine drumherum? Normalerweise hat die Session die gleiche Lebensdauer wie die Transaktion. Hast du keine Transaktion wird für jeden direkten Call auf das Template eine Session aufgemacht.

Weiterhin glaube ich, dass du das Persistenzverhalten von Hibernate mit dem von JPA verwechselst. JPA persistiert Änderungen am Objektgraphen beim commit der Transaktion, so dass kein explizites save notwendig ist. Dass Hibernate das auch tut wäre mir neu. Du kommst also um einen zweiten saveOrUpdate Call nit herum.

Warum eigentlich nicht gleich JPA? 

Gruß
Ollie


----------



## DarthShader (3. August 2009)

Hallo Oliver,

vielen Dank für Deine Antwort. Ich habe noch keine Transaktionen definiert, dies ist auch für meine Frage glaube ich nicht so relevant bzw. ich hätte sagen sollen, dass ich die Frage in Bezug auf das Nichtvorhandensein von Transaktionsdefinitionen beziehe.

JPA ist ja Teil der EJB 3 Spezifikation - Hibernate stellt eine Implementierung der JPA dar. Damit könnte man sagen, dass ich JPA verwende, wenn ich Hibernate verwende - soweit zu meinem bescheidenen Wissensstand 

Damit hat sich die Frage, ob Hibernate überhaupt in der Lage ist, solche updates automatisch zu machen, ja fast erledigt. Also Hibernate kann das auf jeden Fall - soweit ich weiß, sind diese Einflüsse sogar von Hibernate nach JPA geflossen.

Wenn ich Spring nicht verwende, dann geht solch ein Arbeiten auf einem persistenten Objekt auf jeden fall. Die DB wird aktualisiert, ohne dass man dies selbst tun müsste.

Ich weiß nur eben nicht genau, wie sich das bei der Verwendung von Springs HibernateTemplate verhält, weil das ja die Session selbstständig öffnet und schließt.


----------



## Oliver Gierke (3. August 2009)

DarthShader hat gesagt.:


> Hallo Oliver,
> 
> vielen Dank für Deine Antwort. Ich habe noch keine Transaktionen definiert, dies ist auch für meine Frage glaube ich nicht so relevant bzw. ich hätte sagen sollen, dass ich die Frage in Bezug auf das Nichtvorhandensein von Transaktionsdefinitionen beziehe.


Ohne Transaktion ist nach dem Call die Session weg. Nachzuschauen im HibernateTemplate, Methode doExecute(HibernateCallback, boolean , boolean). Da ist im finally Block explizit die Unterscheidung drin.



> JPA ist ja Teil der EJB 3 Spezifikation - Hibernate stellt eine Implementierung der JPA dar. Damit könnte man sagen, dass ich JPA verwende, wenn ich Hibernate verwende - soweit zu meinem bescheidenen Wissensstand


Das ist falsch. JPA ist ein API. D.h. solang du nicht gegen die JPA programmierst, nutzt du sie nicht . Nimmt man nun diesen Fakt als gegeben hin, stellt sich die Frage ob Hibernate diese API implementiert. Hibernate Core (so wie du ihn z.B. benutzt) tut dies explizit *nicht*. Allerdings gibt es aus dem Hibernateprojekt eine Implementierung der JPA, die unter der Haube den Hibernate Core implementiert. Das ist ein wichtiger Unterschied. 



> Damit hat sich die Frage, ob Hibernate überhaupt in der Lage ist, solche updates automatisch zu machen, ja fast erledigt. Also Hibernate kann das auf jeden Fall - soweit ich weiß, sind diese Einflüsse sogar von Hibernate nach JPA geflossen.
> 
> Wenn ich Spring nicht verwende, dann geht solch ein Arbeiten auf einem persistenten Objekt auf jeden fall. Die DB wird aktualisiert, ohne dass man dies selbst tun müsste.


So genau steck ich da nicht drin, aber mit der geschlossenen Session ist halt eben essig. Ein Persistenzkontext ist in der Hibernateimplementierung der JPA über die Session abgebildet, d.h. dieser Autopersistiermechanismus funktioniert nur innerhalb einer offenen Session.



> Ich weiß nur eben nicht genau, wie sich das bei der Verwendung von Springs HibernateTemplate verhält, weil das ja die Session selbstständig öffnet und schließt.


Siehe oben. Nur keine Angst vor dem Sourcecode. Der ist bei Spring in großen Teilen sehr verständlich .


----------



## DarthShader (4. August 2009)

Hallo Oliver,

danke für Deine ausführliche Antwort und die "Hintergrundinformationen" zu Hibernate und JPA. Ich sollte vielleicht einfach den Nutzen von HibernateTemplate in meinem Code etwas hinterfragen - auch den Sourcecode von Spring schaue ich mir einmal an, danke!


----------



## Oliver Gierke (4. August 2009)

Kein Thema . Wie gesagt, wenn du irgendne Möglichkeit hast, deine DAOs direkt gegen JPA zu programmieren, ist das das einfacherere und sauberere Programmiermodell. Zudem gibt es aufbauen auf Spring und JPA eine nützliche Library (http://hades.synyx.org - shameless plug ) die hilft, Datenzugriffscode auf ein minimum zu reduzieren. Grundsätzlich lässt sich sagen, dass JPA wohl "the way to go" ist.

Gruß
Ollie


----------



## DarthShader (4. August 2009)

Hm ok - ich hoffe nur nicht, dass ich jetzt eine falsche Architekturentscheidung getroffen habe. Generell ist es so, dass ich mein Domain Modell komplett mit Annotationen der JPA konfiguriert habe. Das ist ja nicht Hibernate-Spezifisch. Als müsste es ja theoretisch möglich sein, mit nicht all zu großem Aufwand zu JPA zu wechseln.

Ich war bisher immer sehr von Hibernate angetan, sehr mächtig mit guten Tools. Direktes JPA, auch in Verbindung mit dem von Dir genannten Framework, werde ich mir mal anschauen.

Ich scheue mich immer etwas davor, solche kleinen Framework zu verwenden - ist das Major? Wird es noch lange supported? Stehe ich bei einem Problem irgendwann alleine da?  Bei Hibernate sind die Antworten klar.


----------



## DarthShader (4. August 2009)

Hallo Oliver,

wenn ich nocheinmal nachfragen darf, welche Implementierung der JPA würdest Du empfehlen? Momentan habe ich einen Server, der lediglich in Spring und Hibernate umgesetzt ist, also ohne Servlet Container bzw. Application Server.


----------



## Oliver Gierke (4. August 2009)

DarthShader hat gesagt.:


> Hm ok - ich hoffe nur nicht, dass ich jetzt eine falsche Architekturentscheidung getroffen habe. Generell ist es so, dass ich mein Domain Modell komplett mit Annotationen der JPA konfiguriert habe. Das ist ja nicht Hibernate-Spezifisch. Als müsste es ja theoretisch möglich sein, mit nicht all zu großem Aufwand zu JPA zu wechseln.



Du hast keine falsche Architekturentscheidung getroffen, sonder eine vielleicht suboptimale Technologieentscheidung. Anders rum wäre die Sache vermutlich problematischer. Du hast einen DAO Layer, d.h. eine Implementierung bereitzustellen, die jetzt auf JPA aufsetzt sollte nicht das Problem sein. Es gibt für JPA ähnlich wie für Hibernate ein Template, dass man allerdings eigentlich nicht braucht. Du kannst im Allgemeinen direkt @EntityManager in den DAOs benutzen.



> Ich scheue mich immer etwas davor, solche kleinen Framework zu verwenden - ist das Major? Wird es noch lange supported? Stehe ich bei einem Problem irgendwann alleine da?  Bei Hibernate sind die Antworten klar.


In dem Fall fragst du den Falschen, weil ich quasi Autor der Bibliothek bin. Der Grund dieses Ding anzufangen war für mich, dass man viele grundsätzliche Dinge die Persistenz angehen eh in jedem Projekt wieder codet bzw. es jede Menge Best Practices gibt, für die es noch keine OpenSource Implementierung gab.

Das Ding macht eigentlich 4 Dinge: es implementiert oben verlinkten Artikel, was dazu führt, dass du für Standard CRUD Operationen ein generisches DAO bekommst, dass du nur für alle Entitäten nutzen kannst (1 DAO pro Entität, streng typisiertes Interface im Gegensatz zum EntityManager). Zweitens gibt es jede Menge Hilfsklassen für Entitäten, Hilfestellung bei Pagination (Verteilen von Anfrageergebnissen auf Seiten usw.). Der 3. Punkt ist das Ausführen von Queries durch einfaches Deklarieren von Methoden in Interfaces. Keine weitere Implementierung notwendig. Darüber hinaus gibt es noch eine Reihen von Supportfeatures, wie transparentes Auditing, ein Spring Namespace usw. Wichtig hierbei ist, dass du das ganze der Tradition von Spring folgend selektiv nutzen kannst, also immer nur das, was man wirklich braucht.

Schau dir mal die Beispielapplikation an, die gibt nen guten Überblick über die möglichen Tiefen der Nutzung. Spannend hierbei ist auch, dass es möglich ist sich in einen bestehenden Data Access Layer basierend auf Hibernate reinzuhängen und z.B. zu Test- und Experimentierzwecken mal einzelne DAOs mit Hades zu bauen und dabei den Rest auf plain Hibernate zu lassen usw. Details, wie man das konfiguriert gibt es in der Referenzdoku im FAQ Teil.

Gruß
Ollie

PS: generell empfehle ich Hibernate... OpenJPA ist halt nach der JPA Schluss und es gibt einige Stellen, an denen man beim Mapping schon mal auf proprietäre Annotations zurückgreifen muss. Das geht dann halt nur mit Hibernate und Toplink. Hibernate arbeitet ja mit Bytecodeenhancement (cglib), Toplink setzt auf Loadtimeweaving (aop). Ich persönlich bevorzuge daher Hibernate, da das weniger Implikationen auf den Betrieb hat. Desweiteren gibt es bei Toplink noch hier und da ein paar quirks, die relativ hässlich sind...


----------



## DarthShader (4. August 2009)

Hi,



> In dem Fall fragst du den Falschen, weil ich quasi Autor der Bibliothek bin.



jetzt verstehe ich auch den Smiley da oben neben dem Hades-Link  Der Untertitel des Artikels den Du erwähnst, lautet "Build a generic typesafe DAO with Hibernate and Spring AOP". Kann man mit Deinem Hades auch in bezug auf Spring und Hibernate etwas anfangen, oder werde ich nur glücklich, wenn ich ne reine JPA Implementation und Dein Hades verwende?

Ich weiß nicht, ob ich JPA schon richtig verstehe - es gibt da also mehrere Implementierungen? Auch welche, die nicht im Rahmen eines Applikations-Servers laufen?


Mal ganz am Rande: ich interessiere mich sehr für die Enterprise Entwicklung mit Java. Allerdings muss ich immer wieder feststellen, wieviele Technologien, Best Practices, Architekturen etc. es noch zu lernen gibt. Ich versuche mein Möglichstes, ständig auf dem Laufenden zu sein und stets dazu zu lernen. Leicht ist es nicht, da mitzuhalten  Deshalb bitte ich sehr dankbar über solche Threads, in denen es um Vorgehensweisen und allgemeine Technologien geht.


----------



## Oliver Gierke (4. August 2009)

DarthShader hat gesagt.:


> Kann man mit Deinem Hades auch in bezug auf Spring und Hibernate etwas anfangen, oder werde ich nur glücklich, wenn ich ne reine JPA Implementation und Dein Hades verwende?


Hades funktioniert grundsätzlich mit jeder JPA Anwendung. Spring macht das Leben leichter und wird auch zur Laufzeit benötigt, jedoch wirklich zwingend nur als Bibliothek. D.h. man muss nciht unbedingt Springkonfiguration oder Springs Dependency Injection nutzen. Einen Teil der Funktionalität kann man nur mit bestimmten Persistenzprovidern nutzen. Hierzu zählen Hibernate und Eclipselink/Toplink. Darüber hinaus integriert sich Hades prima in eine App, in der bereits ein DAO Layer auf Basis von reinem Hibernate (wie in deinem Fall) existiert.



> Ich weiß nicht, ob ich JPA schon richtig verstehe - es gibt da also mehrere Implementierungen? Auch welche, die nicht im Rahmen eines Applikations-Servers laufen?


Ja. Referenzimplementierung ist EclipseLink (früher Toplink). Wurde von Oracle gestiftet und liegt jetzt bei der Eclipse Foundation. Hat mit der IDE allerdings nichts zu tun. Darüber hinaus gibt es halt noch Hibernate (IMHO sogar die bessere Implementierung) und einen recht unbekannten Anbieter namens DataNucleus, der z.B. in der Google AppEngine läuft (frag mich nicht, warum Google den ausgewählt hat). Von Apache gibt es noch OpenJPA, das aber wirklich nur den JPA Standard implementiert und kein Feature mehr (was in vielen Fällen ein Problem ist). JavaEE 5 zertifizierte Appserver bringen also eine solche Implementierung mit. In JBoss läuft logischerweise Hibernate, im Glassfish eher TopLink.

Gruß
Ollie


----------



## Thomas Darimont (4. August 2009)

Hallo,



> Weiterhin glaube ich, dass du das Persistenzverhalten von Hibernate mit dem von JPA verwechselst. JPA persistiert Änderungen am Objektgraphen beim commit der Transaktion, so dass kein explizites save notwendig ist. Dass Hibernate das auch tut wäre mir neu. Du kommst also um einen zweiten saveOrUpdate Call nit herum.



Ich glaube schon, dass Hibernate das macht. Insbesondere dann, wenn man die entites mit saveOrUpdate(...) persistiert und eine entsprechende Transaktion "drumherum" gespannt ist.

Hier ein kleines minimal Beispiel dazu:

```
package de.tutorials.persistence;
import de.tutorials.domain.Person;

public interface IPersonDao {
    void save(Person person);
    Person getBy(int id);
}
```


```
package de.tutorials.persistence.internal;

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import de.tutorials.domain.Person;
import de.tutorials.persistence.IPersonDao;

public class PersonDao extends HibernateDaoSupport implements IPersonDao {
    public Person getBy(int id) {
        return (Person) getHibernateTemplate().get(Person.class, id);
    }

    public void save(Person person) {
        getHibernateTemplate().saveOrUpdate(person);
    }
}
```


```
package de.tutorials.services;

import de.tutorials.domain.Person;

public interface IPersonService {
    void register(Person person);
    Person getBy(int id);
}
```


```
package de.tutorials.services.internal;

import org.springframework.transaction.annotation.Transactional;

import de.tutorials.domain.Person;
import de.tutorials.persistence.IPersonDao;
import de.tutorials.services.IPersonService;

public class PersonService implements IPersonService {

    IPersonDao personDao;

    @Transactional
    public void register(Person person) {
        personDao.save(person);
        
        //Nach dem Speichern (attachen der Entity mit einer Session) verändern wir die Entity.
        //die Änderung werden beim verlassen der Methode durch das committen der transaktion und flushen der Session 
        //entsprechend an die Datenbank übergeben.
        person.setName("Furz");
    }

    @Transactional
    (
            //readOnly=true //wird das gesetzt werden die Änderungen natürlich nicht an die Datenbank weitergegeben
    )
    public Person getBy(int id) {
        Person p = personDao.getBy(id);
        
         //p.setName("Bubu" + System.currentTimeMillis());
        //Nach dem verlassen der getBy Methode wird die aktive Transaktion comitted und dabei ein flush der Hibernate Session gemacht.
        //Dabei deshalb sieht man in der Datenbank nach dem verlassen dieser Methode die Änderungen die man an der Attachedten Entity gemacht hat.

        return p;
    }

    public IPersonDao getPersonDao() {    return personDao; }
    public void setPersonDao(IPersonDao personDao) { this.personDao = personDao; }
}
```

Person Pojo:

```
package de.tutorials.domain;

import java.util.Date;

public class Person {

    int id;
    String name;
    Date birthday;

    public Person() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    
    @Override
    public String toString() {
        return this.id + " " + this.name +" " + this.birthday;
    }

}
```


```
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="de.tutorials.domain">
    <class name="Person">
        <id name="id" type="int">
            <generator class="assigned" />
        </id>
        <property name="name"/>
        <property name="birthday"/>
    </class>
</hibernate-mapping>
```

Das Beispiel

```
package de.tutorials;

import java.util.Date;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

import de.tutorials.domain.Person;
import de.tutorials.services.IPersonService;

public class Main {

    /**
     * @param args
     */
    public static void main(String[] args) {
        ApplicationContext context = new FileSystemXmlApplicationContext("conf/context.xml");
        IPersonService personService = (IPersonService)context.getBean("personService");
        Person p = new Person();
        p.setId(0);
        p.setName("Test0");
        p.setBirthday(new Date());
        personService.register(p);
        
        Person p1 = personService.getBy(0);
        
        System.out.println(p1);
    }
}
```

Ausgabe:

```
0 Furz 2009-08-04 23:49:43.0
```

Die Spring Config:

```
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
    ">

    <bean id="dataSource" destroy-method="close"
        class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="oracle.jdbc.OracleDriver" />
        <property name="url" value="jdbc:oracle:thin:@localhost:1521:XE" />
        <property name="username" value="tutorials" />
        <property name="password" value="tutorials" />
    </bean>


    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mappingResources">
            <list>
                <value>de/tutorials/domain/Person.hbm.xml</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <value>
                hibernate.dialect=org.hibernate.dialect.Oracle10gDialect
      </value>
        </property>
    </bean>


    <bean id="personDao" class="de.tutorials.persistence.internal.PersonDao">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <bean id="personService" class="de.tutorials.services.internal.PersonService">
        <property name="personDao" ref="personDao" />
    </bean>


    <bean id="txManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <tx:annotation-driven transaction-manager="txManager" />
</beans>
```
Gruß Tom


----------



## DarthShader (5. August 2009)

Hallo Oliver,

ich danke Dir für die vielen hilfreichen Informationen. Deine Bibliothek werde ich mir auf jeden fall ansehen, ich habe heute auch den von Dir verlinkten Artikel (Don't repeat your DAO) gelesen.


----------



## Oliver Gierke (5. August 2009)

Hey Thomas,

danke für das Beispiel. Ich denke man kann nun festhalten, dass das Autopersistieren in JPA und Hibernate gleich erfolgt. Jedoch ist wichtig, dass dies nur innerhalb des Lebenszyklus der Session (die normalerweise an eine Transaktion gebunden ist) passiert. Wird keine Transaktion aufgemacht ist direkt nach dem Aufruf des HibernateTemplates die Session zu und damit kein Autopersist mehr möglich.

Gruß
Ollie


----------



## Thomas Darimont (5. August 2009)

Hallo,



> danke für das Beispiel.



Immer wieder gerne 



> Ich denke man kann nun festhalten, dass das Autopersistieren in JPA und Hibernate gleich erfolgt. Jedoch ist wichtig, dass dies nur innerhalb des Lebenszyklus der Session (die normalerweise an eine Transaktion gebunden ist) passiert. Wird keine Transaktion aufgemacht ist direkt nach dem Aufruf des HibernateTemplates die Session zu und damit kein Autopersist mehr möglich.



Hui Theserverside... kommst ja ganz schön rum in der letzten Zeit ;-)

Gruß Tom


----------

