一篇 SpringData+JPA 总结
概述
-
SpringData,Spring 的一个子项目,用于简化数据库访问,支持 NoSQL 和关系数据库存储
-
SpringData 项目所支持 NoSQL 存储
- MongDB(文档数据库)
- Neo4j(图形数据库)
- Redis(键 / 值存储)
- Hbase(列族数据库)
-
SpringData 项目所支持的关系存储技术
- JDBC
- JPA(本次重点)
SpringData 整合 JPA 以及 HelloWorld
-
Maven 项目 jar 包导入
<properties> <spring.verison>4.3.8.RELEASE</spring.verison> <hibernate.version>4.3.1.Final</hibernate.version> </properties> <dependencies> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-commons</artifactId> <version>1.6.2.RELEASE</version> </dependency>
<span class="hljs-tag"><<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.data<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-data-jpa<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>1.4.2.RELEASE<span class="hljs-tag"></<span class="hljs-name">version</span>></span> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-core<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>${spring.verison}<span class="hljs-tag"></<span class="hljs-name">version</span>></span> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-orm<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>${spring.verison}<span class="hljs-tag"></<span class="hljs-name">version</span>></span> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-web<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>${spring.verison}<span class="hljs-tag"></<span class="hljs-name">version</span>></span> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.aspectj<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>aspectjweaver<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>1.8.10<span class="hljs-tag"></<span class="hljs-name">version</span>></span> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>com.mchange<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>c3p0<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>0.9.5<span class="hljs-tag"></<span class="hljs-name">version</span>></span> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.hibernate<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>hibernate-core<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>${hibernate.version}<span class="hljs-tag"></<span class="hljs-name">version</span>></span> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.hibernate<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>hibernate-entitymanager<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>${hibernate.version}<span class="hljs-tag"></<span class="hljs-name">version</span>></span> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>mysql<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>mysql-connector-java<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>5.1.21<span class="hljs-tag"></<span class="hljs-name">version</span>></span> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
</dependencies>
-
步骤
- 配置 Spring 整合 JPA
- 在 Spring 配置文件中配置 SpringData
- 声明持久化层的接口,该接口继承 Repository
- 在接口中声明需要的方法
配置 Spring 整合 JPA
- 此步骤在我以前的博文(一篇 JPA 总结)末端可以找到,这里不再啰嗦。
在 Spring 配置文件中配置 SpringData
-
即在 Spring 配置文件中配置
<jpa:repository />
-
配置了
<jpa:repository />
后,Spring 初始化容器时将会扫描 base-package 指定的包目录及其子目录,为继承 Repository 或其子接口的接口创建代理对象,并将代理对象注册为 Spring Bean,业务层便可以通过 Spring 自动封装的特性来直接使用该对象 -
配置文件代码
<!--base-package: 扫描 Repository Bean 所在的 package,DAO 层 --> <jpa:repositories base-package="com.springdata.jpa.dao" entity-manager-factory-ref="entityManager"/>
声明持久化层的接口,继承 Repository 接口及其子接口
- 我们在上一步中配置文件的 base-package 包下新建接口,继承 Repository 或其子接口
- 在该步骤之前我们先需要编写实体,如我们声明的接口中 Person 类,其所需的配置和注解同时也可以参看以前的博文(一篇 JPA 总结)。
- 所需实体类(Person & Address)的基本属性
- 所需实体类(Person & Address)的基本属性
- 在该步骤之前我们先需要编写实体,如我们声明的接口中 Person 类,其所需的配置和注解同时也可以参看以前的博文(一篇 JPA 总结)。
在接口中声明需要的方法
- 如上图,方法 getPersonByPersonName(String personName) 为根据 personName 属性 获取 Person 对象
- 注意: 方法名必须保持一致!
测试
-
至此我们对 SpringData 整合 JPA 环境以及准备代码工作完成,附上一张代码结构图,下面开始测试。
-
在 maven 项目的 test 目录下新建测试类测试 getPersonByPersonName(String personName) 方法(自动生成的数据表中已加入数据,Person 表和 Address 表)
public class AllTest {
<span class="hljs-keyword">private</span> ApplicationContext context; <span class="hljs-keyword">private</span> PersonRepository personRepository; { <span class="hljs-comment">// 从对应的 spring 配置文件中,初始化 SpringIOC 容器</span> context = <span class="hljs-keyword">new</span> ClassPathXmlApplicationContext(<span class="hljs-string">"spring-config.xml"</span>); <span class="hljs-comment">// 由于 SpringData 的配置,继承 Repository 及其子接口的类将会被自动加载到 IOC 容器,便于获取</span> personRepository = context.getBean(PersonRepository.<span class="hljs-keyword">class</span>); } <span class="hljs-comment">/** * 测试方法 * */</span> @Test <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testGetPerson</span>()</span> { <span class="hljs-comment">// 使用从 Spring 的 IOC 容器获取的 personRepository 实例中调用测试方法</span> Person person = personRepository.getPersonByPersonName(<span class="hljs-string">"bgZyy"</span>); System.<span class="hljs-keyword">out</span>.println(person); }
}
-
运行结果
Repository 接口
- 其是一个空接口,即是一个标记接口,若我们定义的接口实现了 Repository 接口,则该接口就会被 IOC 容器识别为一个 Repository Bean,纳入到 IOC 容器中(Spring 帮我们实现该接口,进而被纳入 IOC 容器),进而可以在该接口中定义满足一定规范的方法
- Repository Bean 也可以使用注解去代替实现接口
Repository 子接口
- Repository: 仅仅是一个标识,表明任何继承它的均为仓库接口类
- CrudRepository: 继承 Repository,实现了一组 CRUD 相关的方法
- PagingAndSortingRepository: 继承 CrudRepository,实现了一组分页排序相关的方法
- JpaRepository: 继承 PagingAndSortingRepository,实现一组 JPA 规范相关的方法
- 自定义的 XxxxRepository 需要继承 JpaRepository,这样的 XxxxRepository 接口就具备了通用的数据访问控制层的能力。
JpaSpecificationExecutor: 不属于 Repository 体系,实现一组 JPA Criteria 查询相关的方法
SpringData 方法命名规范
查询操作
-
查询方法以 find | read | get 开头
-
涉及到查询条件时,用条件关键字连接(条件属性首字母需要大写)
-
遵循以上方法命名规则进一步理解 getPersonByPerosnName(String personName),以及如下两个方法
-
对上图两方法进行测试
-
SpringData 所支持的关键字
级联属性的查询
- SpringData 还支持级联属性的查询,如查询 Person 类中的 address 属性。若实体的属性拥有和级联属性同名的一个普通属性,那么默认情况下使用自身普通属性查询,若想使用级联属性查询,则需要使用下划线连接标记,如下:
查询方法解析流程
- 创建如下的查询:getPersonByAddressCity();
- 框架在解析该方法时,首先剔除 findBy,然后对剩下的先判断 userDepUuid (根据 POJO 规范,首字母变为小写)是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,继续第二步;
- 从右往左截取第一个大写字母开头的字符串 ( 此处为 City),然后检查剩下的字符串是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则重复第二步,继续从右往左截取;最后假设 address 为查询实体的一个属性;
- 接着处理剩下部分(City),先判断 address 所对应的类型是否有 city 属性,如果有,则表示该方法最终是根据 Address.city 的取值进行查询;否则继续按照步骤 2 的规则从右往左截取,最终如何进行查询。
- 可能会存在一种特殊情况,即上述所说的级联属性的查询(在 IDEA 中编写 SpringData 方法有提示,不易出错)。
注解
使用上述 SpringData 方法规则进行查询简单,但是完成不了子查询等功能,此时便可以使用 @Query 注解
Query
-
使用 @Query 注解实现子查询
-
使用占位符为 @Query 注解传参
-
使用命名参数为 @Query 注解传参
-
使用本地 SQL 查询(此时 Query 注解中需要标注 nativeQuery = true)
-
使用 @Query 注解进行模糊查询
@Modifying
-
我们在 Spring 下配置了 service 包下的所有方法都会当做事务方法去处理,现在我们将进行更新操作,需要将其置一个事务方法,所以将接下来的测试方法放在 service 包下
-
注意:默认情况下,SpringData 的每个方法上都有事务,但都是一个只读事务,他们不能完成修改操作
-
结合 @Modlifying 注解和 @Query 注解以及事务实现更新操作
其他
我们所实现的接口不但可以去实现 Repository 接口,而且可以去实现其子接口完成更多或以更简单的方式完成功能。
-
举例(查找某表所有的内容,使用 CURDRepository 可以快速实现)
- 实现 CURDRepository 接口
- 使用 findall() 方法
- 实现 CURDRepository 接口
-
实现翻页
- 之前实现翻页我们需要对分页信息进行封装等各种准备工作,很麻烦!掌握了 SpringData + JPA 之后就可以快速实现翻页功能。
- 方法测试展示(可以直接在测试类中去编写)
- 首先需要实现 PageAndSoryRepository 接口
- 翻页实现
- 运行结果
- 首先需要实现 PageAndSoryRepository 接口
-
实现带查询条件的翻页
- 实现 JPASpecificationPaging 接口
- 具体实现如下面代码截图,与普通翻页类似
- 运行结果(可以上面普通分页的 SQL 语句进行对比)
掌握了 SpringData + JPA 在开发过程中可以显著的提高 Dao 层的开发效率,个人认为很值得学习!
哪位大神发现文中有什么不对的地方,还望指出,以及任何建议,我定会虚心接受,先谢!