Voorbeeld van Spring ORM – JPA, Hibernate, Transactie

Welkom bij de Spring ORM voorbeeldtutorial. Vandaag zullen we kijken naar een voorbeeld van Spring ORM met behulp van Hibernate JPA-transactiebeheer. Ik zal je een zeer eenvoudig voorbeeld laten zien van een op zichzelf staande Spring-toepassing met de volgende functies.

  • Dependency Injection (@Autowired-annotatie)
  • JPA EntityManager (geleverd door Hibernate)
  • Annotated transactionele methoden (@Transactional-annotatie)

Spring ORM Voorbeeld

Ik heb een in-memory database gebruikt voor het Spring ORM-voorbeeld, dus er is geen behoefte aan enige database-instelling (maar je kunt het wijzigen naar elke andere database in de spring.xml datasource sectie). Dit is een op zichzelf staande Spring ORM-toepassing om alle afhankelijkheden te minimaliseren (maar je kunt het eenvoudig wijzigen naar een webproject door configuratie als je bekend bent met Spring). LET OP: Voor Spring AOP gebaseerde transactie (zonder @Transactional annotatie) methode resolutie benadering, bekijk deze tutorial: Spring ORM AOP Transactiebeheer. Hieronder zie je ons uiteindelijke Spring ORM-voorbeeldproject. Laten we elk van de componenten van het Spring ORM-voorbeeldproject één voor één doornemen.

Spring ORM Maven-afhankelijkheden

Hieronder staat ons uiteindelijke pom.xml-bestand met Spring ORM-afhankelijkheden. We hebben Spring 4 en Hibernate 4 gebruikt in ons Spring ORM-voorbeeld.

<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>
		<!-- Generic properties -->
		<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 Vendor -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>${hibernate.version}</version>
		</dependency>

		<!-- IN MEMORY Database and JDBC Driver -->
		<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>
  • We hebben spring-context en spring-orm nodig als Spring-afhankelijkheden.
  • We gebruiken hibernate-entitymanager voor Hibernate als JPA-implementatie. hibernate-entitymanager is afhankelijk van hibernate-core, daarom hoeven we hibernate-core niet expliciet in pom.xml op te nemen. Het wordt in ons project geladen via maven transitive dependencies.
  • We hebben ook een JDBC-driver nodig als afhankelijkheid voor database-toegang. We gebruiken HSQLDB dat de JDBC-driver bevat en een werkende in-memory database.

Spring ORM Modelklasse

We kunnen standaard JPA-annotaties gebruiken voor het mappen in onze modelbeans omdat Hibernate JPA-implementatie biedt.

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 + "]";
	}

}

We gebruiken de JPA-annotaties @Entity en @Id om onze POJO te kwalificeren als een entiteit en om de primaire sleutel te definiëren.

Spring ORM DAO-klasse

We maken een zeer eenvoudige DAO-klasse die persist en findALL-methoden biedt.

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<Product> findAll() {
		return em.createQuery("SELECT p FROM Product p").getResultList();
	}

}
  • @Component is een Spring-annotatie die de Spring-container vertelt dat we deze klasse kunnen gebruiken via Spring IoC (Dependency Injection).
  • We gebruiken de JPA @PersistenceContext annotatie die de afhankelijkheidsinjectie naar een EntityManager aangeeft. Spring injecteert een juiste instantie van EntityManager op basis van de spring.xml-configuratie.

Spring ORM-serviceklasse

Onze eenvoudige serviceklasse heeft 2 schrijfmethoden en 1 leesmethode – toevoegen, alles toevoegen en alles vermelden.

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<Product> products) {
		for (Product product : products) {
			productDao.persist(product);
		}
	}

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

	}

}
  • We gebruiken de Spring @Autowired annotatie om ProductDao in onze serviceklasse te injecteren.
  • We willen transactiebeheer gebruiken, dus methoden zijn geannoteerd met @Transactional Spring-annotatie. De listAll-methode leest alleen de database, dus we stellen de @Transactional annotatie in op alleen-lezen voor optimalisatie.

