Spring Boot 的 集成 Spring Data (JPA)
完成状态
- 编写中
- 已完成
- 维护中
JPA
JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中,其具体实现参考了持久层ORM框架Hibernate实现 ------- 百度百科
hibernate 和 jpa 的关系
hibernate是持久化实现技术,jpa是持久化的标准,hibernate是一种持久化的具体实现,jpa只是一种接口协议,可以通过实现jpa 接口协议实现不同的持久化技术,比如比较成熟的有 Hibernate EntityManager, EclipseLink, OpenJPA 等。本节主要内容 Spring Data JPA 是在hibernate 基础上的封装。接下来会验证这一点。
Spring Data JPA
Spring Data JPA 是基于ORM 框架hibernate针对JPA 规范的实现。是hibernate的扩展级。Spring Data JPA 是 Spring Data 大家族中的一份子。通过Spring Data JPA 可以使开发者使用极简的代码实现对数据的访问和操作。包括常用的增删改查,排序等等的常用的数据库操作, 极大地提高了开发效率。
Spring Data 与 Spring Data JPA 的关系
Spring Data JPA 是 Spring Data 的一个子集。Spring Data 包含的内容比较多,其中包含了针对不同数据库的访问,比如SQL数据库(Orcale, Mysql等);NoSQL(Mongodb, redis等)数据库的访问。 JPA 只是其中实现了 JPS 规范的一部分内容。JPA 的使用必须依赖于 Spring Data 的内容。
本章将基于 Spring Data以及其 JPA 部分进行数据库访问,针对Spring Data 以及 JPA 进行详细的说明与讲解, 本文将包含以下内容:
- Spring Data 功能特点说明
- Spring Data JPA 功能特点说明
- Spring Data JPA 与 Spring Boot 整合
- Spring Data JPA 基本使用
- Spring Data JPA 实体详解
- Spring Data JPA JPQL
- 参考
Spring Data 功能
类接口介绍
由于Spring Data JPA 是 Spring Data 的一份子,所以基础功能含在Spring Data 包中。不过Spring Data JPA 也有一个具体的扩展包。以下是Spring Data 核心包和 JPA 的具体位置以及类结构:

