这是一个非常简单的Spring Orm示例,它向您展示了如何使用
- 依赖注入(@Autowired annotation),
- JPA EntityManager(由Hibernate提供),
- 事务方法(AOP配置)。
带有AOP事务的Spring Orm示例
要配置事务方法,我们请勿使用@因为我们使用面向切面approach now(例如:我们可以说服务包中以“;get”;开头的所有方法都是只读事务性方法),我们在本例中使用内存数据库来简化,因此不需要任何数据库设置。这是一个独立的应用程序示例。
(对于@Transactional annotated approach,请查看本教程: https://www.journaldev.com/7655/spring-orm-example-jpa-hibernate-transaction)
项目结构:
我们逐一查看这些文件并解释:
1Maven依赖项
我们将首先调查pom.xml文件所有必需的依赖项及其版本的文件。在本例中,我们使用了spring4和hibernate4。
<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-AOP</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>
<!-- AspectJ (required spring-aop dependency) -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.5</version>
</dependency>
<!-- JPA Implementation (Hibernate)-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<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背景和Spring风暴作为Spring依赖项。
- Springaop还附带了spring context作为它的依赖库,因此不需要添加它。
- aspectjweaver公司是Spring Aop的依赖项,但是我们必须添加它,因为它没有为Spring Aop明确定义。
- 我们使用Hibernate实体管理器将Hibernate用作JPA供应商。Hibernate实体管理器依赖于Hibernate核心这就是为什么我们不需要Hibernate核心在pom.xml文件明确地。
- 我们还需要一个JDBC驱动程序作为Hibernate使用的数据库访问的依赖项。在这个例子中,我们使用hsqldb公司它包含JDBC驱动程序和一个在内存中工作的数据库。(如果要使用外部数据库,则必须将其更改为数据库的JDBC驱动程序、fe:PostgreSQL或mysql connector java maven依赖项。)
2模型类
我们可以在我们的模型中使用标准的JPA注解进行映射,因为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() {
//Default constructor needed for JPA.
}
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
and @Id
JPA annotations to qualify our POJO as an Entity and defining it’s primary key.
三。刀类
我们创建了一个非常简单的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) {
System.out.println("persist:"+product);
em.persist(product);
}
public List<Product> findAll() {
return em.createQuery("SELECT p FROM Product p")
.getResultList();
}
}
- @组件是Spring注解,它告诉Spring容器我们可以通过Spring Ioc(依赖注入)使用这个类。
- 我们使用JPA的@PersistenceContext注解来指示对EntityManager的依赖注入。Spring根据spring.xml文件配置。
4服务等级
我们的简单服务类有2个write和1个read方法: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;
@Component
public class ProductService {
@Autowired
private ProductDao productDao;
public void add(Product product) {
productDao.persist(product);
}
public void addAll(Collection<Product> products) {
for (Product product : products) {
productDao.persist(product);
}
}
public List<Product> listAll() {
return productDao.findAll();
}
}
请注意
- 没有@事务性注解在方法之前,但我们希望这些方法是事务性的。原因是我们使用AOP方法来配置将在中配置的事务性方法spring.xml文件下面。
- 我们使用Spring’;s@Autowired注解将ProductDao组件注入依赖项。
5Spring配置XML
现在我们已经准备好了迷你应用程序中需要的每个工作类。让我们创建Spring的配置文件:
<?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:aop="https://www.springframework.org/schema/aop"
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
https://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.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 />
<!-- JPA -->
<!-- Datasource, that is currently hsqldb (in-memory database). -->
<bean id="dataSource">
<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>
<!-- EntityManagerFactory -->
<bean id="entityManagerFactory"
p:packagesToScan="hu.daniel.hari.learn.spring.orm.model"
p:dataSource-ref="dataSource"
>
<property name="jpaVendorAdapter">
<bean>
<property name="generateDdl" value="true" />
<property name="showSql" value="true" />
</bean>
</property>
</bean>
<!-- AOP Configuration for selecting transactional methods -->
<!-- the transactional advice (what "happens"; see the <aop:advisor/> ) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- all methods starting with "list" or "get" are read-only -->
<tx:method name="list*" read-only="true"/>
<tx:method name="get*" read-only="true"/>
<!-- for other methods use the default transaction settings -->
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<!-- ensure that the above transactional advice runs for any execution
of a method in the service package -->
<aop:config>
<aop:pointcut id="serviceMethods"
expression="execution(* hu.daniel.hari.learn.spring.orm.service.*.*(..))" />
<aop:advisor pointcut-ref="serviceMethods" advice-ref="txAdvice" />
</aop:config>
<!-- TransactionManager -->
<bean id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
</beans>
- 首先,我们告诉spring,我们希望对spring组件(服务、dao等)使用类路径扫描,而不是在这个xml中一个一个地定义它们,而且我们还启用spring注解检测。
- 添加当前为hsqldb(内存数据库)的数据源。
- 我们设置了一个JPA EntityManagerFactory,应用程序将使用它来获取EntityManager。Spring支持3种不同的方法来实现这一点(请参阅下面的参考资料了解详细信息),我们使用LocalContainerEntityManagerFactoryBean来实现完整的JPA功能。我们将其属性设置为:
- packagesToScan属性,指向我们模型的包。(无需持久性.xml)
- 数据源(定义见上文)
- jpaventoradapter作为Hibernate(同时设置一些Hibernate属性)
- 我们按照以下方式配置Spring AOP行为:我们希望服务包中的所有方法都是事务性的,对于以“;get*”;或“;list*”;开头的方法,我们希望它们是只读事务性的. 这很简单。
- 我们创建Spring的PlatformTransactionManager实例作为JpaTransactionManager。(此事务管理器适用于使用单个JPA EntityManagerFactory进行事务数据访问的应用程序。)
6Spring ORM示例测试类
我们的设置已经就绪,所以让我们为应用程序编写一个测试类。
package hu.daniel.hari.learn.spring.orm.main;
import hu.daniel.hari.learn.spring.orm.model.Product;
import hu.daniel.hari.learn.spring.orm.service.ProductService;
import java.util.Arrays;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.dao.DataAccessException;
/**
* Simple tester for a Spring application that uses JPA
* with AOP based Transactions.
**/
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, "Television"));
productService.add(new Product(2, "Phone"));
System.out.println("listAll: " + productService.listAll());
//Test transaction rollback (for duplicated key)
try {
productService.addAll(Arrays.asList(
new Product(3, "Peach"), new Product(4, "Strawberry"), new Product(1, "Melone")));
} catch (DataAccessException dataAccessException) {
//Do nothing here, we just test rollback
}
//Test element list after rollback (same two element, 3 more hasn"t been added.)
System.out.println("listAll: " + productService.listAll());
ctx.close();
}
}
您可以看到,从main方法启动Spring容器并获得第一个依赖注入入口点,即服务类实例是多么简单。ProductDao
class reference injected to the ProductService
class after the context initialized.
在我们得到ProducService
instance, we can test it’s methods, all method call will be transactioned due to Spring’s proxy mechanism. We also test rollback in this example.
如果您运行,您将得到以下日志:
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]]
请注意,第二个事务回滚,这就是为什么产品列表没有改变的原因。
你可以看到我们事务性方法,而不逐个指定它们使用@Transactional注解,正如我们在AOP方法.
如果你使用log4j.properties
file from attached source, you can see what’s going on under the hood.
参考文献: 对象关系映射(ORM)数据访问 Spring面向切面编程
(对于@Transactional annotated approach,请查看本教程: