Spring Boot JPA 快速指南



Spring Boot JPA - 概述

什麼是JPA?

Java Persistence API (Java 持久化 API) 是一組類和方法,用於將大量資料持久儲存到資料庫中,由 Oracle 公司提供。

在哪裡使用JPA?

為了減少編寫關係物件管理程式碼的負擔,程式設計師遵循“JPA 提供程式”框架,該框架允許輕鬆與資料庫例項互動。此處,JPA 接管了所需的框架。

JPA

JPA 歷史

早期版本的 EJB 將持久層與業務邏輯層結合在一起,使用 javax.ejb.EntityBean 介面。

  • 在引入 EJB 3.0 時,持久層被分離並指定為 JPA 1.0 (Java 持久化 API)。此 API 的規範於 2006 年 5 月 11 日與 JAVA EE5 的規範一起釋出,使用 JSR 220。

  • JPA 2.0 於 2009 年 12 月 10 日作為 Java 社群程序 JSR 317 的一部分與 JAVA EE6 的規範一起釋出。

  • JPA 2.1 於 2013 年 4 月 22 日使用 JSR 338 與 JAVA EE7 的規範一起釋出。

JPA 提供程式

JPA 是一個開源 API,因此 Oracle、Redhat、Eclipse 等各種企業供應商透過在其產品中新增 JPA 永續性功能來提供新產品。其中一些產品包括:

Hibernate、Eclipselink、Toplink、Spring Data JPA 等。

Spring Boot JPA - 環境搭建

本章將指導您如何準備開發環境以開始使用 Spring Boot 框架的工作。它還將教您如何在設定 Spring Boot 框架之前在您的機器上設定 JDK 和 Eclipse:

步驟 1 - 設定 Java 開發工具包 (JDK)

Java SE 可免費下載。要下載,請點選此處,請下載與您的作業系統相容的版本。

按照說明下載 Java,然後執行**.exe**檔案以在您的機器上安裝 Java。在您的機器上安裝 Java 後,您需要設定環境變數以指向正確的安裝目錄。

為 Windows 2000/XP 設定路徑

假設您已將 Java 安裝在 c:\Program Files\java\jdk 目錄中:

  • 右鍵單擊“我的電腦”,然後選擇“屬性”。

  • 在“高階”選項卡下單擊“環境變數”按鈕。

  • 現在,編輯“Path”變數,並在其末尾新增 Java 可執行檔案目錄的路徑。例如,如果路徑當前設定為**C:\Windows\System32**,則按如下方式進行編輯:

    C:\Windows\System32;c:\Program Files\java\jdk\bin.

為 Windows 95/98/ME 設定路徑

假設您已將 Java 安裝在 c:\Program Files\java\jdk 目錄中:

  • 編輯“C:\autoexec.bat”檔案,並在末尾新增以下行:

    SET PATH=%PATH%;C:\Program Files\java\jdk\bin

為 Linux、UNIX、Solaris、FreeBSD 設定路徑

應將環境變數 PATH 設定為指向 Java 二進位制檔案安裝的位置。如果您遇到問題,請參閱您的 shell 文件。

例如,如果您使用 bash 作為 shell,則您將在**.bashrc**檔案的末尾新增以下行:

    export PATH=/path/to/java:$PATH'

或者,如果您使用整合開發環境 (IDE),例如 Borland JBuilder、Eclipse、IntelliJ IDEA 或 Sun ONE Studio,則您將必須編譯並執行一個簡單的程式以確認 IDE 知道您已將 Java 安裝在何處。否則,您將必須按照 IDE 文件中給出的說明進行正確的設定。

步驟 2 - 設定 Eclipse IDE

本教程中的所有示例均使用 Eclipse IDE 編寫。因此,我們建議您在您的機器上安裝最新版本的 Eclipse。

要安裝 Eclipse IDE,請從www.eclipse.org/downloads/下載最新的 Eclipse 二進位制檔案。下載安裝程式後,將二進位制分發版解壓縮到方便的位置。例如,在 Windows 上為 C:\eclipse,或在 Linux/Unix 上為 /usr/local/eclipse,最後適當地設定 PATH 變數。

可以透過在 Windows 機器上執行以下命令來啟動 Eclipse,或者您可以雙擊 eclipse.exe。

%C:\eclipse\eclipse.exe 

可以透過在 Unix (Solaris、Linux 等) 機器上執行以下命令來啟動 Eclipse:

$/usr/local/eclipse/eclipse

成功啟動後,如果一切正常,則應顯示以下結果:

