天天看點

第9章 Spring Boot整合JPA 與 JpaRepository 基礎方法介紹

參考文章:

第1章 Spring Boot到底是什麼?_陳小房的部落格

第2章 第一個Spring Boot項目_陳小房的部落格

第3章 Spring Boot項目配置詳解_陳小房的部落格

第4章 Spring Boot的Web應用開發入門_陳小房的部落格

第5章 Spring Boot模闆Thymeleaf入門詳解_陳小房的部落格

第6章 MyBatis架構入門詳解(1)_陳小房的部落格

第7章 MyBatis架構入門詳解(2)_陳小房的部落格

第8章 使用注解的方式整合MyBatis 入門詳解_陳小房的部落格

目錄

​​1. 什麼是JPA​​

​​1.1 JPA和MyBatis的關系​​

​​2. Spring Data JPA​​

​​2.1 配置SpringData JPA​​

​​3. 驗證Spring Data JPA​​

​​ 4. JpaRepository類定義​​

​​5. JpaRepository基礎方法​​

​​5.1 新增資料​​

​​save​​

​​saveAll​​

​​5. 2 更新資料庫方法​​

​​5.3 查找資料​​

​​findById​​

​​findAll​​

​​findAllById​​

​​existsById​​

​​ findAll(Sort sort)​​

​​findAll(Pageable pageable)​​

​​count​​

​​5.4 資料删除​​

​​deleteById​​

​​deleteAll()​​

1. 什麼是JPA

   JPA(Java Persistence API, Java 持久化API)是SUN公司提出的Java持久化規範,它提供了一種對象/關系映射的管理工具來管理Java中的關系型資料庫。JPA的主要目的是簡化現有的持久化開發工作并且整合ORM架構,JPA本身并不是ORM架構,它是一種規範,這種規範可以私下通過注解或者XML描述“對象-關系表”之間的映射關系,并将實體對象持久化到資料庫中,進而極大地簡化現有的持久化開發工作。

1.1 JPA和MyBatis的關系

    實作JPA的架構有Hibernate, TopLink, 而我們前面介紹的MyBatis不屬于實作JPA的架構,主要差別有:

  1. Mybatis是對象和結果集的映射,而JPA規範強調的是對象和關系表之間的映射。
  2. 遵守JPA規範的架構具有良好的移植性,不用關心用什麼資料庫,而Mybatis架構在更改資料庫時需要修改底層SQL。

2. Spring Data JPA

2.1 配置SpringData JPA

    Spring Data 是Spring 的一個子項目, 旨在統一和簡化對各類型持久化存儲, 而不拘泥于是關系型資料庫還是NoSQL 資料存儲。

   Spring Data JPA是Spring Data項目的一部分,它是在ORM架構思想、JPA規範的基礎上封裝的一套JPA應用架構。使用Spring Data JPA隻需要繼承和擴充Spring Data 中統一的資料通路接口Repository接口無需編寫SQL實作資料庫通路。

在pom.xml檔案中添加spring-boot-starter-data-jpa依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>      

在資料庫中建立一個schema用于JPA測試

第9章 Spring Boot整合JPA 與 JpaRepository 基礎方法介紹

 然後在application.properties檔案中進行相關配置

spring.datasource.url=jdbc:mysql://localhost:3306/jpa_demo?useSSL=true&useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=itJMF-4RObQ2
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

#JPA 配置
spring.jpa.hibernate.ddl-auto=update
#spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true      

 這裡對JPA配置進行簡單說明

 spring.jpa.hibernate.ddl-auto

該配置比較常用,配置實體類維護資料庫表結構的具體行為。它主要有4個選項

  1. create: 啟動時删資料庫中的表,然後建立新表,退出時不删除資料表
  2. create-drop: 啟動時删資料庫中的表,然後建立,退出時删除資料表 如果表不存在則報錯
  3. update: 如果啟動時表格式不一緻則更新表,原有資料保留
  4. validate: 項目啟動表結構進行校驗 如果不一緻則報錯”

這裡我們選擇了update

spring.jpa.show-sql=true 操作時在控制台列印真實的SQL語句,便于調試

spring.jpa.properties.hibernate.format_sql=true 以JSON格式列印輸出SQL語句友善檢視

3. 驗證Spring Data JPA

首先,建立Scenic實體類,它是一個實體類,按照JPA的設計思想,這個實體類也是定義資料庫中的表結構的類,示例代碼如下

@Entity
@Table(name = "scenics")
public class Scenic {
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
    private int scenicId;

    private int cityId;

    //@Column(name = "scenicName")
    @Column(length = 64)

    private String scenicName;

    private int price;


    public Scenic() {

    }