Voorbeeld van Spring ORM Bean-configuratie XML

Onze Java-klassen voor het voorbeeldproject van Spring ORM zijn klaar, laten we nu naar ons spring bean-configuratiebestand kijken. 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>
  1. Eerst vertellen we Spring dat we classpath-scanning willen gebruiken voor Spring-componenten (services, DAO’s) in plaats van ze één voor één te definiëren in de spring-xml. We hebben ook de detectie van Spring-annotaties ingeschakeld.
  2. Het toevoegen van de gegevensbron, die momenteel de HSQLDB in-memory database is.
  3. We stellen een JPA EntityManagerFactory in die door de applicatie zal worden gebruikt om een EntityManager te verkrijgen. Spring ondersteunt 3 verschillende manieren om dit te doen, we hebben LocalContainerEntityManagerFactoryBean gebruikt voor volledige JPA-mogelijkheden. We stellen de attributen van LocalContainerEntityManagerFactoryBean als volgt in:
    1. packagesToScan-attribuut dat wijst naar onze modelklassenpakket.
    2. gegevensbron gedefinieerd eerder in het Spring-configuratiebestand.
    3. jpaVendorAdapter als Hibernate en instelling van enkele hibernate-eigenschappen.
  4. We maken een Spring PlatformTransactionManager-instantie aan als een JpaTransactionManager. Deze transactiemanager is geschikt voor toepassingen die een enkele JPA EntityManagerFactory gebruiken voor transactionele gegevenstoegang.
  5. We maken de configuratie van transactioneel gedrag op basis van annotaties mogelijk, en we stellen de transactionManager in die we hebben gemaakt.

Spring ORM Hibernate JPA Voorbeeld Testprogramma

Ons Spring ORM JPA Hibernate voorbeeldproject is klaar, laten we dus een testprogramma schrijven voor onze applicatie.

public class SpringOrmMain {
	
	public static void main(String[] args) {
		
		//Maak de Spring-toepassingscontext
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/spring.xml");
		
		//Haal service op uit de context. (de afhankelijkheid van de service (ProductDAO) is bedraad in ProductService)
		ProductService productService = ctx.getBean(ProductService.class);
		
		//Voer enkele gegevensbewerkingen uit
		
		productService.add(new Product(1, "Bulb"));
		productService.add(new Product(2, "Dijone mustard"));
		
		System.out.println("listAll: " + productService.listAll());
		
		//Test transactie-rollback (gedupliceerde sleutel)
		
		try {
			productService.addAll(Arrays.asList(new Product(3, "Book"), new Product(4, "Soap"), new Product(1, "Computer")));
		} catch (DataAccessException dataAccessException) {
		}
		
		//Test elementenlijst na rollback
		System.out.println("listAll: " + productService.listAll());
		
		ctx.close();
		
	}
}

Je kunt zien hoe eenvoudig we de Spring-container kunnen starten vanuit een hoofdmethode. We krijgen ons eerste afhankelijkheid-geïnjecteerde instappunt, de serviceklasse-instantie. De verwijzing naar de ProductDao-klasse wordt geïnjecteerd in de ProductService-klasse nadat de Spring-context is geïnitialiseerd. Nadat we de ProducService-instantie hebben gekregen, kunnen we de methoden ervan testen. Alle methodenoproepen zijn transacties vanwege het Spring-proxy-mechanisme. We testen ook rollback in dit voorbeeld. Als je het bovenstaande Spring ORM-voorbeeld-testprogramma uitvoert, krijg je de onderstaande 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]]

Merk op dat de tweede transactie wordt teruggedraaid, daarom is de productlijst niet gewijzigd. Als je het log4j.properties-bestand uit de bijgevoegde bron gebruikt, kun je zien wat er onder de motorkap gebeurt. Referenties: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/orm.html Je kunt het definitieve Spring ORM JPA Hibernate voorbeeldproject downloaden via de onderstaande link en ermee spelen om meer te leren.

Download Spring ORM met Transactieproject

Source:
https://www.digitalocean.com/community/tutorials/spring-orm-example-jpa-hibernate-transaction