Eclipse Home page

步驟 3 - 設定 m2eclipse

M2Eclipse 是 Eclipse 外掛,它非常有益於將 Apache Maven 整合到 Eclipse IDE 中。在本教程中,我們使用 Maven 來構建 Spring Boot 專案,並使用 m2eclipse 在 Eclipse 中執行示例。

使用 Eclipse IDE 中的“安裝新軟體”對話方塊安裝最新的 M2Eclipse 版本,並將其指向此 p2 儲存庫:

https://download.eclipse.org/technology/m2e/releases/latest/

步驟 3 - 設定 Spring Boot 專案

現在,如果一切正常,您可以繼續設定 Spring Boot。以下是將 Spring Boot 專案下載並安裝到您的機器上的簡單步驟。

  • 轉到 Spring Initializr 連結以建立 Spring Boot 專案,https://start.spring.io/

  • 選擇專案為**Maven 專案**。

  • 選擇語言為**Java**。

  • 選擇 Spring Boot 版本為**2.5.3**。

  • 設定專案元資料 - Group 為**com.tutorialspoint**,Artifact 為**springboot-h2**,名稱為**springboot-h2**,描述為**Spring Boot 和 H2 資料庫的演示專案**,包名稱為**com.tutorialspoint.springboot-h2**。

  • 選擇打包方式為**Jar**。

  • 選擇 Java 版本為**11**。

  • 新增依賴項為**Spring Web、Spring Data JPA、H2 資料庫和 Spring Boot DevTools**。

現在單擊“生成”按鈕以生成專案結構。

Spring Initializer

下載基於 Maven 的 Spring Boot 專案後,將 Maven 專案匯入 Eclipse,其餘工作將由 Eclipse 處理。它將下載 Maven 依賴項並構建專案,使其準備好進行進一步開發。

步驟 4 - 使用 POSTMAN 測試 REST API

POSTMAN 是一個用於測試基於 REST 的 API 的有用工具。要安裝 POSTMAN,請從www.postman.com/downloads/下載最新的 POSTMAN 二進位制檔案。下載可安裝檔案後,按照說明進行安裝和使用。

Spring Boot JPA - 架構

Java Persistence API 是將業務實體儲存為關係實體的來源。它演示瞭如何將普通舊 Java 物件 (POJO) 定義為實體,以及如何管理具有關係的實體。

類級別架構

下圖顯示了 JPA 的類級別架構。它顯示了 JPA 的核心類和介面。

JPA Class Level Architecture

下表描述了上述架構中顯示的每個單元。

序號 單元和描述
1

EntityManagerFactory

這是 EntityManager 的工廠類。它建立和管理多個 EntityManager 例項。

2

EntityManager

它是一個介面,它管理對物件的持久化操作。它像 Query 例項的工廠一樣工作。

3

Entity(實體)

實體是持久化物件,儲存為資料庫中的記錄。

4

EntityTransaction

它與 EntityManager 具有一對一的關係。對於每個 EntityManager,操作由 EntityTransaction 類維護。

5

Persistence

此類包含用於獲取 EntityManagerFactory 例項的靜態方法。

6

Query(查詢)

此介面由每個 JPA 供應商實現,以獲取滿足條件的關係物件。

上述類和介面用於將實體作為記錄儲存到資料庫中。它們透過減少程式設計師編寫將資料儲存到資料庫的程式碼的工作量來幫助程式設計師,以便他們可以專注於更重要的活動,例如編寫將類與資料庫表對映的程式碼。

JPA 類關係

在上圖架構中,類和介面之間的關係屬於 javax.persistence 包。下圖顯示了它們之間的關係。

JPA Class Relationships
  • EntityManagerFactory 和 EntityManager 之間的關係是**一對多**。它是一個 EntityManager 例項的工廠類。

  • EntityManager 和 EntityTransaction 之間的關係是**一對一**。對於每個 EntityManager 操作,都有一個 EntityTransaction 例項。

  • EntityManager 和 Query 之間的關係是**一對多**。可以使用一個 EntityManager 例項執行許多查詢。

  • EntityManager 和 Entity 之間的關係是**一對多**。一個 EntityManager 例項可以管理多個 Entity。

Spring Boot JPA vs Hibernate

JPA

JPA 是一種規範,它指定如何訪問、管理和持久化 Java 物件和關係資料庫之間的資訊/資料。它為 ORM(物件關係對映)提供了一種標準方法。

Hibernate

Hibernate 是 JPA 的一種實現。它提供了一個輕量級框架,並且是最常用的 ORM 工具之一。

JPA 與 Hibernate

下表總結了 JPA 和 Hibernate 之間的區別。

類別 JPA Hibernate
型別 JPA 是一種規範,它定義了使用 Java 物件管理關係資料庫資料的方式。 Hibernate 是 JPA 的一種實現。它是一個 ORM 工具,用於將 Java 物件持久化到關係資料庫中。
JPA 使用 javax.persistence 包。 Hibernate 使用 org.hibernate 包。
工廠 JPA 使用 EntityManagerFactory 介面來獲取實體管理器以持久化物件。 Hibernate 使用 SessionFactory 介面來建立會話物件,然後將其用於持久化物件。
CRUD 操作 JPA 使用 EntityManager 介面來建立/讀取/刪除操作並維護持久化上下文。 Hibernate 使用 Session 介面來建立/讀取/刪除操作並維護持久化上下文。
語言 JPA 使用 JPQL(Java 持久化查詢語言)作為面向物件的查詢語言來進行資料庫操作。 Hibernate 使用 HQL(Hibernate 查詢語言)作為面向物件的查詢語言來進行資料庫操作。

Spring Boot JPA - 應用搭建

如上一章環境搭建中所示,我們已將生成的 Spring Boot 專案匯入到 Eclipse 中。現在讓我們在**src/main/java**資料夾中建立以下結構。

Project Structure
  • **com.tutorialspoint.controller.EmployeeController** - 一個基於 REST 的控制器,用於實現基於 REST 的 API。

  • **com.tutorialspoint.entity.Employee** - 表示資料庫中相應表的實體類。

  • **com.tutorialspoint.repository.EmployeeRepository** - 一個儲存庫介面,用於實現資料庫上的 CRUD 操作。

  • **com.tutorialspoint.service.EmployeeService** - 一個服務類,用於在儲存庫函式上實現業務操作。

  • **com.tutorialspoint.springbooth2.SprintBootH2Application** - 一個 Spring Boot 應用類。

SprintBootH2Application 類已經存在。我們需要建立如下的包和相關的類和介面:

實體 - Entity.java

以下是 Employee 的預設程式碼。它表示一個具有 id、name、age 和 email 列的 Employee 表。

package com.tutorialspoint.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table
public class Employee {
   @Id
   @Column
   private int id;

   @Column
   private String name;

   @Column
   private int age;

   @Column
   private String email;

   public int getId() {
      return id;
   }
   public void setId(int id) {
      this.id = id;
   }
   public String getName() {
      return name;
   }
   public void setName(String name) {
      this.name = name;
   }
   public int getAge() {
      return age;
   }
   public void setAge(int age) {
      this.age = age;
   }
   public String getEmail() {
      return email;
   }
   public void setEmail(String email) {
      this.email = email;
   }
}

儲存庫 - EmployeeRepository.java

以下是儲存庫的預設程式碼,用於在上述實體 Employee 上實現 CRUD 操作。

package com.tutorialspoint.repository;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.tutorialspoint.entity.Employee;

@Repository
public interface EmployeeRepository extends CrudRepository<Employee, Integer>  {
}

服務 - EmployeeService.java

以下是 Service 的預設程式碼,用於實現對儲存庫函式的操作。

package com.tutorialspoint.service;

import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.tutorialspoint.entity.Employee;
import com.tutorialspoint.repository.EmployeeRepository;

@Service
public class EmployeeService {
   @Autowired
   EmployeeRepository repository;

   public Employee getEmployeeById(int id) {
      return repository.findById(id).get();
   }
   public List<Employee> getAllEmployees(){
      List<Employee> employees = new ArrayList<Employee>();
      repository.findAll().forEach(employee -> employees.add(employee));
      return employees;
   }
   public void saveOrUpdate(Employee employee) {
      repository.save(employee);
   }
   public void deleteEmployeeById(int id) {
      repository.deleteById(id);
   }
}

控制器 - EmployeeController.java

以下是控制器預設程式碼,用於實現 REST API。

package com.tutorialspoint.controller;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.tutorialspoint.entity.Employee;
import com.tutorialspoint.service.EmployeeService;

@RestController
@RequestMapping(path = "/emp")
public class EmployeeController {
   @Autowired
   EmployeeService employeeService;

   @GetMapping("/employees")
   public List<Employee> getAllEmployees(){
      return employeeService.getAllEmployees();
   }
   @GetMapping("/employee/{id}")
   public Employee getEmployee(@PathVariable("id") int id) {
      return employeeService.getEmployeeById(id);
   }
   @DeleteMapping("/employee/{id}")
   public void deleteEmployee(@PathVariable("id") int id) {
      employeeService.deleteEmployeeById(id);
   }
   @PostMapping("/employee")
   public void addEmployee(@RequestBody Employee employee) {
      employeeService.saveOrUpdate(employee);   
   }
   @PutMapping("/employee")
   public void updateEmployee(@RequestBody Employee employee) {
      employeeService.saveOrUpdate(employee);
   }	
}

應用程式 - SprintBootH2Application.java

以下是應用程式的更新程式碼,用於呼叫上述類。

package com.tutorialspoint.sprintbooth2;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@ComponentScan({"com.tutorialspoint.controller","com.tutorialspoint.service"})
@EntityScan("com.tutorialspoint.entity")
@EnableJpaRepositories("com.tutorialspoint.repository")
@SpringBootApplication
public class SprintBootH2Application {
   public static void main(String[] args) {
      SpringApplication.run(SprintBootH2Application.class, args);
   }
}

執行/除錯配置

在 Eclipse 中建立以下 **Maven 配置** 以使用目標 **spring-boot:run** 執行 Spring Boot 應用程式。此配置將有助於執行 REST API,我們可以使用 POSTMAN 對其進行測試。

Maven Configuration

執行應用程式

在 Eclipse 中,執行 **Employee Application** 配置。Eclipse 控制檯將顯示類似的輸出。

[INFO] Scanning for projects...
...
2021-07-24 20:51:14.823  INFO 9760 --- [restartedMain] c.t.s.SprintBootH2Application: 
Started SprintBootH2Application in 7.353 seconds (JVM running for 8.397)

伺服器啟動並執行後,使用 Postman 發出 POST 請求以首先新增記錄。

在 POSTMAN 中設定以下引數。

  • HTTP 方法 - **POST**

  • URL - **https://:8080/emp/employee**

  • 正文 - **一個員工 JSON 物件**

{  
   "id": "1",  
   "age": "35",  
   "name": "Julie",  
   "email": "julie@gmail.com"  
}   

單擊“傳送”按鈕並檢查響應狀態是否為 OK。現在發出 GET 請求以獲取所有記錄。

在 POSTMAN 中設定以下引數。

  • HTTP 方法 - **GET**

  • URL - **https://:8080/emp/employees**

單擊發送按鈕並驗證響應。

[{  
   "id": "1",  
   "age": "35",  
   "name": "Julie",  
   "email": "julie@gmail.com"  
}]   

Spring Boot JPA - 單元測試倉庫

要測試儲存庫,我們需要以下註解和類:

  • **@ExtendWith(SpringExtension.class)** - 使用 SpringExtension 類將類標記為使用 SpringExtension 類執行的測試用例。

  • **@SpringBootTest(classes = SprintBootH2Application.class)** - 配置 Spring Boot 應用程式。

  • **@Transactional** - 用於標記儲存庫以具有 CRUD 操作能力。

  • **@Autowired private EmployeeRepository employeeRepository** - 要測試的 EmployeeRepository 物件。

示例

以下是 EmployeeRepositoryTest 的完整程式碼。

package com.tutorialspoint.repository;

import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.ArrayList;
import java.util.List;
import javax.transaction.Transactional;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import com.tutorialspoint.entity.Employee;
import com.tutorialspoint.sprintbooth2.SprintBootH2Application;

@ExtendWith(SpringExtension.class)
@Transactional
@SpringBootTest(classes = SprintBootH2Application.class)
public class EmployeeRepositoryTest {
   @Autowired
   private EmployeeRepository employeeRepository;

   @Test
   public void testFindById() {
      Employee employee = getEmployee();	     
      employeeRepository.save(employee);
      Employee result = employeeRepository.findById(employee.getId()).get();
      assertEquals(employee.getId(), result.getId());	     
   }
   @Test
   public void testFindAll() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      List<Employee> result = new ArrayList<>();
      employeeRepository.findAll().forEach(e -> result.add(e));
      assertEquals(result.size(), 1);	     
   }
   @Test
   public void testSave() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      Employee found = employeeRepository.findById(employee.getId()).get();
      assertEquals(employee.getId(), found.getId());	     
   }
   @Test
   public void testDeleteById() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      employeeRepository.deleteById(employee.getId());
      List<Employee> result = new ArrayList<>();
      employeeRepository.findAll().forEach(e -> result.add(e));
      assertEquals(result.size(), 0);
   }
   private Employee getEmployee() {
      Employee employee = new Employee();
      employee.setId(1);
      employee.setName("Mahesh");
      employee.setAge(30);
      employee.setEmail("mahesh@test.com");
      return employee;
   }
}

執行測試用例

輸出

在 Eclipse 中右鍵單擊該檔案,然後選擇 **執行 JUnit 測試** 並驗證結果。

Repository Test Result

Spring Boot JPA - 儲存庫方法

現在讓我們分析一下我們在介面中建立的儲存庫中可用的方法。

儲存庫 - EmployeeRepository.java

以下是儲存庫的預設程式碼,用於在上述實體 Employee 上實現 CRUD 操作。

package com.tutorialspoint.repository;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.tutorialspoint.entity.Employee;

@Repository
public interface EmployeeRepository extends CrudRepository<Employee, Integer>  {
}

此儲存庫預設包含以下方法。

序號 方法和描述
1

count(): long

返回可用實體的數量。

2

delete(Employee entity): void

刪除一個實體。

3

deleteAll():void

刪除所有實體。

4

deleteAll(Iterable< extends Employee > entities):void

刪除作為引數傳遞的實體。

5

deleteAll(Iterable< extends Integer > ids):void

刪除使用作為引數傳遞的 ID 標識的實體。

6

existsById(Integer id):boolean

檢查實體是否存在,使用其 ID。

7

findAll():Iterable< Employee >

返回所有實體。

8

findAllByIds(Iterable< Integer > ids):Iterable< Employee >

返回使用作為引數傳遞的 ID 標識的所有實體。

9

findById(Integer id):Optional< Employee >

返回使用 ID 標識的實體。

10

save(Employee entity): Employee

儲存一個實體並返回更新後的實體。

11

saveAll(Iterable< Employee> entities): Iterable< Employee>

儲存所有傳遞的實體並返回更新後的實體。

Spring Boot JPA - 自定義方法

我們在 JPA 方法 章節中檢查了儲存庫中預設可用的方法。現在讓我們新增一個方法並對其進行測試。

儲存庫 - EmployeeRepository.java

示例

新增一個方法,用於按姓名查詢員工。

package com.tutorialspoint.repository;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.tutorialspoint.entity.Employee;

@Repository
public interface EmployeeRepository extends CrudRepository<Employee, Integer>  {
   public List<Employee> findByName(String name);	
   public List<Employee> findByAge(int age);
}

現在,Spring JPA 將自動建立上述方法的實現,因為我們遵循了基於屬性的命名法。讓我們透過在測試檔案中新增它們的測試用例來測試新增的方法。下面檔案的最後兩種方法測試了新增的自定義方法。

以下是 EmployeeRepositoryTest 的完整程式碼。

package com.tutorialspoint.repository;

import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.ArrayList;
import java.util.List;
import javax.transaction.Transactional;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import com.tutorialspoint.entity.Employee;
import com.tutorialspoint.sprintbooth2.SprintBootH2Application;

@ExtendWith(SpringExtension.class)
@Transactional
@SpringBootTest(classes = SprintBootH2Application.class)
public class EmployeeRepositoryTest {
   @Autowired
   private EmployeeRepository employeeRepository;
   @Test
   public void testFindById() {
      Employee employee = getEmployee();	     
      employeeRepository.save(employee);
      Employee result = employeeRepository.findById(employee.getId()).get();
      assertEquals(employee.getId(), result.getId());	     
   }
   @Test
   public void testFindAll() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      List<Employee> result = new ArrayList<>();
      employeeRepository.findAll().forEach(e -> result.add(e));
      assertEquals(result.size(), 1);	     
   }
   @Test
   public void testSave() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      Employee found = employeeRepository.findById(employee.getId()).get();
      assertEquals(employee.getId(), found.getId());	     
   }
   @Test
   public void testDeleteById() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      employeeRepository.deleteById(employee.getId());
      List<Employee> result = new ArrayList<>();
      employeeRepository.findAll().forEach(e -> result.add(e));
      assertEquals(result.size(), 0);
   }
   private Employee getEmployee() {
      Employee employee = new Employee();
      employee.setId(1);
      employee.setName("Mahesh");
      employee.setAge(30);
      employee.setEmail("mahesh@test.com");
      return employee;
   }
   @Test
   public void testFindByName() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      List<Employee> result = new ArrayList<>();
      employeeRepository.findByName(employee.getName()).forEach(e -> result.add(e));
      assertEquals(result.size(), 1);	     
   }
   @Test
   public void testFindByAge() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      List<Employee> result = new ArrayList<>();
      employeeRepository.findByAge(employee.getAge()).forEach(e -> result.add(e));
      assertEquals(result.size(), 1);	     
   }
}

執行測試用例

輸出

在 Eclipse 中右鍵單擊該檔案,然後選擇 **執行 JUnit 測試** 並驗證結果。

Repository Test Custom Methods

Spring Boot JPA - 命名查詢

有時會出現這種情況,我們需要一個自定義查詢來滿足一個測試用例。我們可以使用 @NamedQuery 註解在實體類中指定命名查詢,然後在儲存庫中宣告該方法。以下是一個示例。

我們在 JPA 自定義方法 章節中添加了自定義方法。現在讓我們使用 @NamedQuery 新增另一個方法並對其進行測試。

實體 - Entity.java

以下是 Employee 的預設程式碼。它表示一個具有 id、name、age 和 email 列的 Employee 表。

package com.tutorialspoint.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

@Entity
@Table
@NamedQuery(name = "Employee.findByEmail",
query = "select e from Employee e where e.email = ?1")
public class Employee {
   @Id
   @Column
   private int id;

   @Column
   private String name;

   @Column
   private int age;

   @Column
   private String email;

   public int getId() {
      return id;
   }
   public void setId(int id) {
      this.id = id;
   }
   public String getName() {
      return name;
   }
   public void setName(String name) {
      this.name = name;
   }
   public int getAge() {
      return age;
   }
   public void setAge(int age) {
      this.age = age;
   }
   public String getEmail() {
      return email;
   }
   public void setEmail(String email) {
      this.email = email;
   }
}

儲存庫 - EmployeeRepository.java

新增一個方法,用於按姓名和年齡查詢員工。

package com.tutorialspoint.repository;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.tutorialspoint.entity.Employee;

@Repository
public interface EmployeeRepository extends CrudRepository<Employee, Integer>  {
   public List<Employee> findByName(String name);	
   public List<Employee> findByAge(int age);
   public Employee findByEmail(String email);
}

現在,Spring JPA 將使用命名查詢中提供的查詢自動建立上述方法的實現。讓我們透過在測試檔案中新增它們的測試用例來測試新增的方法。下面檔案的最後兩種方法測試了新增的命名查詢方法。

以下是 EmployeeRepositoryTest 的完整程式碼。

package com.tutorialspoint.repository;

import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.ArrayList;
import java.util.List;
import javax.transaction.Transactional;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import com.tutorialspoint.entity.Employee;
import com.tutorialspoint.sprintbooth2.SprintBootH2Application;

@ExtendWith(SpringExtension.class)
@Transactional
@SpringBootTest(classes = SprintBootH2Application.class)
public class EmployeeRepositoryTest {
   @Autowired
   private EmployeeRepository employeeRepository;

   @Test
   public void testFindById() {
      Employee employee = getEmployee();	     
      employeeRepository.save(employee);
      Employee result = employeeRepository.findById(employee.getId()).get();
      assertEquals(employee.getId(), result.getId());	     
   }
   @Test
   public void testFindAll() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      List<Employee> result = new ArrayList<>();
      employeeRepository.findAll().forEach(e -> result.add(e));
      assertEquals(result.size(), 1);	     
   }
   @Test
   public void testSave() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      Employee found = employeeRepository.findById(employee.getId()).get();
      assertEquals(employee.getId(), found.getId());	     
   }
   @Test
   public void testDeleteById() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      employeeRepository.deleteById(employee.getId());
      List<Employee> result = new ArrayList<>();
      employeeRepository.findAll().forEach(e -> result.add(e));
      assertEquals(result.size(), 0);
   }
   private Employee getEmployee() {
      Employee employee = new Employee();
      employee.setId(1);
      employee.setName("Mahesh");
      employee.setAge(30);
      employee.setEmail("mahesh@test.com");
      return employee;
   }
   @Test
   public void testFindByName() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      List<Employee> result = new ArrayList<>();
      employeeRepository.findByName(employee.getName()).forEach(e -> result.add(e));
      assertEquals(result.size(), 1);	     
   }
   @Test
   public void testFindByAge() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      List<Employee> result = new ArrayList<>();
      employeeRepository.findByAge(employee.getAge()).forEach(e -> result.add(e));
      assertEquals(result.size(), 1);	     
   }
   @Test
   public void testFindByEmail() {	     
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      Employee result = employeeRepository.findByEmail(employee.getEmail());	     
      assertNotNull(result);	     
   }
}

執行測試用例

在 Eclipse 中右鍵單擊該檔案,然後選擇 **執行 JUnit 測試** 並驗證結果。

Repository Test Named Query method

Spring Boot JPA - 自定義查詢

有時會出現這種情況,我們需要一個自定義查詢來滿足一個測試用例。我們可以使用 @Query 註解在儲存庫中指定查詢。以下是一個示例。在這個示例中,我們使用 JPQL(Java 永續性查詢語言)。

我們在 JPA 命名查詢 章節中添加了命名查詢自定義方法。現在讓我們使用 @Query 新增另一個方法並對其進行測試。

儲存庫 - EmployeeRepository.java

新增一個方法,用於獲取按名稱排序的員工列表。

package com.tutorialspoint.repository;

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.tutorialspoint.entity.Employee;

@Repository
public interface EmployeeRepository extends CrudRepository<Employee, Integer>  {
   public List<Employee> findByName(String name);	
   public List<Employee> findByAge(int age);
   public Employee findByEmail(String email);
   
   @Query(value = "SELECT e FROM Employee e ORDER BY name")
   public List<Employee> findAllSortedByName();
}

讓我們透過在測試檔案中新增它們的測試用例來測試新增的方法。下面檔案的最後兩種方法測試了新增的自定義查詢方法。

以下是 EmployeeRepositoryTest 的完整程式碼。

package com.tutorialspoint.repository;

import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.ArrayList;
import java.util.List;
import javax.transaction.Transactional;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import com.tutorialspoint.entity.Employee;
import com.tutorialspoint.sprintbooth2.SprintBootH2Application;

@ExtendWith(SpringExtension.class)
@Transactional
@SpringBootTest(classes = SprintBootH2Application.class)
public class EmployeeRepositoryTest {
   @Autowired
   private EmployeeRepository employeeRepository;
   @Test
   public void testFindById() {
      Employee employee = getEmployee();	     
      employeeRepository.save(employee);
      Employee result = employeeRepository.findById(employee.getId()).get();
      assertEquals(employee.getId(), result.getId());	     
   }
   @Test
   public void testFindAll() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      List<Employee> result = new ArrayList<>();
      employeeRepository.findAll().forEach(e -> result.add(e));
      assertEquals(result.size(), 1);	     
   }
   @Test
   public void testSave() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      Employee found = employeeRepository.findById(employee.getId()).get();
      assertEquals(employee.getId(), found.getId());	     
   }
   @Test
   public void testDeleteById() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      employeeRepository.deleteById(employee.getId());
      List<Employee> result = new ArrayList<>();
      employeeRepository.findAll().forEach(e -> result.add(e));
      assertEquals(result.size(), 0);
   }
   private Employee getEmployee() {
      Employee employee = new Employee();
      employee.setId(1);
      employee.setName("Mahesh");
      employee.setAge(30);
      employee.setEmail("mahesh@test.com");
      return employee;
   }
   @Test
   public void testFindByName() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      List<Employee> result = new ArrayList<>();
      employeeRepository.findByName(employee.getName()).forEach(e -> result.add(e));
      assertEquals(result.size(), 1);	     
   }
   @Test
   public void testFindByAge() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      List<Employee> result = new ArrayList<>();
      employeeRepository.findByAge(employee.getAge()).forEach(e -> result.add(e));
      assertEquals(result.size(), 1);	     
   }
   @Test
   public void testFindByEmail() {	     
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      Employee result = employeeRepository.findByEmail(employee.getEmail());	     
      assertNotNull(result);	     
   }
   @Test
   public void testFindAllSortedByName() {
      Employee employee = getEmployee();
      Employee employee1 = new Employee();
      employee1.setId(2);
      employee1.setName("Aarav");
      employee1.setAge(20);
      employee1.setEmail("aarav@test.com");
      employeeRepository.save(employee);	     
      employeeRepository.save(employee1);
      List<Employee> result = employeeRepository.findAllSortedByName();
      assertEquals(employee1.getName(), result.get(0).getName());	     
   }
}

執行測試用例

在 Eclipse 中右鍵單擊該檔案,然後選擇 **執行 JUnit 測試** 並驗證結果。

Repository Test Custom Query method

Spring Boot JPA - 原生查詢

有時會出現這種情況,我們需要一個自定義原生查詢來滿足一個測試用例。我們可以使用 @Query 註解在儲存庫中指定查詢。以下是一個示例。在這個示例中,我們使用原生查詢,並在 Query 註解中設定屬性 **nativeQuery=true** 將查詢標記為原生查詢。

我們在 JPA 自定義查詢 章節中添加了自定義方法。現在讓我們使用原生查詢新增另一個方法並對其進行測試。

儲存庫 - EmployeeRepository.java

新增一個方法,用於獲取按名稱排序的員工列表。

package com.tutorialspoint.repository;

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.tutorialspoint.entity.Employee;

@Repository
public interface EmployeeRepository extends CrudRepository<Employee, Integer>  {
   public List<Employee> findByName(String name);	
   public List<Employee> findByAge(int age);
   public Employee findByEmail(String email);
   
   @Query(value = "SELECT e FROM Employee e ORDER BY name")
   public List<Employee> findAllSortedByName();
   
   @Query(value = "SELECT * FROM Employee ORDER BY name", nativeQuery = true)
   public List<Employee> findAllSortedByNameUsingNative();
}

讓我們透過在測試檔案中新增它們的測試用例來測試新增的方法。下面檔案的最後兩種方法測試了新增的自定義查詢方法。

示例

以下是 EmployeeRepositoryTest 的完整程式碼。

package com.tutorialspoint.repository;

import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.ArrayList;
import java.util.List;
import javax.transaction.Transactional;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import com.tutorialspoint.entity.Employee;
import com.tutorialspoint.sprintbooth2.SprintBootH2Application;

@ExtendWith(SpringExtension.class)
@Transactional
@SpringBootTest(classes = SprintBootH2Application.class)
public class EmployeeRepositoryTest {
   @Autowired
   private EmployeeRepository employeeRepository;

   @Test
   public void testFindById() {
      Employee employee = getEmployee();	     
      employeeRepository.save(employee);
      Employee result = employeeRepository.findById(employee.getId()).get();
      assertEquals(employee.getId(), result.getId());	     
   }
   @Test
   public void testFindAll() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      List<Employee> result = new ArrayList<>();
      employeeRepository.findAll().forEach(e -> result.add(e));
      assertEquals(result.size(), 1);	     
   }
   @Test
   public void testSave() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      Employee found = employeeRepository.findById(employee.getId()).get();
      assertEquals(employee.getId(), found.getId());	     
   }
   @Test
   public void testDeleteById() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      employeeRepository.deleteById(employee.getId());
      List<Employee> result = new ArrayList<>();
      employeeRepository.findAll().forEach(e -> result.add(e));
      assertEquals(result.size(), 0);
   }
   private Employee getEmployee() {
      Employee employee = new Employee();
      employee.setId(1);
      employee.setName("Mahesh");
      employee.setAge(30);
      employee.setEmail("mahesh@test.com");
      return employee;
   }
   @Test
   public void testFindByName() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      List<Employee> result = new ArrayList<>();
      employeeRepository.findByName(employee.getName()).forEach(e -> result.add(e));
      assertEquals(result.size(), 1);	     
   }
   @Test
   public void testFindByAge() {
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      List<Employee> result = new ArrayList<>();
      employeeRepository.findByAge(employee.getAge()).forEach(e -> result.add(e));
      assertEquals(result.size(), 1);	     
   }
   @Test
   public void testFindByEmail() {	     
      Employee employee = getEmployee();
      employeeRepository.save(employee);
      Employee result = employeeRepository.findByEmail(employee.getEmail());	     
      assertNotNull(result);	     
   }
   @Test
   public void testFindAllSortedByName() {
      Employee employee = getEmployee();
      Employee employee1 = new Employee();
      employee1.setId(2);
      employee1.setName("Aarav");
      employee1.setAge(20);
      employee1.setEmail("aarav@test.com");
      employeeRepository.save(employee);	     
      employeeRepository.save(employee1);
      List<Employee> result = employeeRepository.findAllSortedByName();
      assertEquals(employee1.getName(), result.get(0).getName());	     
   }
   @Test
   public void testFindAllSortedByNameUsingNative() {
      Employee employee = getEmployee();
      Employee employee1 = new Employee();
      employee1.setId(2);
      employee1.setName("Aarav");
      employee1.setAge(20);
      employee1.setEmail("aarav@test.com");
      employeeRepository.save(employee);	     
      employeeRepository.save(employee1);
      List<Employee> result = employeeRepository.findAllSortedByNameUsingNative();
      assertEquals(employee1.getName(), result.get(0).getName());	     
   }   
}

執行測試用例

輸出

在 Eclipse 中右鍵單擊該檔案,然後選擇 **執行 JUnit 測試** 並驗證結果。

Repository Test Native Query Method
廣告