Spring – Mapper Templates und Transaktions Manager nutzen

Donnerstag, 2. Juni 2011 von  
unter Fachartikel Architektur

Spring bietet in den Versionen 2.0, 2.5.x und 3.x mit den Spring Template- und TransactionManager-Klassen effektiven Abstraktions- und Implementierungs-Support für JDBC, JPA und Hibernate Datenzugriffs Strategien unter Nutzung des DAO (Data Access Object) Design Patterns an. In diesem Blog-Eintrag sollen anhand kleiner Beispiele Hinweise für die Verwendung der OR Mapper (Object Relational Mapper), Transaktions Manager und DaoSupport Klassen gegeben werden. Zielgruppe des Artikels sind Java Entwickler mit Spring Kenntnissen, die bereits Erfahrungen mit Persistenz Strategien und ORM Templates haben, oder diese und ähnliche Erfahrung ergänzen möchten.

Da beim Einsatz eines ORM Frameworks wie JPA (Java Persistence API) oder Hibernate mit Sicherheit dieselben Operationen bzw. Code-Fragmente verwendet werden, wie z.B. session.open(), session.close() oder entsprechend entityManager.close() und für Transaktionen begin(), commit(), rollback(), bietet Spring zur Unterstützung des D-R-Y (“Don’t Repeat Yourself“) Prinzips DaoSupport Klassen und zwecks Abstraktion der Transaktions Mananagement (TM) Low Level APIs Interfaces für die passende TransactionManager Implementierung.

Hier vorweg ein tabellarischer Vergleich:

Beispielprojekt

Der Einstieg in die Arbeit mit ORM Mapper Templates und Transaction Managern wird in diesem Blog-Eintrag veranschaulicht. Die einfachen Fragmente sollen Hinweise zur Anwendung der von Spring bereitgestellten Mapper, Transaktions Manager und DaoSupport Klassen geben. Für das Erstellen eines Beispielprojekts wurden die folgenden Pakete und Komponenten verwendet: Als flexible Entwicklungsumgebung die Spring Source Tool Suite 2.6.0. SR1 in der JEE 64bit Version [Download hier ], weiterhin als Buildsystem Maven mindestens in der Version 2.2.1.

Erste Schritte

Ein Projekt als Beispielapplikation kann schnell mittels Dashboard (Spring Template Project) z.B. als Spring MVC Project mit dem Namen “SpringMVCDemo” angelegt werden.

Nach erfolgreichem Anlegen befindet sich innerhalb des Workspaces nun also ein konfigurierbares und ausführbares Projekt mit den relevanten Maven Dependencies. Für den Einsatz des Spring Frameworks wird z.B. auf bereits vorhandene Blog Einträge zum Thema Spring in diesem Blog und auf die Spring Reference Dokumentation verwiesen.

DEKLARATIVES TRANSAKTIONS MANAGMENT – mit Hibernate / JPA Templates und Annotations

Um die Aufwendungen für Codierung und Einsatz von Spring und Hibernate zu verringern, bietet Spring die HibernateTemplate und JpaTemplate Klassen mit Template Methoden für Hibernate bzw. JPA Operationen an, auf deren Basis als Wrapper-Klassen für diese Operationen leicht DAO-Implementierungen erstellt werden können, die deklarativ entweder per AOP-Pointcut oder per Annotationen (@Transactional) transaktional werden und in einem Transaction Manager laufen können. (Dieser Blog Eintrag fokussiert auf die zweite Möglichkeit.) Die Methoden des HibernateTemplate und JpaTemplate stellen das richtige Öffnen und Schließen der Hibernate Sessions bzw. Entity Manager sicher und ermöglichen die Verwendung von Spring Transaktionen für JPA und Hibernate, ohne ständig denselben Transaktionscode zu wiederholen und ohne direkt Hibernate API Methoden für das Session und Transaction Management aufzurufen. Wenn es z.B. mehrere Hibernate Methoden innerhalb einer transaktionalen DAO Methode aufzurufen gibt, wird die HibernateTemplate Methode diese Methoden in derselben Session und Transaktion ausführen. Weiterhin ist das HibernateTemplate Thread-safe, wodurch eine Singleton Instanz hiervon in alle Hibernate DAOs injected werden kann. Eine HibernateTemplate Instanz benötigt hierfür nur ein gültiges sessionFactory Attribut, welches per Setter- oder Constructor-basierte Dependency Injection (DI) gesetzt werden kann und ebenfalls als Attribut des TransactionManagers gesetzt wird.

