编辑此页面

Spring Data REST 的扩展

虽然用户被鼓励使用 Panache REST Data 来生成 REST 数据访问端点,但 Quarkus 通过 spring-data-rest 扩展提供了一个兼容层,用于 Spring Data REST。

先决条件

要完成本指南,您需要

  • 大约 15 分钟

  • 一个 IDE

  • 已安装 JDK 17+ 并正确配置了 JAVA_HOME

  • Apache Maven 3.9.9

  • 如果您想使用它,可以选择 Quarkus CLI

  • 如果您想构建本机可执行文件(或者如果您使用本机容器构建,则为 Docker),可以选择安装 Mandrel 或 GraalVM 并进行适当的配置

解决方案

我们建议您按照以下章节中的说明,逐步创建应用程序。但是,您可以直接转到完整的示例。

克隆 Git 仓库:git clone https://github.com/quarkusio/quarkus-quickstarts.git,或下载一个 存档

解决方案位于 spring-data-rest-quickstart 目录

创建 Maven 项目

首先,我们需要一个新项目。使用以下命令创建一个新项目

CLI
quarkus create app org.acme:spring-data-rest-quickstart \
    --extension='spring-data-rest,rest-jackson,quarkus-jdbc-postgresql' \
    --no-code
cd spring-data-rest-quickstart

要创建 Gradle 项目,请添加 --gradle--gradle-kotlin-dsl 选项。

有关如何安装和使用 Quarkus CLI 的更多信息,请参阅 Quarkus CLI 指南。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.24.4:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=spring-data-rest-quickstart \
    -Dextensions='spring-data-rest,rest-jackson,quarkus-jdbc-postgresql' \
    -DnoCode
cd spring-data-rest-quickstart

要创建 Gradle 项目,请添加 -DbuildTool=gradle-DbuildTool=gradle-kotlin-dsl 选项。

对于 Windows 用户

  • 如果使用 cmd,(不要使用反斜杠 \ 并将所有内容放在同一行上)

  • 如果使用 Powershell,请将 -D 参数用双引号括起来,例如 "-DprojectArtifactId=spring-data-rest-quickstart"

此命令将生成一个包含 spring-data-rest 扩展的项目。

如果您已配置好 Quarkus 项目,可以通过在项目根目录中运行以下命令,将 spring-data-rest 扩展添加到项目中

CLI
quarkus extension add spring-data-rest
Maven
./mvnw quarkus:add-extension -Dextensions='spring-data-rest'
Gradle
./gradlew addExtension --extensions='spring-data-rest'

这会将以下内容添加到您的构建文件中

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-spring-data-rest</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-spring-data-rest")

此外,还需要添加以下依赖项

对于测试,您还需要 REST Assured。将其添加到构建文件中

pom.xml
<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>rest-assured</artifactId>
    <scope>test</scope>
</dependency>
build.gradle
testImplementation("io.rest-assured:rest-assured")

注意:resteasy-jacksonresteasy-jsonb 都受支持,并且可以互换使用。

定义实体

在本指南的整个过程中,将使用以下 JPA 实体

package org.acme.spring.data.rest;

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

@Entity
public class Fruit {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    private String color;


    public Fruit() {
    }