其中最常用的就是以下几个类:
Repository: 此类是一个空的接口,是最顶层的一个接口(Spring Data中心接口),其没有任何方法和操作,目的就是为了统一所有的Repository类型CrudRepository: 此接口是Repository的子接口,其从名字就可以看出来 此接口提供的是 CURD(增删改查) 操作PagingAndSortingRepository: 此接口是CrudRepository的子接口,提供了分页和排序的功能JpaRepository: 此接口是PagingAndSortingRepository的子接口,此接口和以上三个接口没有在同一个包中,此接口在org.springframework.data.jpa.repository(Spring Data JPA)包中,以上三个都是存在org.springframework.data.repository(Spring Data)包中
通过以上的几个类,大致已经满足所有常用的基础数据库操作,不过有时候由于需求,需要使用到动态SQL等操作时,以上几个类 就不能满足需要了,需要引入下面这个接口
JpaSpecificationExecutor: 这个接口主要就是用来做复杂查询,和以上几个接口不存在任何关系,是一个单独存在的接口
以上只是介绍了几个通用的操作接口,其还有更加具体的持久化技术抽象接口,比如:JpaRepository, MongoRepository,RedisRepository等等,针对不同的数据库存在的不同的抽象化技术
方法定义
Spring Data 提供了从方法名映射出特定的查询语句。通过对方法名的解析,支持按照规范的方法名查询。直接给定合乎规范的方法名,系统会根据方法名生成正确的查询等操作实现,不需要对方法进行实现,会自动生成需要的代码
在自定义方法时,必须按照一定的规则才可以正确解析,其中有一些特定的单词发挥着特定的功能,也许它对最后生成具体代码没有具体的作用,但可以让代码更容易阅读。以下列出用于查询的特定的前缀以及连接词 :
查询: find...By,read...By,query...By,count...By,get...By
连接: And, Or
自定义方法必须符合以下规则:
查询前缀 + 全局修饰 + 实体属性名称 + 限定词 + 连接词 + ... + OrderBy + 排序属性 + 排序方向
List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);
List<Person> findByLastnameIgnoreCase(String lastname);
我对以上方法根据规则进行了以下划分:
List<Person> findBy EmailAddress And Lastname(EmailAddress emailAddress, String lastname);
List<Person> find Distinct People By Lastname Or Firstname(String lastname, String firstname);
List<Person> find People Distinct By Lastname Or Firstname(String lastname, String firstname);
List<Person> find By Lastname IgnoreCase(String lastname);
下面给出更加全面的关键词列表:
| 全局修饰 | 关键词 | 排序方向 | 连接词 | 限定词 |
|---|---|---|---|---|
| Distinct | isNull,isNotNull,like,notLike,Containing, in,notIn,ignoreCase, between, equals,Lessthan,graterThan,after,Before | asc, desc | and , or | first(fitst+number),top(top + number) |
更加详细的关键词表通过连接可以查看Query creation Keyword
根据以上内容,可以实现大部分的功能了,不过 Spring Data还给出了更加全面的功能。下面进行一一列举:
- 嵌套方法命名规则
针对多级嵌套,提供了另外种更加直观的查询方式。通过下划线分割更加清晰明了
public User{
private String name;
private Address address;
}
public Address{
private String name;
private int code;
}
针对需要通过 code 来查询用户的操作,如果按照以上已经给出的方式我们可能会实现出如下的方法名
List<User> find By Address Code(int Code)
但这样并不直观,通过嵌套方法命名规则,可以通过一下方式实现
List<User> find By Address_Code(int Code)
分页排序处理
JPA 针对排序和分页提供了两种方式处理,一种是直接通过定义方法名实现,一种是通过传递参数实现- 通过方法名添加限定词来映射实现
List<User> findByLastnameOrderByAsc(String lastname) List<User> findTop2ByLastnameOrderByAsc(String lastname) User findFirstByOrderByLastnameAsc(); User findTopByOrderByAgeDesc(); Page<User> queryFirst10ByLastname(String lastname, Pageablepageable); 通过参数实现
Page<User> findByLastname(String lastname, Pageable pageable); Slice<User> findByLastname(String lastname, Pageable pageable); List<User> findByLastname(String lastname, Sort sort); List<User> findByLastname(String lastname, Pageable pageable);此处我个人还是推荐使用 参数的形式实现,此种形式更加方便,不需要写太复杂的方法名,降低复杂度,可控度更改
- 通过方法名添加限定词来映射实现
流式查询
可以通过Java8提供的Steam<T>来实现结果返回,此处并不是简单的将结果包装在Stream中,而是通过特定的方式执行@Query("select u from User u") Stream<User> findAllByCustomQueryAndStream(); Stream<User> readAllByFirstnameNotNull(); @Query("select u from User u") Stream<User> streamAllPaged(Pageable pageable);注意: 在使用流时,需要注意以下两点
Stream在使用之后需要关闭的,可以通过str-with-resources block的方式关闭或者close()方法来关闭。try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) { stream.forEach(…); }- 并不是所有的数据模型都支持
Stream<T>模式的
异步查询
同时支持异步查询。@Async Future<User> findByFirstname(String firstname); @Async CompletableFuture<User> findOneByFirstname(String firstname); @Async ListenableFuture<User> findOneByLastname(String lastname);
以上的内容,如果需要更加详细的了解,可以通过访问官方文档地址查询Defining query methods
Spring Data 配置以及部分原理
虽然通过一下简单的集成可以直接使用 JPA , 不过由于我们使用的是 Spring Boot ,其自动化的已经帮我们实现了很多配置,但是自动化的配置并不能完全的满足我们的需求,有时候我们还是需要针对其进行自动化的配置。很好的理解Spring Data如何配置以及如何实现能更加的帮助我们使用Spring Data(JPA)。
Spring Data 配置
每一个Spring Data模块都包含repositories元素能够让你简单的基于base-package定义来进行Spring扫描。我们可以通过 XML 以及 Java 两种配置的形式来配置。
xml 配置
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> <repositories base-package="com.acme.repositories"> <context:exclude-filter type="regex" expression=".*SomeRepository" /> </repositories> </beans:beans>java 配置
@Configuration @EnableJpaRepositories("com.acme.repositories") class ApplicationConfiguration { @Bean public EntityManagerFactory entityManagerFactory() { // … } }通过
@Enable${store}Repositories注解来开启不同的支持,比如@EnableJpaRepositories用来开JPA支持 ,EnableMongoRepositories来开启Mongodb的支持
通过以上方式可以针对 Spring Data 进行自定义化的配置,针对不同的需求进行不同的配置。比如一个项目中同时使用多种数据库,(Spring Data 针对每个模块有单独的配置实现),可以通过针对性的配置实现不同的支持。同时还可以通过过滤等配置针对对应的模块过滤性的加载 registory
Spring Data Registroy
Spring Data 使用通过接口化实现,项目在运行时通过接口自动化生成对应的实现类,通过Spring 的bean管理机制,生成与类名相同的key(类名的第一个首字母小写)对bean进行对应存储。利用工厂方法通过类来获取对应的实例
RepositoryFactorySupport factory = … // Instantiate factory here
UserRepository repository = factory.getRepository(UserRepository.class);
通过以上方式,可以获取系统自动生成的对应的接口实例。 不过有时候,针对一些不同的需求要求,我们并不能很好的通过Spring Data 的方法名映射出合适的方法实现,此时需要我们自己实现Spring Data repositories,此处就需要自定义实现
- 定义接口定义方法
interface CustomizedUserRepository { void someCustomMethod(User user); } 自定义方法实现
class CustomizedUserRepositoryImpl implements CustomizedUserRepository { public void someCustomMethod(User user) { // Your custom implementation } }类名需要按照
{接口名}Impl来实现哦将其扩展到
repository interfaceinterface UserRepository extends CrudRepository<User, Long>, CustomizedUserRepository { // Declare query methods here }通过以上方式实现,即实现了自定义的方法实现,系统也不会再聪明的自动生成对应的方法实现
以上给出了Spring Data 的简单介绍,如果需要更加详细的学习请查询官方文档官方文档
Spring Data JPA 功能特点说明
Spring Data 每一个模块都有其单独的namespace,
通过namespace可以获得去支持的模块。通过单独的配置可以支持一些JPA独有的配置属性
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<jpa:repositories base-package="com.acme.repositories" />
</beans>
通过以上简单的方式就可以实现JPA 的配置,不过,虽然以上XML只有一句配置项,不过默认自动的添加了两项默认配置
Spring Data JPA 与 Spring Boot 整合
引入需要的jar包
本次采用的数据库是 MySQL, 所以也需要引入MySQL驱动包<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>JPA 配置说明
spring.jpa.database= spring.jpa.database-platform= spring.jpa.generate-ddl= spring.jpa.hibernate.ddl-auto= spring.jpa.hibernate.naming-strategy= spring.jpa.hibernate.naming.physical-strategy= spring.jpa.hibernate.naming.strategy= spring.jpa.hibernate.use-new-id-generator-mappings= spring.jpa.open-in-view= spring.jpa.properties= spring.jpa.show-sql= spring.data.jpa.repositories.enabled=以上是Spring Boot 默认支持关于JPA 的设置,其中会发现包含hibernate的配置部分,说明Spring Data JPA 和 hibernate是有关联的。 其中在使用的时候,并不需要全部配置,只需要配置关键部分就可以
- Spring Boot 集成配置
Spring Data JPA 基本使用
正常情况下,通过Spring 来进行Spring Data JAP 操作,一般是需要4步的, 不过如果通过Spring Boot 来进行Spring Data JPA操作,可以将其缩减为2步就可轻松完成。接下来是基于Spring Boot 的实现,如果需要了解具体的Spring 集成操作,那么可以通过一下地址访问官方文档查看 Spring Data JPA 集成
提供一个接口类并扩展自
Repository接口或者其子接口(就是以上说名的几个CrudRepository,PagingAndSortingRepository以及JpaRepository)并提供其需要处理的实体类和ID 类型public interface UserRepository extends CrudRepository<User,Long> { }在接口中声明查询方法
List<User> findByLastname(String lastname); List<User> findByAddress(String address);强烈建议使用IDEA 集成环境来开发,其对Spring 以及其他支持堪称完美,此处在定义查询方法时会给出一定的提示
调用
此处调用,可以直接在Controller层调用,也可以在Service层调用,具体需要根据自己项目架构需求来进行调用实现@Autowired private UserRepository userRepository; @GetMapping("/get_user_by_address/{address}") public ResponseEntity getUserByAddress(@PathVariable String address) { return new ResponseEntity(userRepository.findByAddress(address), HttpStatus.OK); }
详细说明以及疑点解惑
通过以上方式,可以满足简单的数据库操作。不过其中有几点需要注意或者迷惑的地方,接下来将逐点进行说明:
- 提供一个接口类并扩展自
Repository接口
本步并不是必须的,如果系统提供的几个接口以及其中提供的方法完全满足项目需求,那么可以不需要提供单独的接口类,不过为了项目的可维护性等,还是建议提供单独的接口类 - 为什么就提供一个接口就可以实现功能
虽然开发阶段不需要提供具体的代码实现,不过在项目运行阶段,并不是直有接口,而是系统会根据接口信息,自动生成一些模板代码,系统自动生成了接口的实现。不需要开发人员去编写这些具有模板性的代码,大大提高了开发效率 自定义的操作方法系统如何处理
Spring Data JPA 支持自定义操作方法,但并不是随便来写的,Spring Data JPA针对方法名有一些限定说明。只有遵循此规定的,系统才会生成正确的实现代码。本节会有部分内容专门讲解此处的实现。如何实现正确的方法定义需要根据此部分内容来详细了解
此处推荐IDEA集成环境开发,它会按照规范给定提示,可以书写标准的自定义方法如果不需要暴露部分操作接口该怎么办?比如删除操作等
此种需要可以通过自定义接口,比如不需要删除操作,但是又需要排序等操作,可以通过将CrudRepository中了曾,改,查操作的方法复制到自定义的接口中,然后将PagingAndSortingRepository将其中排序的方法复制到自定义的接口中。当然,如果需要其他的操作,那么可以通过自定义接口类实现@NoRepositoryBean interface MyBaseRepository<T, ID extends Serializable> extends Repository<T, ID> { Optional<T> findById(ID id); <S extends T> S save(S entity); } public interface ProductRepository extends MyBaseRepository<Product, Long> { }此内容可以在Spring Data JPA 官方文档找到Defining repository interfaces
通过以上方式实现的,虽然看似使用的是一个自定的接口类实现的,不过内部还是会映射到CrudRepository接口上,只不过没有提供那么多接口以供开发调用(此内容没有经过验证,根据文档介绍是如何)