学习Spring Data Jpa
分类:
IT文章
•
2025-02-05 15:31:25
首先,抛开Spring,先来谈谈JPA。
1.JPA是什么?
JPA全称Java Persistence API.JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
说到底还是一个ORM框架,不过是Sun为了希望整合所有的ORM框架而推出的规范,总的来说没有什么大的区别。依旧是是开发者从复杂的SQL与JDBC中脱离出来。
2.实际中使用JPA
首先在数据库中建库与表,MySQL中脚本如下
1 CREATE DATABASE jpa;
2 USE jpa;
3 CREATE TABLE `user` (
4 `id` bigint(20) NOT NULL AUTO_INCREMENT,
5 `firstName` varchar(45) NOT NULL,
6 `phoneNo` varchar(45) NOT NULL,
7 `lastName` varchar(45) NOT NULL,
8 `birthday` date NOT NULL,
9 PRIMARY KEY (`id`),
10 UNIQUE KEY `id_UNIQUE` (`id`),
11 UNIQUE KEY `phoneNo_UNIQUE` (`phoneNo`)
12 ) ENGINE=InnoDB DEFAULT CHARSET=UTF-8;
JPA标准配置文件,使用Hibernate实现 含每行注释(注意 一定要放置在src下的META-INF目录下,十分坑爹!!)
1 <?xml version="1.0" encoding="UTF-8"?>
2
3 <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
6
7 <!--
8 Name属性用于定义持久化单元的名字 (name必选,空值也合法);
9 transaction-type 指定事务类型(可选)
10 -->
11 <persistence-unit name="TestJPAUnit" transaction-type="RESOURCE_LOCAL">
12
13 <!-- 描述信息.(可选)
14 <description> </description>
15 -->
16 <!-- javax.persistence.PersistenceProvider接口的一个实现类(可选)
17 <provider> </provider>
18 -->
19 <!-- Jta-data-source和 non-jta-data-source用于分别指定持久化提供商使用的JTA和/或non-JTA数据源的全局JNDI名称(可选)
20 <jta-data-source>java:/MySqlDS</jta-data-source>
21 <non-jta-data-source> </non-jta-data-source>
22 -->
23 <!-- 声明orm.xml所在位置.(可选)
24 <mapping-file>product.xml</mapping-file>
25 -->
26 <!-- 以包含persistence.xml的jar文件为基准的相对路径,添加额外的jar文件.(可选)
27 <jar-file>../lib/model.jar</jar-file>
28 -->
29 <!-- 显式列出实体类,在Java SE 环境中应该显式列出.(可选)
30 <class>com.domain.User</class>
31 <class>com.domain.Product</class>
32 -->
33 <!-- 声明是否扫描jar文件中标注了@Enity类加入到上下文.若不扫描,则如下:(可选)
34 <exclude-unlisted-classes/>
35 -->
36 <!-- 厂商专有属性(可选) -->
37 <properties>
38 <!-- hibernate.hbm2ddl.auto= create-drop / create / update -->
39 <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" />
40 <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
41 <property name="hibernate.connection.username" value="remote" />
42 <property name="hibernate.connection.password" value="feiyue" />
43 <property name="hibernate.connection.url" value="jdbc:mysql://192.168.182.131:3306/jpa" />
44 <property name="hibernate.max_fetch_depth" value="3" />
45 <property name="hibernate.show_sql" value="true" />
46 <property name="hibernate.hbm2ddl.auto" value="update"/>
47 </properties>
48
49 </persistence-unit>
50
51 </persistence>
View Code
使用Maven搭建一个测试环境,Maven的使用在这里不再赘述,这里显示所依赖的jar包
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.1.9.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.1.9.Final</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.2</version>
</dependency>
</dependencies>
View Code
搭建一个测试项目 如图