Die Annotation @Transactional kann auch eine gesamte Klasse annotieren, wobei durch zusätzliches Annotieren auf Methodenebene das für die Klasse vorgegebene Transaktionsverhalten für die explizit annotierte Methode überschrieben wird. Ebenso ist es möglich, die Annotation @Transactional auf ein Interface anzuwenden, anstatt auf eine implementierende Klasse, wodurch alle das Beispiel Interface implementierenden Methoden transaktional werden, wenn der Default-Wert proxy-target-class=“false“ des <tx:annotation-driven /> Tag-Attributs nicht mit „true“ überschrieben wird. Das <tx:annotation-driven/> Tag sucht nach per @Transactional Annotation annotierten Beans nur innerhalb desselben Contexts, in dem es auch definiert wurde. Wenn man das Tag <tx:annotation-driven/> also nur im WebApplicationContext, z.B. für ein DispatcherServlet deklariert, wird auch nur nach @Transactional annotierten Beans der in der web.xml deklarierten Controller gesucht, nicht aber nach den annotierten Service Klassen im ApplicationContext.

Für deklaratives TM für die mit der @Transactional Annotation annotierten Methoden genügt es dafür, innerhalb der Bean Deklarationsdatei das Tag <tx:annotation-driven> einzufügen, wodurch zur Laufzeit per Default nach einem TransactionManager namens transactionManager gesucht wird. Um dieses Default-Verhalten mit dem des HibernateTransactionManagers zu überschreiben, genügt es also, eine Bean der id transactionManager vom Typ HibernateTransactionManager zu deklarieren. Dieser Transaction Manager managed dann alle über seine sessionFactory geöffneten Sessions.

Ganz ähnlich kann ein Jpa DAO unter Verwendung des JpaTemplates vereinfacht werden. Hierfür benötigt man dann einen JpaTransactionManager, der alle über den von seiner entityManagerFactory erhältlichen entityManager gestarteten Transaktionen verwaltet und intern entityManager.close() ohne expliziten Codieraufwand aufruft.

Ein weiterer Vorteil beim Einsatz von HibernateTemplate oder JpaTemplate ist die Umdefinition von nativen Hibernate und JPA Exceptions in Spring Exceptions innerhalb der DataAccessException Hierarchie, die somit als Checked Exceptions nicht automatisch die Transaktion zurückrollen, weil für diese auch explizite Rollback-Ausnahmen definiert werden können. Dadurch werden z.B. eine (aus Hibernate kommende) org.hibernate.exception.ConstraintViolationException , oder eine (aus JPA kommende) javax.persistence.EntityExistsException in eine DataIntegrityViolationException übersetzt, die wiederum von DataAccessException abgeleitet ist, wenn die DAOs mit der @Repository Annotation annotiert werden. Somit wird auch ohne Rollback-Ausnahmen ein einheitliches Exception-Handling für Checked Exceptions ermöglicht.

Hier nun die Beispiele HibernateProductDao und JpaProductDao und ihre Bean Konfigurationen.

Die Möglichkeit zu nativen Hibernate oder JPA Aufrufen mit direktem Zugriff auf die darunterliegende Hibernate session oder den JPA Entity Manager hat man natürlich auch weiterhin durch Übergabe anonymer HibernateCallback() bzw. JpACallback()-Implementierungen, welche dann die Methode doInHibernate(Session session) bzw. doInJpa(EntityManager em) implementieren. Die Verwendung eines selbstgeschriebenen HibernateCallBacks empfiehlt sich z.B. für die diversen find-Methoden des HibernateTemplates, da dessen globale Einstellungen teilweise nicht-vorhersagbare Ergebnisse für die maximale Anzahl der Zeilen eines ResultSets einer Abfrage, zum Caching und zu anderen Parametern liefern. Die Implementierungen anderer C-(R)-U-D Methoden des HibernateTemplates, wie z.B. save(), delete(), update(), sind hingegen ohne Einschränkungen verwendbar. Transaktional ist beim Einsatz von Hibernate zu beachten, dass der Flush Mode implizit auf FLUSH_NEVER gesetzt wird für read-only Transaktionen. Somit wird für read-only Transaktionen flush() nur aufgerufen, wenn die Methode explizit aufgerufen wird, und der üblicherweise darauf folgende Commit ebenfalls nur noch explizit durchführbar, sobald für eine laufende oder neue Transaktion in der zuletzt aufgerufenen beteiligten Methode das Attribut read-only mit dem Wert true überschrieben wird, weshalb das D-R-Y Prinzip nicht immer überall eingehalten werden kann, falls man nicht auf nicht-transaktionale Decorator ausweichen möchte.

Hier die Beispiele zu Derivaten von HibernateDaoSupport und JpaDaoSupport und ihre Bean Konfigurationen.

Clean Code DAOs – mit Hibernate und JPA DaoSupport Klassen

Ein Hibernate DAO, der von HibernateDaoSupport abgeleitet wird, bekommt dadurch die Setter-Methoden zum Setzen der SessionFactory und des HibernateTemplates per DI. Ebenso bekommt ein Jpa DAO, der von JpaDaoSupport abgeleitet wird, dadurch die Setter-Methoden zum Setzen der EntityManageFactory und des JpaTemplates per DI. Die angesprochenen, vererbten Setter-Methoden sind final und somit in den abgeleiteten DAOs nicht überschreibbar.

Nur jeweils eines von beiden Attributen macht allerdings Sinn, in den jeweiligen DAO zu injecten, entweder die sessionFactory oder das hibernateTemplate, entweder die entityManagerFactory oder das jpaTemplate. Das andere nicht verwendete Attribut, sollte aus dem DAO aus Gründen des Clean Code entfernt werden und ebenso die nicht verwendete Bean Deklaration des entfernten Attributs aus der Bean Konfiguration.

Hier nun die Beispiele der DAOs mit der jeweiligen DaoSupport Basisklasse.

Context DI – mit Hibernate und JPA DAOs

Den kleinen Nachteil der Abhängigkeit vom Spring API (HibernateTemplate, JpaTemplate) kann man im Hibernate DAO durch Verwendung von Kontext-bezogenen Sessions und einer transaktionalen SessionFactory beheben, im JPA durch die Verwendung von JPA-typischer Context Injection mittels @PersistenceContext Annotation. Dadurch garantiert Spring die Ausführung aller Persistenz Operationen einer einzelnen Transaktion durch denselben EntityManager bzw. in derselben Hibernate Session einer einzelnen Transaktion.

Ab Hibernate 3 liefert der API-Aufruf getCurrentSession() innerhalb derselben Transaction immer dieselbe Session (sog. “Context-bezogene Session“), verwendet somit genau eine Session je Transaktion, was mit Spring’s Transaktions Support optimal funktioniert.

Im JPA gibt es bei Verwendung des <context:annotation-config/> Elements in der Bean Konfiguration auch noch die Möglichkeit, eine EntityManagerFactory zu injecten in Abhängigkeit von der in der @PersistenceUnit(unitName=“productDBUnit“) Annotation verwendeten DB-Konfiguration namens unitName=“productDBUnit“. Dadurch erhält man sowohl über die Erzeugung der EntityManager, als auch über das TM die volle Kontrolle und wird nur noch von der JPA abhängig, somit komplett unabhängig von der Spring API. Außerdem ist es durch die Verwendung des unitName-Property der @PersistenceUnit Annotation möglich, verschiedene DB-Schema-Konfigurationen per DI mit verschiedenen DAOs zu verschalten. Dabei ist allerdings zu beachten, dass die persistence.xml Dateien mit dem Element persistence-unit dieselbe hbm.xml-Mapping-Datei nicht mehrfach enthalten darf, da ansonsten eine org.hibernate.DuplicateMappingException auftritt und den Start der Anwendung verhindert. Da z.B. der annotierte JPA EntityManager die hbm.xml Mapping Dateien und die JPA Annotationen in Spring jedoch ebensogut per Autodetection erkennen kann, sollten die Mapping-Dateien, vor allem bei der Verwendung mehrer DB-Schemata, gar nicht explizit aufgelistet werden, oder direkt mit der @Entity Annotation annotierte POJOs (Plain Old Java Objects) und darauf verweisende <mapping-class>-Elemente in der hibernate.cfg.xml anstatt der Mapping-Dateien verwendet werden und entsprechend für die HibernateSessionFactory eine AnnotationSessionFactoryBean statt einer LocalSessionFactoryBean, um die genannte Exception a priori zu vermeiden.

<persistence-unit name=“productDBUnit“>
<properties>
<property name=“hibernate.product.cfgfile“ value=“/product/hibernate.cfg.xml“>
</properties>
</persistence-unit>

Für alle bisher besprochenen DAOs gilt die Notwendigkeit, entweder die Class mit der @Transactional Annotation zu annotieren, oder jede einzelne Mehode der DAO Implementierung (die Aktivierung durch das Element <tx:annotation-driven> in der Bean-Konfiguration vorausgesetzt). Dies bewirkt die Ausführung aller Persistenz-Operationen einer DAO Methode innerhalb derselben Transaktion und somit in derselben Session. Zusätzlich werden alle von einem darüberliegenden Service-Layer gestarteten Transaktionen für die darin aufgerufenen DAO-Methoden deren Ausführung in derselben Session bewirken. Dabei ist zu beachten, die Transaktionsklammer aus Gründen der Resourcen-Verfügbarkeit und der Laufzeit-Performance immer möglichst klein zu halten.

Hier nun die Beispiele der DAOs mit der SessionFactory DI (Hibernate) bzw. EntityManagerFactory DI (JPA).

Abschließend noch eine kurze Auflistung verschiedener, interessanter Transaktionsmanager:

Alle in diesem Blog-Eintrag verwendeten Beispiele finden sich übrigens hier.

Quelle dieses Blog Eintrags sind die bei SpringSource frei verfügbare Spring Referenz, verschiedene Spring Bücher aus dem Apress-Verlag, das Buch Spring im Einsatz von Craig Walls aus dem Hanser Verlag, das Buch „Hibernate“ von Sebastian Hennebrüder, das Buch „Spring & Hibernate“ aus dem Hanser Verlag, das Buch mit dem Titel „Spring 3“ von Eberhard Wolff aus dem dpunkt Verlag, sowie eigene Erfahrungen mit dem Spring Framework, den ORM Mappern und Transaktions Managern.

Kommentare

2 Kommentare zu “Spring – Mapper Templates und Transaktions Manager nutzen”

  1. Von Spring – Hibernate und Transaktionen : binaris informatik GmbH am Mittwoch, 20. Juli 2011 07:33

    […] Für alle bisher besprochenen DAOs muß entweder die Class mit der @Transactional Annotation annotiert werden, oder jede einzelne Mehode der DAO Implementierung (die Aktivierung durch das Element in der Bean-Konfiguration vorausgesetzt). Dies bewirkt die Ausführung aller Persistenz-Operationen einer DAO Methode innerhalb derselben Transaktion des definierten transactionManager unter Verwendung derselben HibernateSession, wenn z.B. von dem darüberliegenden WebService-Layer bzw. in der Business Facade eine Transaktion gestartet wird und dann die darin aufgerufenen DAO-Methoden mit derselben per getCurrentSession()-Methode erhaltenen Session des HibernateTransactionManagers ausgeführt werden, wenn diese aufgerufenen DAO-Methoden Transaktionen unterstützen. Für die genaue Verwendung der @Transactional Annotation wird auf in diesem Blog vorhandene Eintrage zum Thema Spring verwiesen. […]

  2. Von Services – mit Spring und JPA Implementierungen : binaris informatik GmbH am Montag, 24. Oktober 2011 01:55

    […] @Transactional Annotation auf JPA- und Hibernate-DAO Ebene erklärt der bereits vorhandene Artikel “Spring – Mapper Templates und Transaktions Manager nutzen” in diesem […]

Einen Kommentar hinzufügen...

Sie müssen registriert und angemeldet sein um einen Kommentar zu schreiben.