Spring ORM 示例 - JPA、Hibernate、Transaction
欢迎来到 Spring ORM 示例教程。今天我们将研究使用 Hibernate JPA 事务管理的 Spring ORM 示例。我将向您展示一个非常简单的 Spring 独立应用程序示例,该示例具有以下功能。
- 依赖注入(@Autowired注释)
- JPA EntityManager(由 Hibernate 提供)
- 注释事务方法(@Transactional注释)
Spring ORM 示例
我在 Spring ORM 示例中使用了内存数据库,因此无需设置任何数据库(但您可以在 spring.xml 数据源部分将其更改为任何其他数据库)。这是一个 Spring ORM 独立应用程序,旨在最小化所有依赖关系(但如果您熟悉 spring,则可以通过配置轻松将其更改为 Web 项目)。注意:对于基于 Spring AOP 的事务(不带@Transactional注释)方法解析方法,请查看本教程:Spring ORM AOP 事务管理。下图显示了我们最终的 Spring ORM 示例项目。让我们逐一介绍每个 Spring ORM 示例项目组件。
Spring ORM Maven 依赖项
下面是我们最终的 pom.xml 文件,其中包含 Spring ORM 依赖项。我们在 Spring ORM 示例中使用了 Spring 4 和 Hibernate 4。
<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 驱动程序作为数据库访问的依赖项。我们使用包含 JDBC 驱动程序和内存数据库的 HSQLDB。
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 + "]";
}
}
我们使用@Entity
JPA@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 根据 spring.xml 配置注入适当的 EntityManager 实例。
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。
- We want to use transaction management, so methods are annotated with
@Transactional
Spring annotation. The listAll method only reads the database so we set the @Transactional annotation to read-only for optimisation.
Spring ORM Example Bean Configuration XML
Our spring ORM example project java classes are ready, let’s look at our spring bean configuration file now. 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>
- First we tell spring that we want to use classpath scanning for Spring components (Services, DAOs) rather than defining them one by one in spring xml. We have also enabled Spring annotation detection.
- Adding the datasource, that is currently HSQLDB in-memory database.
- We set up a JPA
EntityManagerFactory
that will used by the application to obtain an EntityManager. Spring supports 3 different ways to do this, we have usedLocalContainerEntityManagerFactoryBean
for full JPA capabilities. We setLocalContainerEntityManagerFactoryBean
attributes as:- packagesToScan attribute that points to our model classes package.
- datasource defined earlier in spring configuration file.
- jpaVendorAdapter as Hibernate and setting some hibernate properties.
- We create Spring PlatformTransactionManager instance as a JpaTransactionManager. This transaction manager is appropriate for applications that use a single JPA EntityManagerFactory for transactional data access.
- We enable the configuration of transactional behavior based on annotations, and we set the transactionManager we created.
Spring ORM Hibernate JPA Example Test Program
Our spring ORM JPA Hibernate example project is ready, so let’s write a test program for our application.
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();
}
}
You can see how easily we can start the Spring container from a main method. We are getting our first dependency injected entry point, the service class instance. ProductDao
class reference injected to the ProductService
class after the spring context is initialized. After we got ProducService
instance, we can test it’s methods, all method call will be transactional due to Spring’s proxy mechanism. We also test rollback in this example. If you run above spring ORM example test program, you will get below 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]]
Note that the second transaction is rolled back, this why product list didn’t changed. If you use log4j.properties
file from attached source, you can see what’s going on under the hood. References: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/orm.html You can download the final Spring ORM JPA Hibernate Example project from below link and play around with it to learn more.
Download Spring ORM with Transaction Project