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 进行详细的说明与讲解, 本文将包含以下内容:

其中最常用的就是以下几个类:

  • 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 interface

    interface UserRepository extends CrudRepository<User, Long>, CustomizedUserRepository {
    
    // Declare query methods here
    }
    

    通过以上方式实现,即实现了自定义的方法实现,系统也不会再聪明的自动生成对应的方法实现

以上给出了Spring Data 的简单介绍,如果需要更加详细的学习请查询官方文档官方文档

Spring Data JPA 功能特点说明

Spring Data 每一个模块都有其单独的namespace, 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 整合

  1. 引入需要的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>
    
  2. 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是有关联的。 其中在使用的时候,并不需要全部配置,只需要配置关键部分就可以

  3. Spring Boot 集成配置

Spring Data JPA 基本使用

正常情况下,通过Spring 来进行Spring Data JAP 操作,一般是需要4步的, 不过如果通过Spring Boot 来进行Spring Data JPA操作,可以将其缩减为2步就可轻松完成。接下来是基于Spring Boot 的实现,如果需要了解具体的Spring 集成操作,那么可以通过一下地址访问官方文档查看 Spring Data JPA 集成

  1. 提供一个接口类并扩展自Repository接口或者其子接口(就是以上说名的几个CrudRepository,PagingAndSortingRepository以及JpaRepository)并提供其需要处理的实体类和ID 类型

     public interface UserRepository extends CrudRepository<User,Long> {
    
     }
    
  2. 在接口中声明查询方法

         List<User> findByLastname(String lastname);
         List<User> findByAddress(String address);
    

    强烈建议使用IDEA 集成环境来开发,其对Spring 以及其他支持堪称完美,此处在定义查询方法时会给出一定的提示

  3. 调用
    此处调用,可以直接在 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);
         }
    

详细说明以及疑点解惑

通过以上方式,可以满足简单的数据库操作。不过其中有几点需要注意或者迷惑的地方,接下来将逐点进行说明:

  1. 提供一个接口类并扩展自Repository接口
    本步并不是必须的,如果系统提供的几个接口以及其中提供的方法完全满足项目需求,那么可以不需要提供单独的接口类,不过为了项目的可维护性等,还是建议提供单独的接口类
  2. 为什么就提供一个接口就可以实现功能
    虽然开发阶段不需要提供具体的代码实现,不过在项目运行阶段,并不是直有接口,而是系统会根据接口信息,自动生成一些模板代码,系统自动生成了接口的实现。不需要开发人员去编写这些具有模板性的代码,大大提高了开发效率
  3. 自定义的操作方法系统如何处理
    Spring Data JPA 支持自定义操作方法,但并不是随便来写的,Spring Data JPA针对方法名有一些限定说明。只有遵循此规定的,系统才会生成正确的实现代码。本节会有部分内容专门讲解此处的实现。如何实现正确的方法定义需要根据此部分内容来详细了解
    此处推荐IDEA集成环境开发,它会按照规范给定提示,可以书写标准的自定义方法

  4. 如果不需要暴露部分操作接口该怎么办?比如删除操作等
    此种需要可以通过自定义接口,比如不需要删除操作,但是又需要排序等操作,可以通过将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接口上,只不过没有提供那么多接口以供开发调用(此内容没有经过验证,根据文档介绍是如何)

Spring Data JPA 实体详解

Spring Data JPA JPQL

参考

Copyright © 抓🐱的🐟.com 2017 all right reserved,powered by Gitbook该文件修订时间: 2020-03-13 07:05:40

results matching ""

    No results matching ""