Entity 实体类 User.java
1 package com.demo.bean;
2
3 // Generated 2016-3-22 17:50:11 by Hibernate Tools 4.0.0
4
5 import java.util.Date;
6 import javax.persistence.Column;
7 import javax.persistence.Entity;
8 import javax.persistence.GeneratedValue;
9 import static javax.persistence.GenerationType.IDENTITY;
10 import javax.persistence.Id;
11 import javax.persistence.Table;
12 import javax.persistence.Temporal;
13 import javax.persistence.TemporalType;
14 import javax.persistence.UniqueConstraint;
15
16 /**
17 * User generated by hbm2java
18 */
19 @Entity
20 @Table(name = "user", catalog = "jpa", uniqueConstraints = @UniqueConstraint(columnNames = "phoneNo"))
21 public class User implements java.io.Serializable {
22
23 /**
24 *
25 */
26 private static final long serialVersionUID = -2126972038126149900L;
27 private Long id;
28 private String firstName;
29 private String phoneNo;
30 private String lastName;
31 private Date birthday;
32
33 public User() {
34 }
35
36 public User(String firstName, String phoneNo, String lastName, Date birthday) {
37 this.firstName = firstName;
38 this.phoneNo = phoneNo;
39 this.lastName = lastName;
40 this.birthday = birthday;
41 }
42
43 @Id
44 @GeneratedValue(strategy = IDENTITY)
45 @Column(name = "id", unique = true, nullable = false)
46 public Long getId() {
47 return this.id;
48 }
49
50 public void setId(Long id) {
51 this.id = id;
52 }
53
54 @Column(name = "firstName", nullable = false, length = 45)
55 public String getFirstName() {
56 return this.firstName;
57 }
58
59 public void setFirstName(String firstName) {
60 this.firstName = firstName;
61 }
62
63 @Column(name = "phoneNo", unique = true, nullable = false, length = 45)
64 public String getPhoneNo() {
65 return this.phoneNo;
66 }
67
68 public void setPhoneNo(String phoneNo) {
69 this.phoneNo = phoneNo;
70 }
71
72 @Column(name = "lastName", nullable = false, length = 45)
73 public String getLastName() {
74 return this.lastName;
75 }
76
77 public void setLastName(String lastName) {
78 this.lastName = lastName;
79 }
80
81 @Temporal(TemporalType.DATE)
82 @Column(name = "birthday", nullable = false, length = 10)
83 public Date getBirthday() {
84 return this.birthday;
85 }
86
87 public void setBirthday(Date birthday) {
88 this.birthday = birthday;
89 }
90
91 @Override
92 public String toString(){
93 return "User:"+
94 "Id:"+id+" Name:"+firstName+" "+lastName+"";
95 }
96
97 }
View Code
Junit4 测试客户端,这里只简单的操作了保存与创建简单的HQL查询。JPATest.java
1 package com.demo.client;
2
3 import java.util.Date;
4 import java.util.List;
5
6 import javax.persistence.EntityManager;
7 import javax.persistence.EntityManagerFactory;
8 import javax.persistence.Persistence;
9
10 import org.junit.Test;
11
12 import com.demo.bean.User;
13
14 public class JPATest {
15
16 @Test
17 public void testSave(){
18 EntityManagerFactory factory = Persistence.createEntityManagerFactory("TestJPAUnit");
19 EntityManager em = factory.createEntityManager();
20 em.getTransaction().begin();
21 User user = new User(); //user为new状态
22 user.setBirthday(new Date());
23 user.setFirstName("Khalid");
24 user.setLastName("Khalid");
25 user.setPhoneNo("+8618888888889");
26 em.persist(user);//持久化user实例
27 em.getTransaction().commit();//提交事务
28 em.close();
29 factory.close();
30 }
31
32 @Test
33 public void testQuery(){
34 EntityManagerFactory factory = Persistence.createEntityManagerFactory("TestJPAUnit");
35 EntityManager em = factory.createEntityManager();
36 @SuppressWarnings("unchecked")
37 List<User> user=em.createQuery("from User").getResultList();
38 System.out.println(user);
39 }
40 }
View Code
运行客户端就可以啦~~其实并没有什么特别的- -。
3.Spring对JPA的支持
前人总结的很好,我就不在赘述了
Spring 框架对 JPA 提供的支持主要体现在如下几个方面:
-
首先,它使得 JPA 配置变得更加灵活。JPA 规范要求,配置文件必须命名为 persistence.xml,并存在于类路径下的 META-INF 目录中。该文件通常包含了初始化 JPA 引擎所需的全部信息。Spring 提供的 LocalContainerEntityManagerFactoryBean 提供了非常灵活的配置,persistence.xml 中的信息都可以在此以属性注入的方式提供。
-
其次,Spring 实现了部分在 EJB 容器环境下才具有的功能,比如对 @PersistenceContext、@PersistenceUnit 的容器注入支持。
-
第三,也是最具意义的,Spring 将 EntityManager 的创建与销毁、事务管理等代码抽取出来,并由其统一管理,开发者不需要关心这些,业务方法中只剩下操作领域对象的代码,事务管理和 EntityManager 创建、销毁的代码都不再需要开发者关心了。
4.使用SpringDataJPA 让一切变得最为简单
使用Spring Data JPA框架,它会帮助你简化Spring唯一没有控制到的DAO层,甚至让你完全不用编写DAO层繁琐的业务逻辑,你仅仅需要做的只有声明接口。
不多说直接上代码!!!
首先依旧是建库与表
1 CREATE DATABASE sdjpa;
2 USE sdjpa;
3 CREATE TABLE `user` (
4 `id` bigint(20) NOT NULL AUTO_INCREMENT,
5 `firstName` varchar(45) NOT NULL,
6 `phoneNo` varchar(45) NOT NULL,
7 `lastName` varchar(45) NOT NULL,
8 `birthday` date NOT NULL,
9 PRIMARY KEY (`id`),
10 UNIQUE KEY `id_UNIQUE` (`id`),
11 UNIQUE KEY `phoneNo_UNIQUE` (`phoneNo`)
12 ) ENGINE=InnoDB DEFAULT CHARSET=UTF-8;
View Code
既然是Spring,applicationContext.xml内有关SpringDataJPA的相关配置
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns:tx="http://www.springframework.org/schema/tx"
4 xmlns:context="http://www.springframework.org/schema/context"
5 xmlns:jpa="http://www.springframework.org/schema/data/jpa"
6 xmlns:task="http://www.springframework.org/schema/task"
7 xmlns:aop="http://www.springframework.org/schema/aop"
8 xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
9 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
10 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
11 http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd
12 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
13 http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"
14 default-lazy-init="true">
15
16
17 <!-- 如果spring用了jpa,并且类型为LocalContainerEntityManagerFactoryBean,则组件注册在此配置文件出现即可,其余配置文件可忽略
18 使用component来替代annotation 自动注册bean, 并保证@Required、@Autowired的属性被注入 -->
19 <context:component-scan base-package="com.**.client,com.demo.dao"/>
20
21 <!-- 数据源-->
22 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
23 <property name="driverClassName" value="com.mysql.jdbc.Driver" />
24 <property name="url" value="jdbc:mysql://192.168.182.131/sdjpa"/>
25 <property name="username" value="root" />
26 <property name="password" value="feiyue" />
27 </bean>
28
29 <!-- Hibernate对Jpa的实现 -->
30 <bean id="hibernateJpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
31
32 <!-- 定义实体管理器工厂
33 Jpa配置 LocalContainerEntityManagerFactoryBean这个选项Spring扮演了容器的角色。完全掌管JPA -->
34 <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
35 <!-- 指定数据源 -->
36 <property name="dataSource" ref="dataSource"/>
37 <!-- 指定Jpa持久化实现厂商类,这里以Hibernate为例 -->
38 <property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter"/>
39 <!-- 指定Entity实体类包路径 -->
40 <property name="packagesToScan" >
41 <array>
42 <value>com.**.bean</value>
43 </array>
44 </property>
45 <!-- 指定JPA属性;如Hibernate中指定是否显示SQL的是否显示、方言等 -->
46 <property name="jpaProperties">
47 <props>
48 <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
49 <!-- EJB3规范的命名实现 例如 firstName 会被映射为 first_name 建议取消
50 <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
51 -->
52 <prop key="hibernate.show_sql">true</prop>
53 <prop key="hibernate.format_sql">true</prop>
54 <!-- 也可以配置为update 自动生成表 我这里在数据库已经建好了 就设置为validate-->
55 <prop key="hibernate.hbm2ddl.auto">update</prop>
56 </props>
57 </property>
58 </bean>
59
60 <!-- 重要配置:启用扫描并自动创建代理的功能 -->
61 <jpa:repositories base-package="com.**.dao" entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager"/>
62
63
64
65 <!-- Jpa 事务管理器 -->
66 <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
67 <property name="entityManagerFactory" ref="entityManagerFactory"/>
68 </bean>
69
70 <!-- 开启注解事务 -->
71 <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
72
73
74 <!-- 启动对@AspectJ(面向切面)注解的支持 -->
75 <aop:aspectj-autoproxy />
76
77 </beans>
View Code
依旧是Maven,这次的依赖库
1 <dependencies>
2 <dependency>
3 <groupId>log4j</groupId>
4 <artifactId>log4j</artifactId>
5 <version>1.2.17</version>
6 </dependency>
7
8 <dependency>
9 <groupId>com.fasterxml.jackson.core</groupId>
10 <artifactId>jackson-databind</artifactId>
11 <version>2.4.5</version>
12 </dependency>
13
14 <dependency>
15 <groupId>com.fasterxml.jackson.core</groupId>
16 <artifactId>jackson-annotations</artifactId>
17 <version>2.4.5</version>
18 </dependency>
19
20 <dependency>
21 <groupId>com.fasterxml.jackson.core</groupId>
22 <artifactId>jackson-core</artifactId>
23 <version>2.4.5</version>
24 </dependency>
25
26
27 <dependency>
28 <groupId>aspectj</groupId>
29 <artifactId>aspectjweaver</artifactId>
30 <version>1.5.4</version>
31 </dependency>
32 <dependency>
33 <groupId>org.springframework</groupId>
34 <artifactId>spring-core</artifactId>
35 <version>4.1.6.RELEASE</version>
36 </dependency>
37
38 <dependency>
39 <groupId>org.springframework</groupId>
40 <artifactId>spring-aop</artifactId>
41 <version>4.1.6.RELEASE</version>
42 </dependency>
43
44 <dependency>
45 <groupId>org.springframework.data</groupId>
46 <artifactId>spring-data-jpa</artifactId>
47 <version>1.7.0.RELEASE</version>
48 </dependency>
49
50 <dependency>
51 <groupId>org.springframework</groupId>
52 <artifactId>spring-beans</artifactId>
53 <version>4.1.6.RELEASE</version>
54 </dependency>
55
56 <dependency>
57 <groupId>org.springframework</groupId>
58 <artifactId>spring-context</artifactId>
59 <version>4.1.6.RELEASE</version>
60 </dependency>
61
62 <dependency>
63 <groupId>org.springframework</groupId>
64 <artifactId>spring-context-support</artifactId>
65 <version>4.1.6.RELEASE</version>
66 </dependency>
67
68 <dependency>
69 <groupId>org.springframework</groupId>
70 <artifactId>spring-jdbc</artifactId>
71 <version>4.1.6.RELEASE</version>
72 </dependency>
73
74 <dependency>
75 <groupId>org.springframework</groupId>
76 <artifactId>spring-expression</artifactId>
77 <version>4.1.6.RELEASE</version>
78 </dependency>
79
80 <dependency>
81 <groupId>org.springframework</groupId>
82 <artifactId>spring-tx</artifactId>
83 <version>4.1.6.RELEASE</version>
84 </dependency>
85
86 <dependency>
87 <groupId>org.springframework</groupId>
88 <artifactId>spring-test</artifactId>
89 <version>4.1.6.RELEASE</version>
90 </dependency>
91
92 <dependency>
93 <groupId>org.springframework</groupId>
94 <artifactId>spring-orm</artifactId>
95 <version>4.1.6.RELEASE</version>
96 </dependency>
97 <dependency>
98 <groupId>org.springframework</groupId>
99 <artifactId>spring-webmvc</artifactId>
100 <version>4.1.6.RELEASE</version>
101 </dependency>
102 <dependency>
103 <groupId>org.hibernate</groupId>
104 <artifactId>hibernate-core</artifactId>
105 <version>4.1.9.Final</version>
106 </dependency>
107
108 <dependency>
109 <groupId>org.hibernate</groupId>
110 <artifactId>hibernate-entitymanager</artifactId>
111 <version>4.1.9.Final</version>
112 </dependency>
113 <dependency>
114 <groupId>org.springframework</groupId>
115 <artifactId>spring-web</artifactId>
116 <version>4.1.6.RELEASE</version>
117 </dependency>
118 <dependency>
119 <groupId>commons-dbcp</groupId>
120 <artifactId>commons-dbcp</artifactId>
121 <version>1.4</version>
122 </dependency>
123
124 <dependency>
125 <groupId>commons-codec</groupId>
126 <artifactId>commons-codec</artifactId>
127 <version>1.10</version>
128 </dependency>
129
130 <dependency>
131 <groupId>commons-io</groupId>
132 <artifactId>commons-io</artifactId>
133 <version>1.3.2</version>
134 </dependency>
135
136 <dependency>
137 <groupId>commons-pool</groupId>
138 <artifactId>commons-pool</artifactId>
139 <version>1.6</version>
140 </dependency>
141
142 <dependency>
143 <groupId>mysql</groupId>
144 <artifactId>mysql-connector-java</artifactId>
145 <version>5.1.25</version>
146 </dependency>
147
148 <dependency>
149 <groupId>org.slf4j</groupId>
150 <artifactId>slf4j-log4j12</artifactId>
151 <version>1.7.2</version>
152 </dependency>
153 </dependencies>
View Code
搭建环境~

