Spring ORM Beispiel – Hibernate JPA Transaction

Willkommen zum Spring ORM Beispiel Tutorial. Heute werden wir uns ein Spring ORM Beispiel mit Hibernate JPA Transaktionsmanagement anschauen. Ich werde Ihnen ein sehr einfaches Beispiel einer Spring standalone Anwendung mit folgenden features zeigen.

  • Dependency Injection (@Autowired annotation)
  • JPA EntityManager (bereitgestellt von Hibernate)
  • Annotierte transactional methods (@Transactional annotation)

Spring ORM Beispiel

Ich habe eine in-memory database für das Spring ORM Beispiel verwendet, daher ist keine Datenbankkonfiguration nötig (aber Sie können diese auf jede andere Datenbank im spring.xml datasource Abschnitt ändern). Dies ist eine Spring ORM standalone Anwendung, um alle Abhängigkeiten zu minimieren (aber Sie können diese leicht zu einem Webprojekt ändern, indem Sie die Konfiguration anpassen, wenn Sie sich mit Spring vertraut machen).

Spring ORM with TX Project Structure

Lassen Sie uns die einzelnen Komponenten des Spring ORM Beispielprojekts nacheinander durchgehen.

Spring ORM Maven Abhängigkeiten

Unten ist unsere endgültige pom.xml-Datei mit Spring ORM Abhängigkeiten. Wir haben Spring 4 und Hibernate 4 in unserem Spring ORM Beispiel verwendet.

<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>hu.daniel.hari.learn.spring</groupId>
	<artifactId>Tutorial-SpringORMwithTX</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<properties>
		<!-- Generische Eigenschaften -->
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.7</java.version>

		<!-- SPRING & HIBERNATE / JPA -->
		<spring.version>4.0.0.RELEASE</spring.version>
		<hibernate.version>4.1.9.Final</hibernate.version>

	</properties>

	<dependencies>
		<!-- LOG -->
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>

		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<!-- JPA Anbieter -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>${hibernate.version}</version>
		</dependency>

		<!-- IN MEMORY Datenbank und JDBC Treiber -->
		<dependency>
			<groupId>hsqldb</groupId>
			<artifactId>hsqldb</artifactId>
			<version>1.8.0.7</version>
		</dependency>

	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>${java.version}</source>
					<target>${java.version}</target>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

  • Wir benötigen spring-context und spring-orm als Spring Abhängigkeiten.
  • Wir verwenden hibernate-entitymanager für Hibernate als JPA Implementierung. hibernate-entitymanager ist abhängig von hibernate-core, deshalb müssen wir hibernate-core nicht explizit in pom.xml aufnehmen. Es wird durch maven transitive dependencies in unser Projekt eingebunden.
  • Wir benötigen auch einen JDBC Treiber als Abhängigkeit für den Datenbankzugriff. Wir verwenden HSQLDB, das den JDBC Treiber und eine funktionierende in-memory Datenbank enthält.

Spring ORM Model Klasse

Wir können standard JPA annotations für das Mapping in unseren Model Beans verwenden, da Hibernate die JPA Implementierung bereitstellt.

package hu.daniel.hari.learn.spring.orm.model;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Product {

    @Id
    private Integer id;
    private String name;

    public Product() {
    }

    public Product(Integer id, String name) {
        this.id = id;
        this.name = name;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Product [id=" + id + ", name=" + name + "]";
    }

}

Wir verwenden die JPA annotations @Entity und @Id, um unser POJO als Entity zu qualifizieren und seinen Primärschlüssel zu definieren.

Spring ORM DAO Klasse

Wir erstellen eine sehr einfache DAO Klasse, die persist und findAll Methoden bereitstellt.

package hu.daniel.hari.learn.spring.orm.dao;

import hu.daniel.hari.learn.spring.orm.model.Product;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Component;

@Component
public class ProductDao {

    @PersistenceContext
    private EntityManager em;

    public void persist(Product product) {
        em.persist(product);
    }

    public List findAll() {
        return em.createQuery("SELECT p FROM Product p").getResultList();
    }

}

@Component ist eine Spring annotation, die dem Spring Container mitteilt, dass wir diese Klasse durch Spring IoC (Dependency Injection) verwenden können.

Wir verwenden die JPA annotation @PersistenceContext, um die Dependency Injection an einen EntityManager zu indizieren. Spring injiziert eine geeignete Instanz des EntityManager gemäß der spring.xml Konfiguration.

Spring ORM Service Klasse

Unsere einfache Service-Klasse hat 2 Schreib- und 1 Lese-Methoden – add, addAll und listAll.

package hu.daniel.hari.learn.spring.orm.service;
import hu.daniel.hari.learn.spring.orm.dao.ProductDao;
import hu.daniel.hari.learn.spring.orm.model.Product;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
public class ProductService {
@Autowired
private ProductDao productDao;

@Transactional
public void add(Product product) {
    productDao.persist(product);
}

@Transactional
public void addAll(Collection products) {
    for (Product product : products) {
        productDao.persist(product);
    }
}

@Transactional(readOnly = true)
public List listAll() {
    return productDao.findAll();

   }
}

Wir verwenden die Spring @Autowired Annotation, um ProductDao in unserer Service-Klasse zu injizieren.

Wir möchten Transaktionsmanagement verwenden, daher sind die Methoden mit der @Transactional Spring Annotation annotiert. Die listAll-Methode liest nur die Datenbank, daher setzen wir die @Transactional Annotation für Optimierung auf read-only.

