Hibernate mit Spring nutzen

veröffentlicht am 13. März 2012

Spring und Hibernate zählen zu den meistverwendeten Frameworks im Java-Umfeld. Doch um diese beiden Technologien reibungslos zusammen zu nutzen, müssen zunächst ein paar Einstellungen gemacht werden. Dieser Artikel gibt eine kurze Einführung, in die Konfiguration und Nutzung der beiden Frameworks miteinander. Als Datenbank kommt hierbei MySQL zum Einsatz.

Zunächst benötigst du Spring, Hibernate und deren für dieses Beispiel gebrauchte Abhängigkeiten. Ich nutze dafür Maven – die entsprechende pom.xml sieht wie folgt aus:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>de.patrickgotthard</groupId>
	<artifactId>example</artifactId>
	<version>1.0.0-SNAPSHOT</version>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.2</version>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>3.1.1.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib</artifactId>
			<version>2.2.2</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>4.1.1.Final</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-c3p0</artifactId>
			<version>4.1.1.Final</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-simple</artifactId>
			<version>1.6.4</version>
		</dependency>
		<dependency>
			<groupId>javassist</groupId>
			<artifactId>javassist</artifactId>
			<version>3.12.1.GA</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.18</version>
		</dependency>
	</dependencies>
</project>

Ohne Maven, Ivy oder andere Tools müssen die Bibliotheken erst einmal händisch zusammengesucht werden. In der Spring Konfigurationsdatei wird zunächst eine DataSource und eine HibernateSessionFactory angelegt. Damit der Quellcode später schön schlank wird, habe ich das Autowiring und die Transaktionssteuerung von Spring aktiviert. Die gesamte Spring Konfiguration (applicationContext.xml) sieht wie folgt aus:

<?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:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">

	<!-- Needed for Autowiring -->
	<context:annotation-config />

	<!-- MySQL DataSource -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver" />
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/propertystore" />
		<property name="user" value="username" />
		<property name="password" value="password" />
	</bean>

	<!-- Hibernate SessionFactory -->
	<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="annotatedClasses">
			<list>
				<value>de.patrickgotthard.example.dto.Property</value>
			</list>
		</property>
		<property name="hibernateProperties">
			<value>
				hibernate.hbm2ddl.auto=update
				hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
			</value>
		</property>
	</bean>

	<!-- Transaction Management -->
	<tx:annotation-driven transaction-manager="txManager"/>
	<bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory" />
	</bean>

	<!-- Business Bean -->
	<bean id="business" class="de.patrickgotthard.example.business.Business" />

</beans>

Damit das Beispiel funktioniert, musst du die Datenbank propertystore anlegen und in der Bean dataSource die Verbindungsdaten zur MySQL-Datenbank abändern. Um andere Datenbanken anzubinden, muss lediglich der entsprechende Treiber eingebunden, sowie die Verbindungsdaten und der HibernateDialect angepasst werden. Natürlich sollte man die Datenbank im produktiven Einsatz nicht von Hibernate hochziehen lassen. Statt hibernate.hbm2ddl.auto=update nutzt man dann validate.

Als Beispiel habe ich einen PropertyStore gewählt. Dazu wird zunächst folgendes Persistenzobjekt angelegt:

package de.patrickgotthard.example.dto;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

@Entity
@Table(name = "properties", uniqueConstraints={@UniqueConstraint(columnNames={"property_key"})})
public class Property {

	@Id
	@GeneratedValue
	@Column(name = "id")
	private Integer id;

	@Column(name = "property_key")
	private String key;

	@Column(name = "property_value")
	private String value;

	public Integer getId() {
		return id;
	}

	public String getKey() {
		return key;
	}

	public void setKey(String key) {
		this.key = key;
	}

	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}

}

Das entsprechende Business-Objekt dazu sieht wie folgt aus:

package de.patrickgotthard.example.business;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import de.patrickgotthard.example.dto.Property;

public class Business {

	@Autowired
	SessionFactory sessionFactory;

	@Transactional
	public void saveProperty(Property property) {
		sessionFactory.getCurrentSession().saveOrUpdate(property);
	}

	@SuppressWarnings("unchecked")
	@Transactional
	public List<Property> getProperties() {
		return sessionFactory.getCurrentSession().createCriteria(Property.class).list();
	}

	@Transactional
	public Property getProperty(String key) {
		Session session = sessionFactory.getCurrentSession();
		Criteria criteria = session.createCriteria(Property.class);
		criteria.add(Restrictions.eq("key", key));
		return (Property) criteria.uniqueResult();
	}

	@Transactional
	public void deleteProperty(String key) {
		Property property = getProperty(key);
		sessionFactory.getCurrentSession().delete(property);
	}

}

Zuletzt habe ich zum Testen folgende Klasse implementiert:

package de.patrickgotthard.example.app;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import de.patrickgotthard.example.business.Business;
import de.patrickgotthard.example.dto.Property;

public class Main {

	public static void main(String[] args) {

		// Construct the spring application context
		AbstractApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

		// Register hook to shutdown Spring gracefully
		// See http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/beans.html#beans-factory-shutdown
		context.registerShutdownHook();

		// Get the business bean from context
		Business business = (Business) context.getBean("business");

		// Create simple property objects
		String hibernatePropertyKey = "hibernate_version";
		Property hibernateProperty = new Property();
		hibernateProperty.setKey(hibernatePropertyKey);
		hibernateProperty.setValue("4.x");

		String springPropertyKey = "spring_version";
		Property springProperty = new Property();
		springProperty.setKey(springPropertyKey);
		springProperty.setValue("3.1.1");

		// Write the properties to database
		business.saveProperty(hibernateProperty);
		business.saveProperty(springProperty);

		// List saved properties
		for (Property property : business.getProperties()) {
			System.out.printf("%s = %s\n", property.getKey(), property.getValue());
		}

		// Update Hibernate version property
		hibernateProperty.setValue("4.1.1");
		business.saveProperty(hibernateProperty);

		// Delete the spring version property from database
		business.deleteProperty(springPropertyKey);

		// Print the stored properties once again
		for (Property property : business.getProperties()) {
			System.out.printf("%s = %s\n", property.getKey(), property.getValue());
		}

	}

}

Wenn das Programm nun gestartet wird, sollte bei der Ausführung neben einer Reihe an Debug Meldungen folgende Konsolenausgabe zu sehen sein:

hibernate_version = 4.x
spring_version = 3.1.1
hibernate_version = 4.1.1

Quellcode

Den Quellcode dieses Artikels kannst du als zip-Archiv herunterladen.

Quellen und weiterführende Links

Kommentare

Bahnhof.

Kommentar #1 von Didi am 13. März 2012



@Didi
Sorry, aber wenn Du nur Bahnhof verstehst, ist das dein und nicht sein Problem. Sein Beispiel ist gut und funktioniert.

Kommentar #3 von Maximilian Eberl am 22. August 2012


Sehr gutes Beispiel! Vielen Dank! hat mir sehr geholfen !

Kommentar #4 von Michael am 24. September 2012


Hallo,
das ist echt ein gutes Beispiel! Kann ich aber auch mittels Java auf die Funktionen in dem Business Object ansprechen? So das ich keine Bean deklarieren müsste? Also ungefähr so:

Business business = new Business();

Gruß

Kommentar #5 von Eberhard am 16. Oktober 2012


Hello Eberhard,

wenn du das Business Objekt per new instanziierst, hat das Objekt keinen Zugriff auf die notwendige SessionFactory. Aber du kannst (und so würde ich es mittlerweise auch immer machen, zum Zeitpunkt des Verfassens kannte ich die Möglichkeit nicht) die Business-Klasse mit @Component annotieren und in der XML-Configuration das Component Scanning aktivieren:

<context:component-scan base-package="de.patrickgotthard.example" />

Dadurch werden alle Klassen, die unterhalb des angegebenen Packages liegen und u.a. mit @Component, @Service oder @Repository annotiert sind automatisch „hochgezogen“.

In der hier vorgestellten main-Methode würdest du dann schreiben:

Business business = context.getBean(Business.class);

Ich hoffe, das hilft dir weiter :)

Gruß
Patrick

Kommentar #6 von Patrick am 16. Oktober 2012


Hallo Patrick,

vielen Dank für deinen Kommentar! Hat mir sehr geholfen. Habe jetzt eine Kombination aus Apache Camel, cxf Komponente, Spring und Hibernate am Laufen und es gibt keine Session Probleme mehr.
Die Busines Klassen habe ich dann über ein Interface nach diesem Vorbild: Link eingebunden. Mittels @Repostory und habe dann mit @Autowired überall Zugriff bekommen.

Nochmals Danke für diesen guten Blogeintrag!

Viele Grüße Eberhard

Kommentar #7 von Eberhard am 16. Oktober 2012


Hey Patrick,

chapeau!! Schönes Tutorial – gerade die Reduktion auf’s Einfache hat Wirkung. Nach wochenlangem Rumgedödel mit Spring und Hibernate hat mir Deine Vorarbeit den Durchblick geschenkt!

Die besten Grüße

Stephan

Kommentar #8 von Stephan Schrader am 27. Oktober 2012


Hallo Stephan,

das Problem mit unnötig komplexen Anleitungen kenne ich nur zu gut, deshalb versuche ich meine Artikel immer so kompakt wie möglich zu halten. Wenn du gerade ein neues Projekt aufbaust, solltest du dir eventuell einmal meinen Artikel zu Spring Data JPA mit Hibernate anschauen. Ist momentan mein bevorzugtes Datenbank Framework und liefert jede Menge Funktionalitäten out-of-the-box, die sonst manuell entwickelt werden müssten.

Gruß
Patrick

Kommentar #9 von Patrick am 27. Oktober 2012


Hey,
gutes Tutorial! Ist echt schwierig geworden etwas vernünftiges zu finden, gerade für die neuen Versionen.

Lassen sich die systemabhängigen Properties (Verbindungskonfiguration) auch auslagern? Ich stelle mir ein Szenario mit verschiedenen Datenbanken vor und verschiedenen Testsystemen die alle eigene Verbindungskonfigurationen haben. Hier müßte man nach dem build noch manuell eingreifen und die Daten anpassen. Schön wäre es wenn das automatisiert passieren könnte oder abhängig von Umgebungsvariablen, die dann z.B. in maven/jenkins gesetzt werden.

cheers,
Nik

Kommentar #10 von Nik am 12. November 2012


Hallo Nik,

du kannst natürlich die Einstellungen auslagern. Hier kann man z.B. für jede Umgebung (Dev, Test, Prod) eine eigene Property Datei anlegen und per Umgebungsvariable referenzieren.

Ich denke, das hilft schonmal weiter :)

Gruß
Patrick

Kommentar #11 von Patrick am 12. November 2012


Vielen Dank für diesen ausführlichen Beitrag!

Kommentar #12 von Rdey am 09. März 2013


Hinterlasse einen Kommentar