核心DAO层代码 IUserRepository.java 是不是非常简洁,剩下的工作Spring Data JPA框架帮你代理完成
1 package com.demo.dao;
2
3
4
5 import java.util.List;
6
7 import org.springframework.data.jpa.repository.JpaRepository;
8 import org.springframework.data.jpa.repository.Modifying;
9 import org.springframework.data.jpa.repository.Query;
10 import org.springframework.data.repository.CrudRepository;
11 import org.springframework.data.repository.PagingAndSortingRepository;
12 import org.springframework.data.repository.query.Param;
13 import org.springframework.stereotype.Repository;
14
15 import com.demo.bean.User;
16
17
18
19 @Repository
20 public interface IUserRepository extends JpaRepository<User, Long>,CrudRepository<User, Long>,
21 PagingAndSortingRepository<User, Long>
22 {
23 /**
24 * 可使用的接口有: **********
25 * Repository:是 Spring Data的一个核心接口,它不提供任何方法,开发者需要在自己定义的接口中声明需要的方法。**
26 * CrudRepository:继承Repository,提供增删改查方法,可以直接调用。 **
27 * PagingAndSortingRepository:继承CrudRepository,具有分页查询和排序功能(本类实例) **
28 * JpaRepository: 继承PagingAndSortingRepository,针对JPA技术提供的接口 **
29 * JpaSpecificationExecutor: 可以执行原生SQL查询 **
30 * 继承不同的接口,有两个不同的泛型参数,他们是该持久层操作的类对象和主键类型。 **
31 *********************************************************************************
32 * */
33
34 //更新操作 需要注解Modifying
35 @Modifying
36 @Query("update User u set u.firstName=:firstName where u.id=:id")
37 public void updateFirstName(@Param("firstName") String firstName,@Param("id") long id);
38
39 @Query("select u from User u where u.phoneNo=?1")
40 public User findByPhoneno(String phoneNo);
41
42 //nativeQuery 使用SQL查询 自动进行转换
43 @Query(value="select * from user u where u.firstName=?1",nativeQuery=true)
44 public List<User> findByFirstname(String firstName);
45 }
View Code
Entity实体类 User.java 注意catalog的变化
1 package com.demo.bean;
2
3 // Generated 2016-3-22 17:50:11 by Hibernate Tools 4.0.0
4
5 import java.util.Date;
6 import javax.persistence.Column;
7 import javax.persistence.Entity;
8 import javax.persistence.GeneratedValue;
9 import static javax.persistence.GenerationType.IDENTITY;
10 import javax.persistence.Id;
11 import javax.persistence.Table;
12 import javax.persistence.Temporal;
13 import javax.persistence.TemporalType;
14 import javax.persistence.UniqueConstraint;
15
16 /**
17 * User generated by hbm2java
18 */
19 @Entity
20 @Table(name = "user", catalog = "sdjpa", uniqueConstraints = @UniqueConstraint(columnNames = "phoneNo"))
21 public class User implements java.io.Serializable {
22
23 /**
24 *
25 */
26 private static final long serialVersionUID = -2126972038126149900L;
27 private Long id;
28 private String firstName;
29 private String phoneNo;
30 private String lastName;
31 private Date birthday;
32
33 public User() {
34 }
35
36 public User(String firstName, String phoneNo, String lastName, Date birthday) {
37 this.firstName = firstName;
38 this.phoneNo = phoneNo;
39 this.lastName = lastName;
40 this.birthday = birthday;
41 }
42
43 @Id
44 @GeneratedValue(strategy = IDENTITY)
45 @Column(name = "id", unique = true, nullable = false)
46 public Long getId() {
47 return this.id;
48 }
49
50 public void setId(Long id) {
51 this.id = id;
52 }
53
54 @Column(name = "firstName", nullable = false, length = 45)
55 public String getFirstName() {
56 return this.firstName;
57 }
58
59 public void setFirstName(String firstName) {
60 this.firstName = firstName;
61 }
62
63 @Column(name = "phoneNo", unique = true, nullable = false, length = 45)
64 public String getPhoneNo() {
65 return this.phoneNo;
66 }
67
68 public void setPhoneNo(String phoneNo) {
69 this.phoneNo = phoneNo;
70 }
71
72 @Column(name = "lastName", nullable = false, length = 45)
73 public String getLastName() {
74 return this.lastName;
75 }
76
77 public void setLastName(String lastName) {
78 this.lastName = lastName;
79 }
80
81 @Temporal(TemporalType.DATE)
82 @Column(name = "birthday", nullable = false, length = 10)
83 public Date getBirthday() {
84 return this.birthday;
85 }
86
87 public void setBirthday(Date birthday) {
88 this.birthday = birthday;
89 }
90
91 @Override
92 public String toString(){
93 return "User:"+
94 "Id:"+id+" Name:"+firstName+" "+lastName+"";
95 }
96
97 }
View Code
为了简单 我通过Spring-test框架以及Junit整合编写了一个测试客户端,没有编写Service层,Spring-test与Junit整合的教程可以点这里,讲的十分详细。使用Spring-test非常大的好处是支持数据库的自动回滚,以至于测试不会破坏数据库的现场,而且可以频繁的测试多组数据。代码如下 TestJPADemoClient.java,基本包括了常用的查询Demo
1 package com.demo.client;
2
3 import static org.junit.Assert.*;
4
5 import java.util.Date;
6
7 import javax.annotation.Resource;
8
9 import org.junit.*;
10 import org.junit.runner.RunWith;
11 import org.springframework.data.domain.Page;
12 import org.springframework.data.domain.PageRequest;
13 import org.springframework.data.domain.Sort;
14 import org.springframework.data.domain.Sort.Direction;
15 import org.springframework.test.annotation.Rollback;
16 import org.springframework.test.context.ContextConfiguration;
17 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
18 import org.springframework.transaction.annotation.Transactional;
19
20 import com.demo.bean.User;
21 import com.demo.dao.IUserRepository;
22
23 @RunWith(SpringJUnit4ClassRunner.class)
24 @ContextConfiguration(locations = { "classpath:applicationContext.xml" })
25 @Transactional
26 public class TestJpaDemoClient {
27
28 @Resource
29 private IUserRepository userRepository;
30
31 // private ApplicationContext applicationContext;
32
33 private static long userId;
34
35 private static User user = null;
36
37 @Before
38 // 插入一些测试数据,与测试方法处于同一事务,自动回滚
39
40 public void setUp() {
41 System.out.println("***************插入测试数据****************");
42 user = new User();
43 user.setBirthday(new Date());
44 user.setFirstName("Fei");
45 user.setLastName("Yue");
46 user.setPhoneNo("+8618888888889");
47 user = userRepository.save(user);
48 userId = user.getId();
49 User userForInsert = new User();
50 userForInsert.setBirthday(new Date());
51 userForInsert.setFirstName("Khalid");
52 userForInsert.setLastName("Khalid");
53 userForInsert.setPhoneNo("+86188888888888");
54 userRepository.save(userForInsert);
55 }
56
57 @Test
58 public void testInsert() {
59 System.out.println("********测试插入*********");
60 System.out.println("********插入前*********");
61 System.out.println(userRepository.findAll());
62 User userForInsert = new User();
63 userForInsert.setBirthday(new Date());
64 userForInsert.setFirstName("Khalid");
65 userForInsert.setLastName("Khalid");
66 userForInsert.setPhoneNo("+861888888888899");
67 userRepository.save(userForInsert);
68 System.out.println("********插入后*********");
69 System.out.println(userRepository.findAll());
70
71 }
72
73 @Test
74 public void testUpdate() {
75 System.out.println("********测试更新*********");
76 System.out.println("*******更新前************");
77 System.out.println(userRepository.findOne(userId));
78 // userRepository.updateFirstName("FY", userId);
79 user.setFirstName("Fy");
80 userRepository.save(user);
81 System.out.println("*******更新后************");
82 System.out.println(userRepository.findOne(userId));
83 }
84
85 @Test
86 public void testQuery() {
87 System.out.println("********测试条件查询*********");
88 User findUser = userRepository.findByPhoneno(user.getPhoneNo());
89 assertTrue(findUser.getPhoneNo().equals(user.getPhoneNo()));
90 System.out.println("********查询结果:" + findUser);
91 }
92
93 @Test
94 public void testFindAll() {
95 System.out.println("********测试查询所有*********");
96 System.out.println(userRepository.findAll());
97 }
98
99 @Test
100 public void testDelById() {
101 System.out.println("********以ID删除*********");
102 userRepository.delete(userId);
103 System.out.println("********以ID删除*********");
104 }
105
106 @Test
107 public void testDel() {
108 System.out.println("********以对象删除********");
109 System.out.println("*******删除前*********");
110 System.out.println(userRepository.findOne(userId));
111 userRepository.delete(user);
112 System.out.println("********删除后*********");
113 assertNull(userRepository.findOne(userId));
114 }
115
116 @Test
117 public void testDelAll() {
118 System.out.println("********批量删除*********");
119 System.out.println("********删除前*********");
120 System.out.println(userRepository.findAll());
121 userRepository.deleteAllInBatch();
122 System.out.println("********删除后*********");
123 assertTrue(userRepository.findAll().isEmpty());
124 }
125
126 @Test
127 public void testPageQuery() {
128 System.out.println("********测试分页查询********");
129 Page<User> page = userRepository.findAll(new PageRequest(0, 1));
130 System.out.println("总页数:" + page.getTotalPages());
131 for (int i = 0; i < page.getTotalPages(); i++) {
132 System.out.println("第" + (i + 1) + "页");
133 for (User user : page) {
134 System.out.println(user);
135 }
136 if (page.hasNext()) {
137 page = userRepository.findAll(page.nextPageable());
138 } else
139 break;
140 }
141
142 }
143
144 @Test
145 public void testCount() {
146 System.out.println("********测试查询条数********");
147 System.out.println("条数为:" + userRepository.count());
148 }
149
150 @Test
151 public void testSortQuery(){
152 System.out.println("********测试排序查询********");
153 System.out.println(userRepository.findAll(new Sort(Direction.DESC,"firstName")));
154
155 }
156
157 @Test
158 public void testSQL(){
159 System.out.println("********使用原生SQL查询********");
160 System.out.println(userRepository.findByFirstname("Fei"));
161 }
162
163 }
View Code
4.1 Spring Data JPA的查询机制
引用前辈的话,写的非常详细清楚~~原文链接
通过解析方法名创建查询
通过前面的例子,读者基本上对解析方法名创建查询的方式有了一个大致的了解,这也是 Spring Data JPA 吸引开发者的一个很重要的因素。该功能其实并非 Spring Data JPA 首创,而是源自一个开源的 JPA 框架 Hades,该框架的作者 Oliver Gierke 本身又是 Spring Data JPA 项目的 Leader,所以把 Hades 的优势引入到 Spring Data JPA 也就是顺理成章的了。
框架在进行方法名解析时,会先把方法名多余的前缀截取掉,比如 find、findBy、read、readBy、get、getBy,然后对剩下部分进行解析。并且如果方法的最后一个参数是 Sort 或者 Pageable 类型,也会提取相关的信息,以便按规则进行排序或者分页查询。
在创建查询时,我们通过在方法名中使用属性名称来表达,比如 findByUserAddressZip ()。框架在解析该方法时,首先剔除 findBy,然后对剩下的属性进行解析,详细规则如下(此处假设该方法针对的域对象为 AccountInfo 类型):
- 先判断 userAddressZip (根据 POJO 规范,首字母变为小写,下同)是否为 AccountInfo 的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,继续第二步;
- 从右往左截取第一个大写字母开头的字符串(此处为 Zip),然后检查剩下的字符串是否为 AccountInfo 的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则重复第二步,继续从右往左截取;最后假设 user 为 AccountInfo 的一个属性;
- 接着处理剩下部分( AddressZip ),先判断 user 所对应的类型是否有 addressZip 属性,如果有,则表示该方法最终是根据 "AccountInfo.user.addressZip" 的取值进行查询;否则继续按照步骤 2 的规则从右往左截取,最终表示根据 "AccountInfo.user.address.zip" 的值进行查询。
可能会存在一种特殊情况,比如 AccountInfo 包含一个 user 的属性,也有一个 userAddress 属性,此时会存在混淆。读者可以明确在属性之间加上 "_" 以显式表达意图,比如 "findByUser_AddressZip()" 或者 "findByUserAddress_Zip()"。
在查询时,通常需要同时根据多个属性进行查询,且查询的条件也格式各样(大于某个值、在某个范围等等),Spring Data JPA 为此提供了一些表达条件查询的关键字,大致如下:
- And --- 等价于 SQL 中的 and 关键字,比如 findByUsernameAndPassword(String user, Striang pwd);
- Or --- 等价于 SQL 中的 or 关键字,比如 findByUsernameOrAddress(String user, String addr);
- Between --- 等价于 SQL 中的 between 关键字,比如 findBySalaryBetween(int max, int min);
- LessThan --- 等价于 SQL 中的 "<",比如 findBySalaryLessThan(int max);
- GreaterThan --- 等价于 SQL 中的">",比如 findBySalaryGreaterThan(int min);
- IsNull --- 等价于 SQL 中的 "is null",比如 findByUsernameIsNull();
- IsNotNull --- 等价于 SQL 中的 "is not null",比如 findByUsernameIsNotNull();
- NotNull --- 与 IsNotNull 等价;
- Like --- 等价于 SQL 中的 "like",比如 findByUsernameLike(String user);
- NotLike --- 等价于 SQL 中的 "not like",比如 findByUsernameNotLike(String user);
- OrderBy --- 等价于 SQL 中的 "order by",比如 findByUsernameOrderBySalaryAsc(String user);
- Not --- 等价于 SQL 中的 "! =",比如 findByUsernameNot(String user);
- In --- 等价于 SQL 中的 "in",比如 findByUsernameIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;
- NotIn --- 等价于 SQL 中的 "not in",比如 findByUsernameNotIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数
使用 @Query 创建查询
@Query 注解的使用非常简单,只需在声明的方法上面标注该注解,同时提供一个 JP QL 查询语句即可,如下所示:
清单 16. 使用 @Query 提供自定义查询语句示例
public interface UserDao extends Repository<AccountInfo, Long> {
@Query("select a from AccountInfo a where a.accountId = ?1")
public AccountInfo findByAccountId(Long accountId);
@Query("select a from AccountInfo a where a.balance > ?1")
public Page<AccountInfo> findByBalanceGreaterThan(
Integer balance,Pageable pageable);
}
很多开发者在创建 JP QL 时喜欢使用命名参数来代替位置编号,@Query 也对此提供了支持。JP QL 语句中通过": 变量"的格式来指定参数,同时在方法的参数前面使用 @Param 将方法参数与 JP QL 中的命名参数对应,示例如下:
清单 17. @Query 支持命名参数示例
public interface UserDao extends Repository<AccountInfo, Long> {
public AccountInfo save(AccountInfo accountInfo);
@Query("from AccountInfo a where a.accountId = :id")
public AccountInfo findByAccountId(@Param("id")Long accountId);
@Query("from AccountInfo a where a.balance > :balance")
public Page<AccountInfo> findByBalanceGreaterThan(
@Param("balance")Integer balance,Pageable pageable);
}
此外,开发者也可以通过使用 @Query 来执行一个更新操作,为此,我们需要在使用 @Query 的同时,用 @Modifying 来将该操作标识为修改查询,这样框架最终会生成一个更新的操作,而非查询。如下所示:
清单 18. 使用 @Modifying 将查询标识为修改查询
@Modifying
@Query("update AccountInfo a set a.salary = ?1 where a.salary < ?2")
public int increaseSalary(int after, int before);
通过调用 JPA 命名查询语句创建查询
命名查询是 JPA 提供的一种将查询语句从方法体中独立出来,以供多个方法共用的功能。Spring Data JPA 对命名查询也提供了很好的支持。用户只需要按照 JPA 规范在 orm.xml 文件或者在代码中使用 @NamedQuery(或 @NamedNativeQuery)定义好查询语句,唯一要做的就是为该语句命名时,需要满足”DomainClass.methodName()”的命名规则。假设定义了如下接口:
清单 19. 使用 JPA 命名查询时,声明接口及方法时不需要什么特殊处理
public interface UserDao extends Repository<AccountInfo, Long> {
......
public List<AccountInfo> findTop5();
}
如果希望为 findTop5() 创建命名查询,并与之关联,我们只需要在适当的位置定义命名查询语句,并将其命名为 "AccountInfo.findTop5",框架在创建代理类的过程中,解析到该方法时,优先查找名为 "AccountInfo.findTop5" 的命名查询定义,如果没有找到,则尝试解析方法名,根据方法名字创建查询。
创建查询的顺序
Spring Data JPA 在为接口创建代理对象时,如果发现同时存在多种上述情况可用,它该优先采用哪种策略呢?为此,<jpa:repositories> 提供了 query-lookup-strategy 属性,用以指定查找的顺序。它有如下三个取值:
- create --- 通过解析方法名字来创建查询。即使有符合的命名查询,或者方法通过 @Query 指定的查询语句,都将会被忽略。
- create-if-not-found --- 如果方法通过 @Query 指定了查询语句,则使用该语句实现查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该命名查询;如果两者都没有找到,则通过解析方法名字来创建查询。这是 query-lookup-strategy 属性的默认值。
- use-declared-query --- 如果方法通过 @Query 指定了查询语句,则使用该语句实现查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该命名查询;如果两者都没有找到,则抛出异常。
5.总结
这篇文章结合了网上大多SpringDataJPA的讲解,加上一些自己的理解,希望可以对需要学习的程序员带来帮助。
2016-03-27
Khalid