    public Scenic(Integer cityId, String scenicName, int price ){
        this.cityId = cityId;
        this.scenicName = scenicName;
        this.price = price;
    }

    public int getScenicId() {
        return scenicId;
    }

    public void setScenicId(int scenicId) {
        this.scenicId = scenicId;
    }

    public int getCityId() {
        return cityId;
    }

    public void setCityId(int cityId) {
        this.cityId = cityId;
    }

    public String getScenicName() {
        return scenicName;
    }

    public void setScenicName(String scenicName) {
        this.scenicName = scenicName;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }
}      

 注意這個實體類中使用了JPA相關的注解

@Entity 這個注解是使用JPA時必須的注解,代表這個類對應了一個資料庫表

@Table 這是一個可選的注解,用于說明資料庫實體對應的表資訊,包括表的名稱、索引資訊等,如果沒有則表名和實體類的名稱一緻,一般情況下我們不使用該注解,這裡為了示範,我們使用該注解将表名申明為Scenics

@Id 代表對應的主鍵

@GeneratedValue 設定資料庫主鍵自動生成規則。strategy屬性提供4種選項

AUTO:主鍵由程式控制,是預設選項。

IDENTITY:主鍵由資料庫自動生成,即采用資料庫ID自增長的方式,注意Oracle不支援這種方式。

SEQUENCE:通過資料庫的序列産生主鍵,通過@SequenceGenerator注解指定序列名,注意MySQL不支援這種方式。

TABLE:通過特定的資料庫表産生主鍵,使用該政策可以使應用更易于資料庫移植。

@Column注解:聲明實體屬性的表字段的定義。預設的實體每個屬性都對應表的一個字段,字段名預設與屬性名保持一緻。字段的類型根據實體屬性類型自動對應。這裡主要聲明了字元字段的長度length,如果不聲明,則系統會采用255作為該字段的長度。

這裡需要注意JPA自動建表的字段命名規則采用的是下劃線,如屬性scenicName會将生成資料庫字段scenic_name,如果需要生成非下劃線字段,可以采用@Column(name = "ScenicName")

運作測試

運作Spring boot應用,控制台輸出

第9章 Spring Boot整合JPA 與 JpaRepository 基礎方法介紹

  檢視資料庫,可以看到已經自動建立了scenics資料庫表

第9章 Spring Boot整合JPA 與 JpaRepository 基礎方法介紹

 4. JpaRepository類定義

  接下來學習如何通過類來操作資料庫,在Spring Data JPA中使用JpaRepository接口類完成對資料庫的操作。JpaRepository是Spring Data JPA中最重要的類之一。

 這是JpaRepository接口的定義

