Spring 框架入门教程
1. 1. Spring 框架 2. 2. Spring 5 3. 3. Spring WebFlux 4. 4. 先介绍一下 Spring 5. 5. 什么是 Spring 依赖注入 6. 6. 什么是 Spring IoC 容器 和 Bean 7. 7. Spring Bean 的生命周期 8. 8. Spring REST 开发 9. 9. Spring REST XML 10. 10. Spring RestTemplate 开发 11. 11. Spring AOP 切面编程 12. 12. Spring AOP 方法调优 13. 13. Spring 注解详解 14. 14. Spring 核心注解之 @Autowired 15. 15. Spring 核心注解之 @RequestMapping 16. 16. Spring MVC 开发样例 17. 17. Spring MVC 开发指南 18. 18. Spring MVC 异常处理机制 19. 19. Spring MVC Validator 20. 20. Spring MVC 拦截器 21. 21. Spring MVC 文件上传 22. 22. Spring MVC 国际化(i18n) 23. 23. Spring MVC Hibernate MqSQL 24. 24. Spring ORM 25. 25. Spring ORM JPA 26. 26. Spring Data JPA 27. 27. Spring 事务管理 28. 28. 常用的 Spring JdbcTemplate 29. 29. Spring Security 简介 30. 30. Spring Security 教程 31. 31. Spring Security UserDetailsService 32. 32. Spring MVC 登录注销简单案例 33. 33. Spring Security Roles 34. 34. Spring Boot Tutorial 35. 35. Spring Boot Components 36. 36. Spring Boot CLI Hello World 37. 37. Spring Boot Initilizr Web 38. 38. Spring Boot Initilizr IDE 39. 39. Spring Boot Initilizr CLI 40. 40. Spring Boot Initilizr Tools 41. 41. Spring Boot MongoDB 42. 42. Spring Boot Redis Cache 43. 43. Spring Boot 常见面试问题 44. 44. Spring Batch 45. 45. Spring Batch 批处理示例 46. 46. Spring AMQP 47. 47. Spring RabbitMQ 48. 48. Spring AMQP RabbitMQ 49. 49. Apache ActiveMQ 安装与启动 50. 50. Spring ActiveMQ 教程 51. 51. Spring ActiveMQ 示例 52. 52. Spring JDBC 53. 53. Spring DataSource JNDI 54. 54. Spring Hibernate 55. 55. Spring Primefaces JPA 56. 56. Spring Primefaces MongoDB 57. 57. Spring Primefaces Hibernate 58. 58. SpringRoo Primefaces Hibernate 59. 59. Spring JSF 60. 60. Spring JDF Hibernate 61. 61. Spring Data MongoDB 62. 62. Spring 常见面试问题

25. Spring ORM JPA

这是一个非常简单的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,请查看本教程:

精选推荐