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://127.0.0.1:8080/emp/employee**

  • 正文 - **一个员工 JSON 对象**

{  
   "id": "1",  
   "age": "35",  
   "name": "Julie",  
   "email": "[email protected]"  
}   

单击“发送”按钮并检查响应状态是否为 OK。现在发出 GET 请求以获取所有记录。

在 POSTMAN 中设置以下参数。

  • HTTP 方法 - **GET**

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

单击发送按钮并验证响应。

[{  
   "id": "1",  
   "age": "35",  
   "name": "Julie",  
   "email": "[email protected]"  
}]   

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("[email protected]");
      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("[email protected]");
      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("[email protected]");
      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("[email protected]");
      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("[email protected]");
      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("[email protected]");
      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("[email protected]");
      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("[email protected]");
      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
广告