Spring Boot Jpa 简明教程

Spring Boot JPA - Quick Guide

Spring Boot JPA - Overview

What is JPA?

Java 持久化 API 是一个类和方法的集合,用于将海量数据持久性地存储到由 Oracle Corporation 提供的数据库中。

Where to use JPA?

为了减轻编写关系对象管理代码的负担,程序员遵循“JPA 提供程序”框架,该框架允许轻松与数据库实例进行交互。此处必需的框架由 JPA 接管。

jpa provider

JPA History

早期版本的 EJB 将持久性层与业务逻辑层结合在一起,使用 javax.ejb.EntityBean 接口定义。

  1. 在引入 EJB 3.0 时,持久性层被分离并指定为 JPA 1.0(Java 持久化 API)。该 API 的规范于 2006 年 5 月 11 日随 JAVA EE5 规范一起发布,使用 JSR 220。

  2. JPA 2.0 于 2009 年 12 月 10 日随 JAVA EE6 规范一起发布,作为 Java 社区进程 JSR 317 的一部分。

  3. JPA 2.1 于 2013 年 4 月 22 日随 JAVA EE7 规范一起发布,使用 JSR 338。

JPA Providers

JPA 是一个开源 API,因此,各种企业供应商(如 Oracle、Redhat、Eclipse 等)通过在其中添加 JPA 持久性风格来提供新产品。其中一些产品包括 -

Hibernate, Eclipselink, Toplink, Spring Data JPA, etc.

Spring Boot JPA - Environment Setup

本章将指导您如何准备开发环境,以便使用 Spring Boot Framework 开始您的工作。它还将教您在设置 Spring Boot Framework 之前如何设置机器上的 JDK、Eclipse -

Step 1 - Setup Java Development Kit (JDK)

Java SE 可免费下载。若要下载 click here,请下载与你的操作系统兼容的版本。

按照说明下载 Java,并运行 .exe 在你的计算机上安装 Java。在计算机上安装 Java 后,你需要设置环境变量来指向正确的安装目录。

Setting Up the Path for Windows 2000/XP

假设你已将 Java 安装在 c:\Program Files\java\jdk 目录中 −

  1. 右键单击“我的电脑”,然后选择“属性”。

  2. 单击“高级”选项卡下的“环境变量”按钮。

  3. 现在,编辑“Path”变量,并在其末尾添加 Java 可执行目录的路径。例如,如果路径当前设置为 C:\Windows\System32 ,则按如下方式进行编辑 C:\Windows\System32;c:\Program Files\java\jdk\bin

Setting Up the Path for Windows 95/98/ME

假设你已将 Java 安装在 c:\Program Files\java\jdk 目录中 −

  1. 编辑“C:\autoexec.bat”文件,并在结尾处添加以下行 − SET PATH=%PATH%;C:\Program Files\java\jdk\bin

Setting Up the Path for Linux, UNIX, Solaris, FreeBSD

环境变量 PATH 应设置为指向已安装 Java 二进制文件的位置。如果你在这方面遇到问题,请参阅 shell 文档。

例如,如果你使用 bash 作为你的 shell,那么你将在 .bashrc 的末尾添加以下行 −

或者,如果你使用诸如 Borland JBuilder、Eclipse、IntelliJ IDEA 或 Sun ONE Studio 这样的集成开发环境 (IDE),则必须编译并运行一个简单程序来确认 IDE 知道你在何处安装了 Java。否则,你必须按照 IDE 文档中给出的内容执行正确的设置。

Step 2 - Setup 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

成功启动后,如果一切正常,它应该显示以下结果 −

eclipsehomepage

Step 3 - Setup m2eclipse

M2Eclipse 是一个 Eclipse 插件,它对于将 Apache Maven 集成到 Eclipse IDE 中非常有用。我们在本教程中使用 maven 来构建 spring boot 项目,并使用 m2eclipse 在 eclipse 内运行示例。

使用 Eclipse IDE 中的“安装新软件”对话框安装最新的 M2Eclipse 版本,并将其指向此 p2 仓库 -

Step 3 - Setup Spring Boot Project

现在,如果一切正常,则可以继续设置 Spring Boot。以下是下载和在机器上安装 Spring Boot 项目的简单步骤。

  1. 转到 Spring 初始化器链接以创建 Spring Boot 项目, https://start.spring.io/

  2. 选择项目作为 Maven Project

  3. Select language as Java.

  4. 选择 Spring Boot 版本作为 2.5.3

  5. 将项目元数据设定为 - 组为 com.tutorialspoint ,工件为 springboot-h2 ,名称为 springboot-h2 ,描述为 Demo project for Spring Boot and H2 Database ,包名为 com.tutorialspoint.springboot-h2

  6. Select packaging as Jar.

  7. Select java as 11.

  8. 添加依赖关系为 Spring Web, Spring Data JPA, H2 Database and Spring Boot DevTools

现在点击生成按钮来生成项目结构。

spring initializr

一旦基于 maven 的 Spring Boot 项目下载完成,然后将 maven 项目导入 eclipse,其余操作由 eclipse 处理。它将下载 maven 依赖关系,并构建项目以使之可以进行进一步的开发。

Step 4 - POSTMAN for REST APIs Testing

POSTMAN 是一款用来测试基于 REST 的 API 的有用工具。要安装 POSTMAN,可从 www.postman.com/downloads/ 下载最新的 POSTMAN 二进制文件。下载完可安装文件后,按照说明进行安装并使用。

Spring Boot JPA - Architecture

Java 持久化 API 是一个将业务实体存储为关系实体的源。它展示了如何将一个普通旧 Java 对象 (POJO) 定义为实体,以及如何通过关系管理实体。

Class Level Architecture

下图展示了 JPA 的类级架构。它展示了 JPA 的核心类和接口。

jpa class level architecture

下表描述了上述架构中显示的每个单元。

Sr.No

Units & Description

1

EntityManagerFactory 它是 EntityManager 的工厂类。它创建和管理多个 EntityManager 实例。

2

EntityManager 它是一个接口,管理对象上的持久性操作。它如同 Query 实例的工厂。

3

Entity 实体是持久性对象,存储为数据库中的记录。

4

EntityTransaction 它与 EntityManager 存在一对一的关系。对于每个 EntityManager,操作由 EntityTransaction 类维护。

5

Persistence 此类包含获取 EntityManagerFactory 实例的静态方法。

6

Query 此接口由每个 JPA 供应商实现,以获取满足条件的关系对象。

上述类和接口用于将实体作为记录存储在数据库中。它们通过减少程序员为将数据存储到数据库中而编写代码的工作量来为他们提供帮助,以便他们可以专注于诸如编写映射类与数据库表的代码之类的更重要的活动。

JPA Class Relationships

在上述架构中,类和接口之间的关系属于 javax.persistence 包。下图显示了它们之间的关系。

jpa class relationships
  1. EntityManagerFactory 和 EntityManager 之间的关系为 one-to-many 。它是 EntityManager 实例的工厂类。

  2. EntityManager 和 EntityTransaction 之间的关系为 one-to-one 。对于每个 EntityManager 操作,都有一个 EntityTransaction 实例。

  3. EntityManager 和 Query 之间的关系为 one-to-many 。可以使用一个 EntityManager 实例执行许多查询。

  4. EntityManager 和 Entity 之间的关系为 one-to-many 。一个 EntityManager 实例可以管理多个 Entity。

Spring Boot JPA vs Hibernate

JPA

JPA 是一个规范,它指定如何通过 Java 对象和关系数据库来访问、管理和持久化信息/数据。它为 ORM(对象关系映射)提供了一种标准方法。

Hibernate

Hibernate 是 JPA 的一种实现。它提供了一个轻量级框架,并且是最流行的 ORM 工具之一。

JPA Vs Hibernate

下表总结了 JPA 和 Hibernate 之间的差异。

Category

JPA

Hibernate

Type

JPA 是一个规范,并定义了使用 Java 对象管理关系数据库数据的方法。

Hibernate 是 JPA 的一种实现。它是一种 ORM 工具,用于将 Java 对象持久化到关系数据库中。

Package

JPA uses javax.persistence package.

Hibernate uses org.hibernate package.

Factory

JPA 使用 EntityManagerFactory 接口获得实体管理器以持久化对象。

Hibernate 使用 SessionFactory 接口来创建会话对象,该对象随后用于持久化对象。

CRUD Operations

JPA 使用 EntityManager 接口来创建/读取/删除操作,并维护持久性上下文。

Hibernate 使用 Session 接口来创建/读取/删除操作,并维护持久性上下文。

Language

JPA 使用 JPQL(Java 持久性查询语言)作为面向对象查询语言进行数据库操作。

Hibernate 使用 HQL(Hibernate 查询语言)作为面向对象查询语言进行数据库操作。

Spring Boot JPA - Application Setup

与上一章 Environment Setup 中一样,我们已在 Eclipse 中导入了已生成的 Spring Boot 项目。现在,让我们在 src/main/java 文件夹中创建以下结构。

project structure
  1. com.tutorialspoint.controller.EmployeeController ——一个REST基于控制器实现REST基于API。

  2. com.tutorialspoint.entity.Employee ——一个实体类表示数据库中对应的表。

  3. com.tutorialspoint.repository.EmployeeRepository ——一个存储库接口来执行crud操作库。

  4. com.tutorialspoint.service.EmployeeService ——一个服务类来执行业务操作库函数。

  5. com.tutorialspoint.springbooth2.SprintBootH2Application ——一个SpringBoot应用程序类。

SprintBootH2Application类已存在。我们须创建上述包和相关类和接口如下所示——

Entity - 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;
   }
}

Repository - EmployeeRepository.java

以下为实现上述实体创建CRUD操作的存储库的默认代码,Employee。

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>  {
}

Service - EmployeeService.java

以下为实现库函数操作的服务的默认代码。

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);
   }
}

Controller - 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);
   }
}

Application - SprintBootH2Application.java

以下为用于使用上述类的Application的更新代码。

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);
   }
}

Run/Debug Configuration

在eclipse中创建以下 maven configuration 来运行带目标 spring-boot:run 的springboot应用程序。此配置将有助于运行REST API,我们可以使用POSTMAN对其进行测试。

maven configuration

Run the application

在 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 中设置以下参数。

  1. HTTP Method - POST

  2. URL - http://localhost:8080/emp/employee

  3. 正文 - An employee JSON

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

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

在 POSTMAN 中设置以下参数。

  1. HTTP Method - GET

  2. URL - http://localhost:8080/emp/employees

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

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

Spring Boot JPA - Unit Test Repository

要测试存储库,我们需要以下注释和类:

  1. @ExtendWith(SpringExtension.class) ——使用SpringExtension类将类标记为以测试用例运行。

  2. @SpringBootTest(classes = SprintBootH2Application.class) - 配置 Spring Boot 应用程序。

  3. @Transactional - 用于标记存储库以执行 CRUD 操作。

  4. @Autowired private EmployeeRepository employeeRepository - 要测试的 EmployeeRepository 对象。

Example

以下是 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;
   }
}

Run the test cases

Output

在 Eclipse 中右键单击该文件并选择 Run a JUnit Test ,然后验证结果。

repository test result

Spring Boot JPA - Repository methods

现在让我们分析在已创建的存储库接口中可用的方法。

Repository - EmployeeRepository.java

以下为实现上述实体创建CRUD操作的存储库的默认代码,Employee。

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>  {
}

现在,此存储库默认包含以下方法。

Sr.No

Method & Description

1

count(): long 返回可用的实体数量。

2

delete(Employee entity): void 删除实体。

3

