使用 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作为后端数据存储,但这些不在本指南的范围之内。

您需要什么

  • 大约 15 分钟

  • 您喜欢的文本编辑器或 IDE

  • Java 17 或更高版本

如何完成本指南

与大多数 Spring 入门指南一样,您可以从头开始完成每个步骤,也可以直接查看此存储库中的代码来跳转到解决方案。

在本地环境中查看最终结果,您可以执行以下操作之一:

从 Spring Initializr 开始

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

手动初始化项目

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

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

  3. 单击依赖项,然后选择Rest RepositoriesSpring Data JPAH2 Database

  4. 单击生成

  5. 下载生成的 ZIP 文件,这是一个使用您选择的选项配置的 Web 应用程序的存档。

如果您的 IDE 集成了 Spring Initializr,则可以在 IDE 中完成此过程。
您还可以从 Github fork 项目并在您的 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 对象被配置为自动生成,因此您无需处理它。)

创建 Person 存储库

接下来,您需要创建一个简单的存储库,如下面的列表(位于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对象的列表。您可以在本指南的后面看到如何调用它。

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

运行应用程序

您现在可以通过在AccessingDataRestApplication中执行 main 方法来运行应用程序。您可以从 IDE 运行程序,也可以在项目根目录中执行以下 Gradle 命令:

./gradlew bootRun

或者,您可以使用 Maven 使用以下命令运行应用程序:

./mvnw spring-boot:run

测试应用程序

应用程序运行后,您可以对其进行测试。您可以使用任何您想要的 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
    }
  }
}

前面的示例提供了对该服务器功能的初步了解。位于https://127.0.0.1:8080/people处有一个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 的后端的应用程序。

获取代码