Поиск по сайту:

Пример Spring ORM — JPA, Hibernate, Transaction


Добро пожаловать в учебник по примерам Spring ORM. Сегодня мы рассмотрим пример Spring ORM с использованием управления транзакциями Hibernate JPA. Я покажу вам очень простой пример автономного приложения Spring со следующими функциями.

  • Внедрение зависимостей (@аннотация Autowired)
  • JPA EntityManager (предоставляется Hibernate)
  • Аннотированные транзакционные методы (@Transactional annotation)

Пример Spring ORM

Зависимости Spring ORM Maven

Ниже представлен наш окончательный файл pom.xml с зависимостями Spring ORM. Мы использовали Spring 4 и Hibernate 4 в нашем примере Spring ORM.

<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>

  • Нам нужны spring-context и spring-orm в качестве зависимостей Spring.
  • Мы используем hibernate-entitymanager для Hibernate в качестве реализации JPA. hibernate-entitymanager зависит от hibernate-core, поэтому нам не нужно явно помещать hibernate-core в pom.xml. Он подключается к нашему проекту через транзитивные зависимости maven.
  • Нам также нужен драйвер JDBC в качестве зависимости для доступа к базе данных. Мы используем HSQLDB, которая содержит драйвер JDBC и базу данных, работающую в памяти.

Класс модели Spring ORM

Мы можем использовать стандартные аннотации JPA для отображения в bean-компонентах нашей модели, потому что Hibernate обеспечивает реализацию JPA.

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

}

Мы используем аннотации JPA @Entity и @Id, чтобы квалифицировать наш POJO как объект и определить его первичный ключ.

Класс Spring ORM DAO

Мы создаем очень простой класс DAO, который предоставляет методы persist и findALL.

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 — это аннотация Spring, сообщающая контейнеру Spring, что мы можем использовать этот класс через Spring IoC (внедрение зависимостей).
  • Мы используем аннотацию JPA @PersistenceContext, указывающую на внедрение зависимостей в EntityManager. Spring вводит правильный экземпляр EntityManager в соответствии с конфигурацией spring.xml.

Класс обслуживания Spring ORM

Наш простой сервисный класс имеет 2 метода записи и 1 метод чтения — add, addAll и 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<Product> products) {
		for (Product product : products) {
			productDao.persist(product);
		}
	}

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

	}

}

  • Мы используем аннотацию Spring @Autowired для внедрения ProductDao в наш класс обслуживания.
  • Мы хотим использовать управление транзакциями, поэтому методы помечаются аннотацией @Transactional Spring. Метод listAll только читает базу данных, поэтому для оптимизации мы установили аннотацию @Transactional только для чтения.

Spring ORM Пример конфигурации компонента XML

Java-классы нашего примера проекта Spring ORM готовы, давайте теперь посмотрим на наш файл конфигурации bean-компонента Spring. 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. Сначала мы сообщаем Spring, что хотим использовать сканирование путей к классам для компонентов Spring (службы, DAO), а не определять их один за другим в spring xml. Мы также включили обнаружение аннотаций Spring.
  2. Добавление источника данных, который в настоящее время является базой данных HSQLDB в памяти.
  3. Мы настроили EntityManagerFactory JPA, который будет использоваться приложением для получения EntityManager. Spring поддерживает 3 разных способа сделать это, мы использовали LocalContainerEntityManagerFactoryBean для полных возможностей JPA. Мы устанавливаем атрибуты LocalContainerEntityManagerFactoryBean как:
    1. Атрибут packageToScan, указывающий на пакет классов нашей модели.
    2. источник данных, определенный ранее в файле конфигурации spring.
    3. jpaVendorAdapter как Hibernate и настройка некоторых свойств гибернации.

    Пример тестовой программы Spring ORM Hibernate JPA

    Наш пример проекта Spring ORM JPA Hibernate готов, поэтому давайте напишем тестовую программу для нашего приложения.

    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();
    		
    	}
    }
    

    Вы можете видеть, как легко мы можем запустить контейнер Spring из основного метода. Мы получаем нашу первую точку входа с внедренной зависимостью, экземпляр класса обслуживания. Ссылка на класс ProductDao внедряется в класс ProductService после инициализации контекста Spring. После того, как мы получили экземпляр ProducService, мы можем протестировать его методы, все вызовы методов будут транзакционными из-за прокси-механизма Spring. Мы также тестируем откат в этом примере. Если вы запустите тестовую программу Spring ORM выше, вы получите журналы ниже.

    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]]
    

    Обратите внимание, что вторая транзакция откатывается, поэтому список товаров не изменился. Если вы используете файл log4j.properties из прикрепленного источника, вы можете увидеть, что происходит под капотом. Ссылки: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/orm.html Вы можете загрузить окончательный пример проекта Spring ORM JPA Hibernate по ссылке ниже и поиграть с ним, чтобы узнать более.

    Скачать Spring ORM с проектом транзакций