Spring ORM Beispiel-Bean-Konfigurations-XML

Unsere Java-Klassen des Spring ORM Beispielprojekts sind fertig, jetzt schauen wir uns unsere Spring-Bean-Konfigurationsdatei an. spring.xml

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

    <!-- Scans the classpath for annotated components that will be auto-registered as Spring beans -->
    <context:component-scan base-package="hu.daniel.hari.learn.spring" />
    <!-- Activates various annotations to be detected in bean classes e.g: @Autowired -->
    <context:annotation-config />

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
        <property name="url" value="jdbc:hsqldb:mem://productDb" />
        <property name="username" value="sa" />
        <property name="password" value="" />
    </bean>
    
    <bean id="entityManagerFactory" 
            class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
            p:packagesToScan="hu.daniel.hari.learn.spring.orm.model"
            p:dataSource-ref="dataSource"
            >
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="generateDdl" value="true" />
                <property name="showSql" value="true" />
            </bean>
        </property>
    </bean>

    <!-- Transactions -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    <!-- enable the configuration of transactional behavior based on annotations -->
    <tx:annotation-driven transaction-manager="transactionManager" />

</beans>

Zuerst sagen wir Spring, dass wir das Scannen des Classpath für Spring-Komponenten (Services, DAOs) verwenden möchten, anstatt sie einzeln in spring xml zu definieren. Wir haben auch die Erkennung von Spring-Annotationen aktiviert.

Hinzufügen der Datenquelle, die derzeit eine HSQLDB In-Memory-Datenbank ist.

Wir richten eine JPA EntityManagerFactory ein, die von der Anwendung verwendet wird, um einen EntityManager zu erhalten. Spring unterstützt 3 verschiedene Arten, dies zu tun, wir haben LocalContainerEntityManagerFactoryBean für volle JPA-Fähigkeiten verwendet. Wir setzen die Attribute von LocalContainerEntityManagerFactoryBean wie folgt:

  • packagesToScan-Attribut, das auf unser Paket mit Modellklassen zeigt.
  • Früher in der Spring-Konfigurationsdatei definierte Datenquelle.
  • jpaVendorAdapter als Hibernate und Einrichten einiger Hibernate-Eigenschaften.

Wir erstellen eine Spring PlatformTransactionManager-Instanz als JpaTransactionManager. Dieser Transaktionsmanager ist geeignet für Anwendungen, die eine einzige JPA EntityManagerFactory für den transaktionalen Datenzugriff verwenden.

Wir ermöglichen die Konfiguration des transaktionalen Verhaltens basierend auf Annotationen und setzen den von uns erstellten transactionManager.

Spring ORM Hibernate JPA Beispiel Testprogramm

Unser Spring ORM JPA Hibernate-Beispielprojekt ist fertig, also schreiben wir ein Testprogramm für unsere Anwendung.

public class SpringOrmMain {
    
    public static void main(String[] args) {
        
        //Create Spring application context
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/spring.xml");
        
        //Get service from context. (service's dependency (ProductDAO) is autowired in ProductService)
        ProductService productService = ctx.getBean(ProductService.class);
        
        //Do some data operation
        
        productService.add(new Product(1, "Bulb"));
        productService.add(new Product(2, "Dijone mustard"));
        
        System.out.println("listAll: " + productService.listAll());
        
        //Test transaction rollback (duplicated key)
        
        try {
            productService.addAll(Arrays.asList(new Product(3, "Book"), new Product(4, "Soap"), new Product(1, "Computer")));
        } catch (DataAccessException dataAccessException) {
        }
        
        //Test element list after rollback
        System.out.println("listAll: " + productService.listAll());
        
        ctx.close();
        
    }
}

Man kann sehen, wie einfach wir den Spring Container von einer Hauptmethode aus starten können. Wir bekommen unsere erste abhängigkeitsinjizierte Einstiegspunkte, die Service-Klasseninstanz. Die ProduktDao-Klassenreferenz wird in die ProductService-Klasse injiziert, nachdem der Spring-Kontext initialisiert wurde. Nachdem wir die ProductService-Instanz erhalten haben, können wir ihre Methoden testen, alle Methodenaufrufe sind aufgrund von Springs Proxy-Mechanismus transaktional. In diesem Beispiel testen wir auch das Rollback.

Wenn Sie das oben genannte Spring ORM-Beispieltestprogramm ausführen, erhalten Sie die folgenden Logs.

Hibernate: insert into Product (name, id) values (?, ?)
Hibernate: insert into Product (name, id) values (?, ?)
Hibernate: select product0_.id as id0_, product0_.name as name0_ from Product product0_
listAll: [Product [id=1, name=Bulb], Product [id=2, name=Dijone mustard]]
Hibernate: insert into Product (name, id) values (?, ?)
Hibernate: insert into Product (name, id) values (?, ?)
Hibernate: insert into Product (name, id) values (?, ?)
Hibernate: select product0_.id as id0_, product0_.name as name0_ from Product product0_
listAll: [Product [id=1, name=Bulb], Product [id=2, name=Dijone mustard]]

Beachten Sie, dass die zweite Transaktion zurückgerollt wird, deshalb hat sich die Produktliste nicht geändert.

Kostenlosen Account erstellen

Registrieren Sie sich jetzt und erhalten Sie Zugang zu unseren Cloud Produkten.

Das könnte Sie auch interessieren: