通过 REST 访问 JPA 数据

本指南将引导你完成创建应用程序的过程,该应用程序通过基于超媒体的 RESTful 前端访问关系型 JPA 数据。

你将构建什么

你将构建一个 Spring 应用程序,它允许你使用 Spring Data REST 创建和检索存储在数据库中的 Person 对象。Spring Data REST 采用了 Spring HATEOASSpring Data JPA 的特性,并自动将它们组合在一起。

Spring Data REST 还支持 Spring Data Neo4jSpring Data GemfireSpring Data MongoDB 作为后端数据存储,但它们不属于本指南的内容。

你需要什么

如何完成本指南

与大多数 Spring 入门指南 一样,你可以从头开始完成每一步,也可以跳过你已经熟悉的设置步骤。无论哪种方式,你最终都会得到可用的代码。

从头开始,请继续 使用 Spring Initializr 开始

跳过基础知识,请执行以下操作

完成后,可以根据 {project_id}/complete 中的代码检查结果。

从 Spring Initializr 开始

可以使用此 预初始化项目,然后单击“生成”以下载 ZIP 文件。此项目已配置为适合本教程中的示例。

手动初始化项目

  1. 导航到 https://start.spring.io。此服务会提取应用程序所需的所有依赖项,并为您完成大部分设置。

  2. 选择 Gradle 或 Maven 以及要使用的语言。本指南假设您选择了 Java。

  3. 单击“依赖项”,然后选择“Rest 存储库”、“Spring Data JPA”和“H2 数据库”。

  4. 单击“生成”。

  5. 下载生成的 ZIP 文件,该文件是已根据您的选择配置的 Web 应用程序的存档。

如果您的 IDE 集成了 Spring Initializr,则可以从 IDE 完成此过程。
您还可以从 Github 分叉项目,并在 IDE 或其他编辑器中打开它。

创建域对象

创建新的域对象来表示一个人,如下表所示(在 src/main/java/com/example/accessingdatarest/Person.java 中)

package com.example.accessingdatarest;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class Person {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private long id;

  private String firstName;
  private String lastName;

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
}

Person 对象有名字和姓氏。(还有一个 ID 对象被配置为自动生成,因此您不必处理它。)

创建人员存储库

接下来,您需要创建一个简单的存储库,如下表所示(在 src/main/java/com/example/accessingdatarest/PersonRepository.java 中)

package com.example.accessingdatarest;

import java.util.List;

import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

@RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long>, CrudRepository<Person,Long> {

  List<Person> findByLastName(@Param("name") String name);

}

此存储库是一个接口,允许您执行涉及 Person 对象的各种操作。它通过扩展 Spring Data Commons 中定义的 PagingAndSortingRepository 接口来获取这些操作。

在运行时,Spring Data REST 会自动创建此接口的实现。然后,它使用 @RepositoryRestResource 注释来指示 Spring MVC 在 /people 处创建 RESTful 端点。

@RepositoryRestResource 不是导出存储库的必需项。它仅用于更改导出详细信息,例如使用 /people 而不是默认值 /persons

在此处,您还定义了一个自定义查询,以基于 lastName 检索 Person 对象的列表。您可以在本指南的后面部分了解如何调用它。

@SpringBootApplication 是一个便捷注释,它添加了以下所有内容

  • @Configuration:将类标记为应用程序上下文的 bean 定义源。

  • @EnableAutoConfiguration:指示 Spring Boot 根据类路径设置、其他 bean 和各种属性设置开始添加 bean。例如,如果 spring-webmvc 位于类路径中,则此注释将应用程序标记为 Web 应用程序并激活关键行为,例如设置 DispatcherServlet

  • @ComponentScan:指示 Spring 在 com/example 包中查找其他组件、配置和服务,以便它找到控制器。

main() 方法使用 Spring Boot 的 SpringApplication.run() 方法启动应用程序。您是否注意到没有一行 XML?也没有 web.xml 文件。此 Web 应用程序是 100% 纯 Java,并且您不必处理配置任何管道或基础设施。

Spring Boot 自动启动 Spring Data JPA 以创建 PersonRepository 的具体实现,并通过使用 JPA 将其配置为与后端内存数据库通信。

