Spring ORM示例 - 带有AOP事务管理

 

Spring ORM示例 - 带有AOP事务管理

 

这是一个非常简单的Spring ORM示例,向您展示如何使用Spring配置应用程序

  • 依赖注入(@Autowired annotation),
  • JPA EntityManager(由Hibernate提供),
  • 事务方法(配置AOP)。

目录[ 隐藏 ]

带有AOP事务的Spring ORM示例

为了配置事务方法,我们现在不使用 @Transactional注释,因为我们现在使用面向方法(例如:我们可以说服务包中以“get”开头的所有方法都是只读事务方法)。我们使用内存数据库来简化此示例,因此无需任何数据库设置。这是一个独立的应用程序示例。

(对于@Transactional注释方法,请查看本教程:https//www.journaldev.com/7655/spring-orm-example-jpa-hibernate-transaction

项目结构:

Spring ORM示例AOP事务管理

我们逐个浏览这些文件并解释它们:

 

1. Maven依赖

我们将首先查看我们的pom.xml文件,了解所有必需的依赖项及其版本。我们在这个例子中使用了Spring 4和Hibernate 4。


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://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-contextspring-orm作为Spring依赖项。
  • spring-aop还带有spring-context作为它的依赖库,所以不需要添加它。
  • aspectjweaver是spring-aop的依赖项,但我们必须添加它,因为它没有为spring-aop明确定义。
  • 我们使用hibernate-entitymanager将Hibernate用作JPA供应商。hibernate-entitymanager依赖于hibernate-core这就是为什么我们不必将hibernate-core明确地放在pom.xml中。
  • 我们还需要一个JDBC驱动程序作为Hibernate使用的数据库访问依赖项。在这个例子中,我们使用了包含JDBC驱动程序的hsqldb,以及在本例中也使用了内存数据库。(如果要使用外部数据库,则必须将其更改为该db的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@IdJPA注解资格我们的POJO作为一个实体,它定义的主键。

道教班

我们创建了一个非常简单的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();
	}
}
  • @Component是Spring注释,告诉Spring容器我们可以通过Spring IoC(依赖注入)使用这个类。
  • 我们使用JPA的@PersistenceContext注释来指示对EntityManager的依赖注入。Spring根据spring.xml配置注入适当的EntityManager实例。

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

请注意

  • 方法之前没有 @Transactional注释,但我们希望这些方法是事务性的。原因是我们使用AOP方法来配置哪些方法是事务性的,将在下面的spring.xml中配置。
  • 我们使用Spring的@Autowired批注使ProductDao组件被依赖注入。

5. Spring配置XML

现在我们准备好了迷你应用程序中所需的每个工作类。让我们创建Spring的配置文件:

 


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

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

	<!-- 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" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>
	
</beans>
  • 首先我们告诉spring我们想要对Spring组件(服务,DAO等)使用类路径扫描,而不是在这个xml中逐个不方便地定义它们,并且我们还启用了Spring注释检测。
  • 添加数据源,即当前hsqldb(内存数据库)。
  • 我们设置了一个JPA EntityManagerFactory,它将被应用程序用来获取EntityManager。Spring支持3种不同的方法(详见下面的参考资料),我们使用LocalContainerEntityManagerFactoryBean来获得完整的JPA功能。
    我们将其属性设置为:

     

    • packagesToScan属性指向我们模型的包。(不需要persistence.xml)
    • 数据源(上面定义)
    • jpaVendorAdapter作为Hibernate(也设置了一些hibernate属性)
  • 我们通过以下方式配置Spring-AOP行为:我们希望服务包中的所有方法都是事务性的,对于以“get *”或“list *”开头的方法,我们希望它们是只读事务性的。这很简单。
  • 我们将Spring的PlatformTransactionManager实例创建为JpaTransactionManager。(此事务管理器适用于使用单个JPA EntityManagerFactory进行事务数据访问的应用程序。)

6. Spring 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注入类的类引用ProductService

在我们获得ProducService实例之后,我们可以测试它的方法,由于Spring的代理机制,所有方法调用都将被转换。我们还在此示例中测试回滚。

如果你跑,你得到以下日志:


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来自附加源的文件,您可以看到幕后发生了什么。

参考文献:
对象关系映射(ORM)数据访问
面向方面的Spring编程

(对于@Transactional注释方法,请查看本教程:
https//www.journaldev.com/7655/spring-orm-example-jpa-hibernate-transaction

您可以从下面的链接下载最终的maven项目源,并使用它来了解更多信息。

使用Transaction AOP Project下载Spring ORM

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值