deleteAll():void deletes all the entities.

4

deleteAll(Iterable&lt; extends Employee &gt; entities):void 删除作为参数传递的实体。

5

deleteAll(Iterable&lt; extends Integer &gt; ids):void 使用作为参数传递的 ID 来删除实体。

6

existsById(Integer id):boolean 使用其 ID 检查实体是否存在。

7

findAll():Iterable&lt; Employee &gt; 返回所有实体。

8

findAllByIds(Iterable&lt; Integer &gt; ids):Iterable&lt; Employee &gt; 返回使用作为参数传递的 ID 标识的所有实体。

9

findById(Integer id):Optional&lt; Employee &gt; 返回使用 ID 标识的实体。

10

save(Employee entity): Employee 保存实体并返回更新后的实体。

11

saveAll(Iterable&lt; Employee&gt; entities): Iterable&lt; Employee&gt; 会保存所有传递的实体并返回更新的实体。

Spring Boot JPA - Custom methods

我们在 JPA Methods 章节中检查了存储库中默认提供的这些方法。现在我们添加一个方法,并对其进行测试。

Repository - EmployeeRepository.java

Example

添加一个通过名字找到员工的方法。

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 会根据我们遵循基于属性的命名方式自动创建一个以上方法的实现。让我们在 test 文件中添加测试用例,从而测试所添加的方法。以下文件中的最后两个方法测试了所添加的自定义方法。

以下是 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);
   }
}

Run the test cases

Output

在 Eclipse 中右键单击该文件并选择 Run a JUnit Test ,然后验证结果。

unit testing custom methods

Spring Boot JPA - Named Queries

有时会出现需要自定义查询来完成某个测试用例的情况。我们可以使用 @NamedQuery 注解指定实体类中的命名查询,然后再在存储库中声明该方法。以下是示例。

我们在 JPA Custom Methods 章节中存储库中添加了自定义方法。现在,我们使用 @NamedQuery 添加另一个方法,并对其进行测试。

Entity - 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;
   }
}

Repository - 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 将使用命名查询中提供的查询,自动创建以上方法的实现。让我们在 test 文件中添加测试用例,从而测试所添加的方法。以下文件中的最后两个方法测试了所添加的命名查询方法。

以下是 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);
   }
}

Run the test cases

在 Eclipse 中右键单击该文件并选择 Run a JUnit Test ,然后验证结果。

unit testing named queries

Spring Boot JPA - Custom Query

有时会出现需要自定义查询来完成某个测试用例的情况。我们可以使用 @Query 注解在存储库内指定一个查询。以下是示例。在此示例中,我们使用 Java 持久化查询语言 (JPQL)。

我们在 JPA Named Query 章节中存储库中添加了名为查询的自定义方法。现在,我们使用 @Query 添加另一个方法,并对其进行测试。

Repository - 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();
}

让我们在 test 文件中添加测试用例,从而测试所添加的方法。以下文件中的最后两个方法测试了所添加的自定义查询方法。

以下是 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());
   }
}

Run the test cases

在 Eclipse 中右键单击该文件并选择 Run a JUnit Test ,然后验证结果。

unit testing custom queries

Spring Boot JPA - Native Query

有时会出现需要自定义本机查询来完成某个测试用例的情况。我们可以使用 @Query 注解在存储库内指定一个查询。以下是示例。在此示例中,我们使用本机查询,并在 Query 注解中设置 nativeQuery=true 属性,将查询标记为本机查询。

我们在 JPA Custom Query 章节中存储库中添加了自定义方法。现在,我们使用本机查询添加另一个方法,并对其进行测试。

Repository - 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();
}

让我们在 test 文件中添加测试用例,从而测试所添加的方法。以下文件中的最后两个方法测试了所添加的自定义查询方法。

Example

以下是 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());
   }
}

Run the test cases

Output

在 Eclipse 中右键单击该文件并选择 Run a JUnit Test ,然后验证结果。

unit testing native queries