Spring ist eines der am meisten verwendeten Java EE Frameworks und Hibernate ist das beliebteste ORM-Framework. Deshalb wird die Kombination aus Spring und Hibernate häufig in Unternehmensanwendungen eingesetzt.
Spring Hibernate
In diesem Tutorial werden wir heute Spring 4 verwenden und es mit Hibernate 3 integrieren, und dann dasselbe Projekt aktualisieren, um Hibernate 4 zu verwenden. Da es viele Versionen sowohl für Spring als auch für Hibernate gibt und das Spring ORM-Artefakt sowohl Hibernate 3 als auch Hibernate 4 unterstützt, ist es gut, dass ich alle Abhängigkeiten aufliste, die ich in meinem Projekt verwendet habe. Beachten Sie, dass ich festgestellt habe, dass nicht alle Spring- und Hibernate-Versionen kompatibel sind. Die unten aufgeführten Versionen haben bei mir funktioniert, also denke ich, dass sie kompatibel sind. Wenn Sie andere Versionen verwenden und java.lang.NoClassDefFoundError erhalten, bedeutet dies, dass sie nicht kompatibel sind. Meistens liegt das daran, dass Hibernate-Klassen von einem Paket in ein anderes verschoben wurden, was diesen Fehler verursacht. Zum Beispiel wurde die Klasse org.hibernate.engine.FilterDefinition in neueren Hibernate-Versionen nach org.hibernate.engine.spi.FilterDefinition verschoben.
- Spring Framework-Version: 4.0.3.RELEASE
- Hibernate Core und Hibernate EntityManager-Version: 3.6.9.Final und 4.3.5.Final
- Spring ORM-Version: 4.0.3.RELEASE
Passende Infrastruktur bei centron
Node-Anwendungen wollen deployt werden: ccloud³ VMs mit vollem Root-Zugriff ab 3,11 € im Monat – oder gleich als Managed Server, wenn der Betrieb nicht bei Ihnen liegen soll. Cloud Server mieten →
Datenbankeinrichtung
Ich verwende eine MySQL-Datenbank für mein Projekt, daher wird das untenstehende setup.sql-Skript die notwendige Tabelle für dieses Beispiel erstellen.
CREATE TABLE `Person` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL DEFAULT '',
`country` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
commit;
Maven-Abhängigkeiten für die Spring-Hibernate-Integration
Sehen wir uns zuerst die Datei pom.xml mit allen benötigten Abhängigkeiten und ihren Versionen an.
Die wichtigen Abhängigkeiten für ein Projekt mit Spring-Hibernate-Integration sind:
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples</groupId>
<artifactId>SpringHibernateExample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<!-- Generic properties -->
<java.version>1.6</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Spring -->
<spring-framework.version>4.0.3.RELEASE</spring-framework.version>
<!-- Hibernate / JPA -->
<!-- <hibernate.version>4.3.5.Final</hibernate.version> -->
<hibernate.version>3.6.9.Final</hibernate.version>
<!-- Logging -->
<logback.version>1.0.13</logback.version>
<slf4j.version>1.7.5</slf4j.version>
</properties>
<dependencies>
<!-- Spring and Transactions -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- Spring ORM support -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- Logging with SLF4J & LogBack -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<scope>runtime</scope>
</dependency>
<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
</project>- spring-context und spring-tx für die Kernfunktionen von Spring. Beachten Sie, dass ich Version 4.0.3.RELEASE verwende.
- spring-orm für die ORM-Unterstützung von Spring; diese Abhängigkeit ist für die Hibernate-Integration in unserem Spring-Projekt erforderlich.
- hibernate-entitymanager und hibernate-core für das Hibernate-Framework. Die Version ist hier 3.6.9.Final; für Hibernate 4 genügt es, sie auf 4.3.5.Final zu ändern, wie in der pom.xml oben kommentiert.
- mysql-connector-java als MySQL-Treiber für die Datenbankverbindung.
Modellklasse bzw. Entity Bean mit JPA-Annotationen
Wir können sowohl das XML-basierte Mapping von Hibernate als auch das Mapping über JPA-Annotationen verwenden. Hier nutze ich JPA-Annotationen, weil Hibernate eine JPA-Implementierung mitbringt.
package com.journaldev.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* Entity bean with JPA annotations
* Hibernate provides JPA implementation
* @author pankaj
*
*/
@Entity
@Table(name="Person")
public class Person {
@Id
@Column(name="id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String name;
private String country;
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 String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
@Override
public String toString(){
return "id="+id+", name="+name+", country="+country;
}
}DAO-Interface
In unseren DAO-Klassen implementieren wir zwei Methoden: die erste speichert das Person-Objekt in der Tabelle, die zweite liest alle Datensätze aus der Tabelle und gibt sie als Liste von Person-Objekten zurück.
package com.journaldev.dao;
import java.util.List;
import com.journaldev.model.Person;
public interface PersonDAO {
public void save(Person p);
public List<Person> list();
}Die Implementierung der DAO-Klasse sieht dann so aus:
package com.journaldev.dao;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.journaldev.model.Person;
public class PersonDAOImpl implements PersonDAO {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
public void save(Person p) {
Session session = this.sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.persist(p);
tx.commit();
session.close();
}
@SuppressWarnings("unchecked")
@Override
public List<Person> list() {
Session session = this.sessionFactory.openSession();
List<Person> personList = session.createQuery("from Person").list();
session.close();
return personList;
}
}Beachten Sie, dass dies die einzige Stelle ist, an der wir Hibernate-Klassen verwenden. Dieses Muster hält unsere Implementierung flexibel und erleichtert den Wechsel von einer Technologie zu einer anderen. Wollten wir zum Beispiel das ORM-Framework iBatis einsetzen, bräuchten wir lediglich eine DAO-Implementierung für iBatis und müssten die Spring-Bean-Konfigurationsdatei anpassen. Im obigen Beispiel verwende ich das Transaktionsmanagement der Hibernate-Session. Genauso lässt sich aber das deklarative Transaktionsmanagement von Spring über die Annotation @Transactional nutzen
Spring-Bean-Konfigurationsdatei für die Hibernate-3-Integration
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:aop="https://www.springframework.org/schema/aop"
xmlns:tx="https://www.springframework.org/schema/tx"
xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
https://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-4.0.xsd
https://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<bean
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/TestDB" />
<property name="username" value="pankaj" />
<property name="password" value="pankaj123" />
</bean>
<!-- Hibernate 3 XML SessionFactory Bean definition-->
<!-- <bean
>
<property name="dataSource" ref="dataSource" />
<property name="mappingResources">
<list>
<value>person.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQLDialect
</value>
</property>
</bean> -->
<!-- Hibernate 3 Annotation SessionFactory Bean definition-->
<bean
>
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>com.journaldev.model.Person</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.current_session_context_class">thread</prop>
<prop key="hibernate.show_sql">false</prop>
</props>
</property>
</bean>
<bean>
<property name="sessionFactory" ref="hibernate3AnnotatedSessionFactory" />
</bean>
</beans>Es gibt zwei Wege, Hibernate die Verbindungsdaten der Datenbank mitzugeben: erstens alles über hibernateProperties zu übergeben, zweitens eine DataSource anzulegen und diese an Hibernate zu übergeben. Ich bevorzuge den zweiten Ansatz – deshalb ist die Abhängigkeit Apache Commons DBCP eingebunden, um über die Verbindungseigenschaften eine BasicDataSource zu erzeugen. Für die Integration von Spring und Hibernate 3 stellt Spring ORM zwei Klassen bereit: org.springframework.orm.hibernate3.LocalSessionFactoryBean für XML-basierte Hibernate-Mappings und org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean für annotationsbasiertes Mapping. Eine einfache Bean-Konfiguration für LocalSessionFactoryBean habe ich als Kommentar hinterlegt, falls Sie XML-basierte Mappings verwenden. AnnotationSessionFactoryBean erbt von LocalSessionFactoryBean und verfügt damit über alle grundlegenden Eigenschaften für die Hibernate-Integration. Die Eigenschaften sind größtenteils selbsterklärend und Hibernate-spezifisch, deshalb gehe ich hier nicht weiter auf sie ein. Wenn Sie sich fragen, woher hibernateProperties und annotatedClasses kommen: ein Blick in den Quellcode der Bean-Klasse hilft weiter. Beachten Sie die Bean-Definition von personDAO – wie erwähnt müssten wir für einen Wechsel zu einem anderen ORM-Framework hier die Implementierungsklasse austauschen und die jeweils nötigen Eigenschaften setzen.
Testprogramm für Spring 4 und Hibernate 3
package com.journaldev.main;
import java.util.List;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.journaldev.dao.PersonDAO;
import com.journaldev.model.Person;
public class SpringHibernateMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
PersonDAO personDAO = context.getBean(PersonDAO.class);
Person person = new Person();
person.setName("Pankaj"); person.setCountry("India");
personDAO.save(person);
System.out.println("Person::"+person);
List<Person> list = personDAO.list();
for(Person p : list){
System.out.println("Person List::"+p);
}
//close resources
context.close();
}
}Wenn wir das obige Programm ausführen, erhalten wir viel Hibernate-bezogene Ausgabe, weil ich das Logging nicht sauber konfiguriert habe – das führt hier aber zu weit. Unser Programm erzeugt jedenfalls die folgende Ausgabe.
Person::id=3, name=Pankaj, country=India
Person List::id=1, name=Pankaj, country=India
Person List::id=2, name=Pankaj, country=India
Person List::id=3, name=Pankaj, country=IndiaÄnderungen für die Integration von Spring 4 und Hibernate 4
Stellen wir unsere Anwendung nun von Hibernate 3 auf Hibernate 4 um. Für diese Migration sind lediglich die folgenden Konfigurationsänderungen nötig.
pom.xml aktualisieren
Ändern Sie die Hibernate-Version in der Datei pom.xml auf 4.3.5.Final, wie oben in den Kommentaren gezeigt.
Spring-Bean-Konfigurationsdatei für die Hibernate-4-Integration
Passen Sie die Spring-Bean-Konfigurationsdatei an. Inzwischen dürfte deutlich geworden sein, dass diese Datei der Schlüssel zur Integration von Spring und Hibernate ist. Unten steht die Konfiguration, die mit Spring 4 und Hibernate 4 funktioniert.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:aop="https://www.springframework.org/schema/aop"
xmlns:tx="https://www.springframework.org/schema/tx"
xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
https://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-4.0.xsd
https://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<bean
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/TestDB" />
<property name="username" value="pankaj" />
<property name="password" value="pankaj123" />
</bean>
<!-- Hibernate 4 SessionFactory Bean definition -->
<bean
>
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>com.journaldev.model.Person</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.current_session_context_class">thread</prop>
<prop key="hibernate.show_sql">false</prop>
</props>
</property>
</bean>
<bean>
<property name="sessionFactory" ref="hibernate4AnnotatedSessionFactory" />
</bean>
</beans>Für Hibernate 4 verwenden wir org.springframework.orm.hibernate4.LocalSessionFactoryBean für die SessionFactory-Bean. Spring ORM hat die beiden Klassen aus Hibernate 3 zusammengeführt, es gibt jetzt nur noch eine – das vermeidet Verwechslungen. Alle übrigen Konfigurationen bleiben unverändert.
Damit ist unser Projekt erfolgreich auf Hibernate 4 migriert. Ändern Sie in der Klasse SpringHibernateMain lediglich die Bean-Konfiguration auf spring4.xml, dann läuft alles wie zuvor – mit derselben Ausgabe.
Testen Sie Ihr Setup auf ccloud³
Registrieren Sie sich in der ccloud³ und erhalten Sie 200 € Startguthaben für Ihr Projekt – z. B. für eine PostgreSQL-VM mit automatischen Backups.