@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T>      

 可以看出JpaRepository繼承了接口PagingAndSortingRepository和QueryByExampleExecutor。而PagingAndSortingRepository又繼承CrudRepository。

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {      

 是以JpaRepository接口同時擁有了基本CRUD功能以及分頁功能。

 JpaRepository提供了30多個預設方法,基本能滿足項目中的資料庫操作功能。當我們需要定義自己的Repository接口的時候,我們可以直接繼承JpaRepository,進而獲得SpringBoot Data JPA為我們内置的多種基本資料操作方法。

5. JpaRepository基礎方法

下面對主要的方法進行測試

在項目目錄下建立repository包,添加接口檔案ScenicRepository

第9章 Spring Boot整合JPA 與 JpaRepository 基礎方法介紹

 代碼如下 

@Repository
public interface ScenicRepository extends JpaRepository<Scenic, Integer> {
}      

 在測試用例類Chapter9ApplicationTests中添加測試方法進行測試

5.1 新增資料

save

JPARepository接口提供save方法用于新增資料,它的使用很簡單,隻要傳入需要新增的實體類即可

@Resource
    private ScenicRepository scenicRepository;

    @Test
    public void testSave() {

        Scenic scenic = new Scenic(1,  "scenic1", 10);
        scenicRepository.save(scenic);
    }      

 執行結果

第9章 Spring Boot整合JPA 與 JpaRepository 基礎方法介紹

 saveAll

如果需要批量新增則可以使用saveAll方法

@Test
    public void testSaveAll() {
        List<Scenic> scenicList = new ArrayList<>();
        scenicList.add(new Scenic(11, "savaAll_11", 100));
        scenicList.add(new Scenic(12, "saveAll_12", 200));
        scenicList.add(new Scenic(13, "saveAll_13", 300));
        scenicRepository.saveAll(scenicList);
    }      

 執行結果

第9章 Spring Boot整合JPA 與 JpaRepository 基礎方法介紹

 5. 2 更新資料庫方法

 更新已有資料庫的方法也是save  

@Test
    public void testUpdate() {
        Scenic scenic = scenicRepository.findById(1).get();
        scenic.setScenicName("scenicUpdate");
        scenicRepository.save(scenic);
    }      

  執行結果

第9章 Spring Boot整合JPA 與 JpaRepository 基礎方法介紹

5.3 查找資料

findById

可以根據id查詢對應的實體,函數原型如下

/**
     * 根據id查詢對應的實體。
     */
    Optional<T> findById(ID id);      

 測試代碼

@Test
    public void testFindById() {
        Scenic scenic = scenicRepository.findById(2).get();
        System.out.println(scenic.getScenicName() + ":" + scenic.getScenicId());
    }      

 執行輸出 

第9章 Spring Boot整合JPA 與 JpaRepository 基礎方法介紹

findAll

查詢所有的實體。方法原型

Iterable<T> findAll();

測試代碼

@Test
    public void testFindAll() {
        List<Scenic> scenicList = scenicRepository.findAll();
        for(Scenic scenic: scenicList) {
            System.out.println( scenic.getScenicName()+ ":" + scenic.getScenicId() + ":" + scenic.getPrice());
        }
    }      

 測試結果

第9章 Spring Boot整合JPA 與 JpaRepository 基礎方法介紹

findAllById

如果不想查詢全部資料,但是需要根據給定的Id查詢部分資料,可以使用findAllById

@Test
    public void testFindAllById() {
        List<Integer> idList = new ArrayList<>();
        idList.add(2);
        idList.add(3);

        List<Scenic> scenicList = scenicRepository.findAllById(idList);
        for (Scenic scenic : scenicList) {
            System.out.println(scenic.getScenicName() + ":" + scenic.getScenicId() + ":" + scenic.getPrice());
        }
    }      

 測試結果

第9章 Spring Boot整合JPA 與 JpaRepository 基礎方法介紹

existsById

有時候我們并不想查找資料,而是隻想知道資料是否存在與資料庫表中,這個時候可以用existsById

boolean existsById(ID id);

測試代碼

@Test
    public void testExistsById() {
        System.out.println(scenicRepository.existsById(1));
    }      

 測試結果

第9章 Spring Boot整合JPA 與 JpaRepository 基礎方法介紹

 findAll(Sort sort)

有時候需要對查詢到的資料進行排序,此時可以傳遞一個排序函數。例如我們對secenic查詢結果按照price降序排列

@Test
    public void testFindAllSort() {
        Sort sort = Sort.by(Sort.Direction.DESC, "price");
        List<Scenic> scenicList = scenicRepository.findAll(sort);

        for(Scenic scenic: scenicList) {
            System.out.println( scenic.getScenicName()+ ":" + scenic.getScenicId() + ":" + scenic.getPrice());
        }
    }      

執行結果

第9章 Spring Boot整合JPA 與 JpaRepository 基礎方法介紹

findAll(Pageable pageable)

傳回一頁實體,根據Pageable參數提供的規則進行過濾

@Test
    public void testFindAllPageable() {

        // 分頁查詢
        // PageRequest.of 的第一個參數表示第幾頁(注意:第一頁從序号0開始),第二個參數表示每頁的大小
        // PageRequest of(int page, int size)
        Pageable pageable = PageRequest.of(1, 2);

        Page<Scenic> page = scenicRepository.findAll(pageable);
        System.out.println("總頁數:" + page.getTotalPages());
        System.out.println("總記錄數:" + page.getTotalElements());
        System.out.println("目前第幾頁:" + (page.getNumber() + 1));
        System.out.println("目前頁面的記錄數:" + page.getNumberOfElements());

    }      

 測試結果

第9章 Spring Boot整合JPA 與 JpaRepository 基礎方法介紹
第9章 Spring Boot整合JPA 與 JpaRepository 基礎方法介紹

  count

 和SQL指令一樣,用于統計資料元素個數

編寫測試代碼 

@Test
    public void  testCount() {
        Long cnt = scenicRepository.count();
        System.out.println("元素個數: "+cnt);
    }      

 運作結果

第9章 Spring Boot整合JPA 與 JpaRepository 基礎方法介紹

 5.4 資料删除

deleteById

根據id删除對應的資料

@Test
    public void testDeleteById() {
        System.out.println("*************************");
        testFindAll();
        scenicRepository.deleteById(1);
        System.out.println("*************************");
        testFindAll();
        System.out.println("*************************");
    }      

 删除前

第9章 Spring Boot整合JPA 與 JpaRepository 基礎方法介紹

 删除後

第9章 Spring Boot整合JPA 與 JpaRepository 基礎方法介紹

deleteAll()

deleteAll可以删除對應資料庫表中的所有資料

@Test
    public void testDeleteAll() {
        scenicRepository.deleteAll();
    }