Spring Data REST 构建在 Spring MVC 之上。它创建一系列 Spring MVC 控制器、JSON 转换器和其他 bean,以提供 RESTful 前端。这些组件链接到 Spring Data JPA 后端。当您使用 Spring Boot 时,所有这些都是自动配置的。如果您想了解其工作原理,请查看 Spring Data REST 中的 RepositoryRestMvcConfiguration

构建可执行 JAR

您可以使用 Gradle 或 Maven 从命令行运行应用程序。您还可以构建一个包含所有必需的依赖项、类和资源的可执行 JAR 文件并运行它。构建可执行 jar 使得在整个开发生命周期、不同环境中轻松地交付、版本化和部署服务作为应用程序,等等。

如果您使用 Gradle,则可以使用 ./gradlew bootRun 运行应用程序。或者,您可以使用 ./gradlew build 构建 JAR 文件,然后运行 JAR 文件,如下所示

java -jar build/libs/{project_id}-0.1.0.jar

如果您使用 Maven,则可以使用 ./mvnw spring-boot:run 运行应用程序。或者,您可以使用 ./mvnw clean package 构建 JAR 文件,然后运行 JAR 文件,如下所示

java -jar target/{project_id}-0.1.0.jar
此处描述的步骤创建了一个可运行的 JAR。您还可以 构建一个经典 WAR 文件

显示日志输出。该服务应在几秒钟内启动并运行。

测试应用程序

现在应用程序正在运行,您可以对其进行测试。您可以使用任何 REST 客户端。以下示例使用 *nix 工具 curl

首先,您要查看顶级服务。以下示例演示如何执行此操作

$ curl https://127.0.0.1:8080
{
  "_links" : {
    "people" : {
      "href" : "https://127.0.0.1:8080/people{?page,size,sort}",
      "templated" : true
    }
  }
}

上述示例提供了此服务器提供的功能的初步了解。有一个 people 链接位于 https://127.0.0.1:8080/people。它有一些选项,例如 ?page?size?sort

Spring Data REST 使用 HAL 格式 作为 JSON 输出。它很灵活,提供了一种便捷的方式来提供与所提供数据相邻的链接。

以下示例演示如何查看人员记录(目前没有)

$ curl https://127.0.0.1:8080/people
{
  "_embedded" : {
    "people" : []
  },
  "_links" : {
    "self" : {
      "href" : "https://127.0.0.1:8080/people{?page,size,sort}",
      "templated" : true
    },
    "search" : {
      "href" : "https://127.0.0.1:8080/people/search"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 0,
    "totalPages" : 0,
    "number" : 0
  }
}

当前没有元素,因此没有页面。是时候创建一个新的 Person 了!以下清单演示如何执行此操作

$ curl -i -H "Content-Type:application/json" -d '{"firstName": "Frodo", "lastName": "Baggins"}' https://127.0.0.1:8080/people
HTTP/1.1 201 Created
Server: Apache-Coyote/1.1
Location: https://127.0.0.1:8080/people/1
Content-Length: 0
Date: Wed, 26 Feb 2014 20:26:55 GMT
  • -i:确保您可以看到包括标头在内的响应消息。显示新创建的 Person 的 URI。

  • -H "Content-Type:application/json":设置内容类型,以便应用程序知道有效负载包含一个 JSON 对象。

  • -d '{"firstName": "Frodo", "lastName": "Baggins"}':是要发送的数据。

  • 如果您使用的是 Windows,则上述命令将在 WSL 上运行。如果您无法安装 WSL,您可能需要用双引号替换单引号并转义现有的双引号,即 -d "{\"firstName\": \"Frodo\", \"lastName\": \"Baggins\"}"

请注意,对 POST 操作的响应如何包含一个 Location 标头。其中包含新创建的资源的 URI。Spring Data REST 还具有两种方法(RepositoryRestConfiguration.setReturnBodyOnCreate(…)setReturnBodyOnUpdate(…)),您可以使用它们来配置框架以立即返回刚创建的资源的表示。RepositoryRestConfiguration.setReturnBodyForPutAndPost(…) 是一个快捷方法,用于启用创建和更新操作的表示响应。

您可以查询所有人员,如下例所示

$ curl https://127.0.0.1:8080/people
{
  "_links" : {
    "self" : {
      "href" : "https://127.0.0.1:8080/people{?page,size,sort}",
      "templated" : true
    },
    "search" : {
      "href" : "https://127.0.0.1:8080/people/search"
    }
  },
  "_embedded" : {
    "people" : [ {
      "firstName" : "Frodo",
      "lastName" : "Baggins",
      "_links" : {
        "self" : {
          "href" : "https://127.0.0.1:8080/people/1"
        }
      }
    } ]
  },
  "page" : {
    "size" : 20,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}

people 对象包含一个列表,其中包括 Frodo。请注意,它如何包含一个 self 链接。Spring Data REST 还使用 Evo Inflector 对实体的名称进行复数化以进行分组。

您可以直接查询单个记录,如下所示

$ curl https://127.0.0.1:8080/people/1
{
  "firstName" : "Frodo",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "https://127.0.0.1:8080/people/1"
    }
  }
}
这似乎是纯基于 Web 的。但是,在幕后,有一个 H2 关系数据库。在生产中,您可能使用一个真正的数据库,例如 PostgreSQL。
在本指南中,只有一个域对象。对于一个更复杂的系统,其中域对象相互关联,Spring Data REST 会呈现其他链接以帮助导航到连接的记录。