    public Fruit(String name, String color) {
        this.name = name;
        this.color = color;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}

配置数据库访问属性

将以下属性添加到 application.properties 中,以配置对本地 PostgreSQL 实例的访问。

quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=quarkus_test
quarkus.datasource.password=quarkus_test
quarkus.datasource.jdbc.url=jdbc:postgresql:quarkus_test
quarkus.datasource.jdbc.max-size=8
quarkus.hibernate-orm.schema-management.strategy=drop-and-create

此配置假定 PostgreSQL 将在本地运行。

完成此操作的一个非常简单的方法是使用以下 Docker 命令

docker run -it --rm=true --name quarkus_test -e POSTGRES_USER=quarkus_test -e POSTGRES_PASSWORD=quarkus_test -e POSTGRES_DB=quarkus_test -p 5432:5432 postgres:14.1

如果您计划使用不同的设置,请相应地更改您的 application.properties

准备数据

为了更容易地展示 Spring Data REST 在 Quarkus 上的某些功能,应通过将以下内容添加到名为 src/main/resources/import.sql 的新文件中,将一些测试数据插入数据库

INSERT INTO fruit(id, name, color) VALUES (1, 'Cherry', 'Red');
INSERT INTO fruit(id, name, color) VALUES (2, 'Apple', 'Red');
INSERT INTO fruit(id, name, color) VALUES (3, 'Banana', 'Yellow');
INSERT INTO fruit(id, name, color) VALUES (4, 'Avocado', 'Green');
INSERT INTO fruit(id, name, color) VALUES (5, 'Strawberry', 'Red');

Hibernate ORM 将在应用程序启动时执行这些查询。

定义存储库

现在是时候定义将用于访问 Fruit 的存储库了。以典型的 Spring Data 方式,像这样创建一个存储库

package org.acme.spring.data.rest;

import org.springframework.data.repository.CrudRepository;

public interface FruitsRepository extends CrudRepository<Fruit, Long> {
}

上面的 FruitsRepository 扩展了 Spring Data 的 org.springframework.data.repository.CrudRepository,这意味着 FruitsRepository 可以使用后者提供的所有方法。

spring-data-jpa 扩展将为该存储库生成一个实现。然后 spring-data-rest 扩展将为其生成一个 REST CRUD 资源。

更新测试

要测试 FruitsRepository 的功能,请继续更新 FruitsRepositoryTest 的内容为

package org.acme.spring.data.rest;

import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.core.IsNot.not;

@QuarkusTest
class FruitsRepositoryTest {

    @Test
    void testListAllFruits() {
        //List all, should have all 3 fruits the database has initially:
        given()
                .accept("application/json")
                .when().get("/fruits")
                .then()
                .statusCode(200)
                .body(
                        containsString("Cherry"),
                        containsString("Apple"),
                        containsString("Banana")
                );

        //Delete the Cherry:
        given()
                .when().delete("/fruits/1")
                .then()
                .statusCode(204);

        //List all, cherry should be missing now:
        given()
                .accept("application/json")
                .when().get("/fruits")
                .then()
                .statusCode(200)
                .body(
                        not(containsString("Cherry")),
                        containsString("Apple"),
                        containsString("Banana")
                );

        //Create a new Fruit
        given()
                .contentType("application/json")
                .accept("application/json")
                .body("{\"name\": \"Orange\", \"color\": \"Orange\"}")
                .when().post("/fruits")
                .then()
                .statusCode(201)
                .body(containsString("Orange"))
                .body("id", notNullValue())
                .extract().body().jsonPath().getString("id");

        //List all, Orange should be present now:
        given()
                .accept("application/json")
                .when().get("/fruits")
                .then()
                .statusCode(200)
                .body(
                        not(containsString("Cherry")),
                        containsString("Apple"),
                        containsString("Orange")
                );
    }
}

通过发出以下命令可以轻松运行测试

Maven
./mvnw test
Gradle
./gradlew test

打包并运行应用程序

Quarkus 开发模式就像使用任何其他 Quarkus 扩展一样,可以与定义的存储库配合使用,从而极大地提高您在开发周期中的生产力。应用程序可以像往常一样使用以下命令在开发模式下启动

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

将应用程序作为原生二进制文件运行

当然,您可以通过 构建原生可执行文件 指南中的说明来创建原生可执行文件。

支持的 Spring Data REST 功能

Quarkus 目前支持 Spring Data REST 功能的子集,即最有用且最常用的功能。

支持什么

以下各节描述了 Spring Data REST 最重要的支持功能。

自动 REST 端点生成

扩展了以下任何 Spring Data 存储库的接口都可以自动生成 REST 端点

  • org.springframework.data.repository.CrudRepository

  • org.springframework.data.repository.PagingAndSortingRepository

  • org.springframework.data.jpa.repository.JpaRepository

从上述存储库生成的端点公开五种常见的 REST 操作

  • GET /fruits - 列出所有实体,如果使用 PagingAndSortingRepositoryJpaRepository,则返回一页。

  • GET /fruits/:id - 按 ID 返回实体。

  • POST /fruits - 创建一个新实体。

  • PUT /fruits/:id - 更新现有实体或使用指定 ID 创建新实体(如果实体定义允许)。

  • DELETE /fruits/:id - 按 ID 删除实体。

支持两种数据类型:application/jsonapplication/hal+json。前者是默认使用的,但强烈建议使用 Accept 标头指定您偏好的类型。

暴露大量实体

如果数据库包含大量实体,一次返回所有实体可能不是个好主意。PagingAndSortingRepository 允许 spring-data-rest 扩展分块访问数据。

因此,您可以扩展 PagingAndSortingRepository

package org.acme.spring.data.rest;

import org.springframework.data.repository.PagingAndSortingRepository;

public interface FruitsRepository extends CrudRepository<Fruit, Long>, PagingAndSortingRepository<Fruit, Long> {
}

现在 GET /fruits 将接受三个新查询参数:sortpagesize

查询参数 描述 默认值 示例值

sort

对列表操作返回的实体进行排序

""

?sort=name(按名称升序),?sort=name,-color(按名称升序和按颜色降序)

page

零索引的页码。无效值被解释为 0。

0

0, 11, 100

size

每页大小。接受的最小值为 1。任何较低的值都被解释为 1。

20

1, 11, 100

对于分页响应,spring-data-rest 还返回一组链接标头,可用于访问其他页面:首页、上一页、下一页和尾页。

此外,您可以直接使用 JpaRepository,它是一个为 JPA 量身定制的更高级别的抽象,而不是同时扩展 PagingAndSortingRepositoryCrudRepository。由于 JpaRepository 已经扩展了 PagingAndSortingRepositoryCrudRepository,因此它可以直接替换 CrudRepository

package org.acme.spring.data.rest;

import org.springframework.data.repository.PagingAndSortingRepository;

public interface FruitsRepository extends JpaRepository<Fruit, Long> {
}

精细调整端点生成

这允许用户指定应公开哪些方法以及用于访问它们的路径。Spring Data REST 提供了两个可以使用的注解:@RepositoryRestResource@RestResourcespring-data-rest 扩展支持这些注解的 exportedpathcollectionResourceRel 属性。

例如,假设 fruits 存储库应该可以通过 /my-fruits 路径访问,并且只允许 GET 操作。在这种情况下,FruitsRepository 将如下所示

package org.acme.spring.data.rest;

import java.util.Optional;

import org.springframework.data.repository.CrudRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.core.annotation.RestResource;

@RepositoryRestResource(exported = false, path = "/my-fruits")
public interface FruitsRepository extends CrudRepository<Fruit, Long> {

    @RestResource(exported = true)
    Optional<Fruit> findById(Long id);

    @RestResource(exported = true)
    Iterable<Fruit> findAll();
}

spring-data-rest 仅使用存储库方法的一个子集进行数据访问。为了自定义 REST 端点,注解正确的方法非常重要

REST 操作 CrudRepository PagingAndSortingRepository 和 JpaRepository

按 ID 获取

Optional<T> findById(ID id)

Optional<T> findById(ID id)

列表

Iterable<T> findAll()

Page<T> findAll(Pageable pageable)

创建

<S extends T> S save(S entity)

<S extends T> S save(S entity)

更新

<S extends T> S save(S entity)

<S extends T> S save(S entity)

删除

void deleteById(ID id)

void deleteById(ID id)

保护端点

此扩展将自动使用 jakarta.annotation.security 包中定义在您的资源接口上的安全注解

import jakarta.annotation.security.DenyAll;
import jakarta.annotation.security.RolesAllowed;

@DenyAll
public interface FruitResource extends CrudRepository<Fruit, Long> {
    @RolesAllowed("superuser")
    Iterable<Fruit> findAll();
}

请注意,此功能由此扩展在其底层使用的 Panache REST Data 扩展提供。因此,纯 Spring Boot 应用程序的行为可能不同。

当前不支持的内容

  • 仅支持上面列出的存储库方法。不支持任何其他标准或自定义方法。

  • 仅支持 exposedpathcollectionResourceRel 注解属性。

重要的技术说明

请注意,Quarkus 中的 Spring 支持不会启动 Spring Application Context,也没有运行任何 Spring 基础设施类。Spring 类和注解仅用于读取元数据和/或用作用户代码方法返回类型或参数类型。

相关内容