您可以找到所有自定义查询,如下例所示

$ curl https://127.0.0.1:8080/people/search
{
  "_links" : {
    "findByLastName" : {
      "href" : "https://127.0.0.1:8080/people/search/findByLastName{?name}",
      "templated" : true
    }
  }
}

您可以看到查询的 URL,包括 HTTP 查询参数 name。请注意,这与嵌入在接口中的 @Param("name") 注释相匹配。

以下示例演示如何使用 findByLastName 查询

$ curl https://127.0.0.1:8080/people/search/findByLastName?name=Baggins
{
  "_embedded" : {
    "persons" : [ {
      "firstName" : "Frodo",
      "lastName" : "Baggins",
      "_links" : {
        "self" : {
          "href" : "https://127.0.0.1:8080/people/1"
        }
      }
    } ]
  }
}

由于您在代码中将其定义为返回 List<Person>,因此它会返回所有结果。如果您将其定义为仅返回 Person,它会选择一个 Person 对象进行返回。由于这可能是不可预测的,因此您可能不想对可以返回多个条目的查询执行此操作。

您还可以发出 PUTPATCHDELETE REST 调用来分别替换、更新或删除现有记录。以下示例使用 PUT 调用

$ curl -X PUT -H "Content-Type:application/json" -d '{"firstName": "Bilbo", "lastName": "Baggins"}' https://127.0.0.1:8080/people/1
$ curl https://127.0.0.1:8080/people/1
{
  "firstName" : "Bilbo",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "https://127.0.0.1:8080/people/1"
    }
  }
}

以下示例使用 PATCH 调用

$ curl -X PATCH -H "Content-Type:application/json" -d '{"firstName": "Bilbo Jr."}' https://127.0.0.1:8080/people/1
$ curl https://127.0.0.1:8080/people/1
{
  "firstName" : "Bilbo Jr.",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "https://127.0.0.1:8080/people/1"
    }
  }
}
PUT 替换整个记录。未提供的字段将替换为 null。您可以使用 PATCH 更新项目子集。

您还可以删除记录,如下例所示

$ curl -X DELETE https://127.0.0.1:8080/people/1
$ curl https://127.0.0.1:8080/people
{
  "_links" : {
    "self" : {
      "href" : "https://127.0.0.1:8080/people{?page,size,sort}",
      "templated" : true
    },
    "search" : {
      "href" : "https://127.0.0.1:8080/people/search"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 0,
    "totalPages" : 0,
    "number" : 0
  }
}

此超媒体驱动界面的一个便利方面是,您可以使用 curl(或您喜欢的任何 REST 客户端)发现所有 RESTful 端点。您无需与客户交换正式合同或接口文档。

摘要

恭喜!您已经开发了一个具有 基于超媒体 的 RESTful 前端和基于 JPA 的后端的应用程序。

获取代码