查看原文
其他

Spring Guides 译文:使用 REST 访问 JPA 数据

2017-10-17 社区翻译 SpringForAll社区

使用REST访问JPA数据

原文:[accessing-jpa-data-with-rest]https://spring.io/guides/gs/accessing-data-rest/ 译者: [strongant] https://github.com/strongant 校对: [汪志峰] https://github.com/maskwang520

本指南将指导你创建一个基于hypermedia的RESTful前端通过JPA访问关系型数据库的应用程序。

你会得到什么?

你会创建一个Spring应用,它能够使用Spring Data REST创建并且删除一个存储在数据库中的 Person对象。Spring Data REST具有Spring HATEOAS和Spring Data JPA的功能,并将它们自动组合在一起。

Spring Data REST 也支持 Spring Data Neo4j, Spring Data Gemfire 和 Spring Data MongoDB作为后端存储, 但这些并不是本指南的一部分。

你需要准备什么?

大约15分钟时间

一个喜欢的文本编辑器或者IDE

JDK 1.8 或 更高版本

Gradle 2.3+ 或 Maven 3.0+

你也可以直接导入代码到IDE:

Spring Tool Suite (STS)

IntelliJ IDEA

怎样完成指南?

像大多数 Spring 入门指南一样, 你可以从头开始,完成每一步, 或者你也可以绕过你熟悉的基本步骤再开始。 不管通过哪种方式,你最后都会得到一份可执行的代码。

如果从基础开始,你可以往下查看怎样使用 Gradle 构建项目。

如果已经熟悉跳过一些基本步骤,你可以:

  • 下载并解压源码库,或者通过 Git克隆:

    git clone https://github.com/spring-guides/gs-accessing-data-rest.git


  • 进入 gs-accessing-data-rest/initial目录


  • 跳过前面的部分创建一个域对象


当你完成之后,你可以在 gs-accessing-data-rest/complete根据代码检查下结果。

使用Gradle构建

首先你需要编写基础构建脚本。在构建 Spring 应用的时候,你可以使用任何你喜欢的系统来构建, 这里提供一份你可能需要用 Gradle 或者 Maven 构建的代码。 如果你两者都不是很熟悉, 你可以先去参考如何使用 Gradle 构建 Java 项目或者如何使用 Maven 构建 Java 项目。

创建以下目录结构

在你的项目根目录,创建如下的子目录结构; 例如,如果你使用的是*nix系统,你可以使用 mkdir-p src/main/java/hello:

  1. └── src

  2.    └── main

  3.        └── java

  4.            └── hello

创建Gradle构建文件

下面是一份初始化Gradle构建文件

build.gradle

  1. buildscript {

  2.    repositories {

  3.        mavenCentral()

  4.    }

  5.    dependencies {

  6.        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.7.RELEASE")

  7.    }

  8. }

  9. apply plugin: 'java'

  10. apply plugin: 'eclipse'

  11. apply plugin: 'idea'

  12. apply plugin: 'org.springframework.boot'

  13. jar {

  14.    baseName = 'gs-accessing-data-rest'

  15.    version = '0.1.0'

  16. }

  17. repositories {

  18.    mavenCentral()

  19. }

  20. sourceCompatibility = 1.8

  21. targetCompatibility = 1.8

  22. dependencies {

  23.    compile("org.springframework.boot:spring-boot-starter-data-rest")

  24.    compile("org.springframework.boot:spring-boot-starter-data-jpa")

  25.    compile("com.h2database:h2")

  26.    testCompile("org.springframework.boot:spring-boot-starter-test")

  27. }

Spring Boot gradle 插件 提供了很多非常方便的功能:

  • 将 classpath 里面所有用到的 jar 包构建成一个可执行的 JAR 文件,使得运行和发布你的服务变得更加便捷。

  • 搜索public static void main()方法并且将它标记为可执行类。

  • 提供了将内部依赖的版本都去匹配 Spring Boot 依赖的版本.你可以根据你的需要来重写版本,但是它默认提供给了 Spring Boot 依赖的版本。

使用Maven构建

首先,你需要设置一个基本的构建脚本。当使用Spring构建应用程序时,你可以使用任何你喜欢的构建系统,但是使用 Maven 构建的代码如下所示。如果您不熟悉Maven,请参阅使用Maven构建Java项目。

创建目录结构

在你选择的项目目录中,创建以下子目录结构;例如, 在Linux/Unix系统中使用如下命令: mkdir-p src/main/java/hello

  1. └── src

  2.    └── main

  3.        └── java

  4.            └── hello

pom.xml

  1. <?xml version="1.0" encoding="UTF-8"?>

  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  3.         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  4.    <modelVersion>4.0.0</modelVersion>

  5.    <groupId>org.springframework</groupId>

  6.    <artifactId>gs-accessing-data-rest</artifactId>

  7.    <version>0.1.0</version>

  8.    <parent>

  9.        <groupId>org.springframework.boot</groupId>

  10.        <artifactId>spring-boot-starter-parent</artifactId>

  11.        <version>1.5.7.RELEASE</version>

  12.    </parent>

  13.    <properties>

  14.        <java.version>1.8</java.version>

  15.    </properties>

  16.    <dependencies>

  17.        <dependency>

  18.            <groupId>org.springframework.boot</groupId>

  19.            <artifactId>spring-boot-starter-data-rest</artifactId>

  20.        </dependency>

  21.        <dependency>

  22.            <groupId>org.springframework.boot</groupId>

  23.            <artifactId>spring-boot-starter-data-jpa</artifactId>

  24.        </dependency>

  25.        <dependency>

  26.            <groupId>com.h2database</groupId>

  27.            <artifactId>h2</artifactId>

  28.        </dependency>

  29.        <dependency>

  30.            <groupId>org.springframework.boot</groupId>

  31.            <artifactId>spring-boot-starter-test</artifactId>

  32.            <scope>test</scope>

  33.        </dependency>

  34.    </dependencies>

  35.    <build>

  36.        <plugins>

  37.            <plugin>

  38.                <groupId>org.springframework.boot</groupId>

  39.                <artifactId>spring-boot-maven-plugin</artifactId>

  40.            </plugin>

  41.        </plugins>

  42.    </build>

  43.    <repositories>

  44.        <repository>

  45.            <id>spring-releases</id>

  46.            <url>https://repo.spring.io/libs-release</url>

  47.        </repository>

  48.    </repositories>

  49.    <pluginRepositories>

  50.        <pluginRepository>

  51.            <id>spring-releases</id>

  52.            <url>https://repo.spring.io/libs-release</url>

  53.        </pluginRepository>

  54.    </pluginRepositories>

  55. </project>

Spring Boot Maven 插件 提供了很多便捷的特性:

  • 它收集类路径上的所有jar包,并构建一个可运行的jar包,这样可以更方便地执行和发布你的服务。

  • 它寻找 publicstaticvoidmain() 方法来将其标记为一个可执行的类。

  • 它提供了一个内置的依赖解析器将应用与Spring Boot依赖的版本号进行匹配。你可以修改成任意的版本,但它将默认为Boot所选择了一组版本。

使用你的IDE构建

  • 阅读如何将本指南直接导入 Spring Tool Suite。

  • 阅读如何使用 IntelliJ IDEA 来构建。

创建一个域对象

创建一个新的 Person域对象。

src/main/java/hello/Person.java

  1. package hello;

  2. import javax.persistence.Entity;

  3. import javax.persistence.GeneratedValue;

  4. import javax.persistence.GenerationType;

  5. import javax.persistence.Id;

  6. @Entity

  7. public class Person {

  8.    @Id

  9.    @GeneratedValue(strategy = GenerationType.AUTO)

  10.    private long id;

  11.    private String firstName;

  12.    private String lastName;

  13.    public String getFirstName() {

  14.        return firstName;

  15.    }

  16.    public void setFirstName(String firstName) {

  17.        this.firstName = firstName;

  18.    }

  19.    public String getLastName() {

  20.        return lastName;

  21.    }

  22.    public void setLastName(String lastName) {

  23.        this.lastName = lastName;

  24.    }

  25. }

这个 Person 类有一个名和姓属性.还有一个id对象配置为自动生成,所以你不必处理。

创建一个 Person 仓库

接下来你需要创建一个简单的仓库。

src/main/java/hello/PersonRepository.java

  1. package hello;

  2. import java.util.List;

  3. import org.springframework.data.repository.PagingAndSortingRepository;

  4. import org.springframework.data.repository.query.Param;

  5. import org.springframework.data.rest.core.annotation.RepositoryRestResource;

  6. @RepositoryRestResource(collectionResourceRel = "people", path = "people")

  7. public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {

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

  9. }

这个存储库是一个接口,允许你对 Person对象执行各种操作。它在 Spring Data Commons中的PagingAndSortingRepository扩展这些操作数据共享。

在运行时,Spring Data REST自动创建该接口的一个实现。然后它会使用Spring MVC @RepositoryRestResource注释直接创建RESTful端点 /people

@RepositoryRestResource不是存储仓库必需导出的。它仅用于改变导出的细节,例如使用 /people而不是默认的 /people值。

在这里你也定义了一个定制的查询检索一个‘Person’的对象列表基于lastName。您将进一步在本指南看到如何调用它。

使应用可运行

尽管可以将本服务打包为传统的 WAR 文件,然后部署在外部的应用程序服务器上,下面展示一种更简便的方法,即创建独立的应用程序。这种方法将本服务打包为单独的、可直接运行的JAR文件,这个jar文件由传统的 main()函数方法驱动。使用这种打包方法,可以使用Spring提供的嵌入式 Tomcat servlet容器作为运行时(HTTP runtime), 而不是直接部署外部实例中。

src/main/java/hello/Application.java

  1. package hello;

  2. import org.springframework.boot.SpringApplication;

  3. import org.springframework.boot.autoconfigure.SpringBootApplication;

  4. @SpringBootApplication

  5. public class Application {

  6.    public static void main(String[] args) {

  7.        SpringApplication.run(Application.class, args);

  8.    }

  9. }

@SpringBootApplication作为一个方便使用的注解,提供了如下的功能:

  • @Configuration表明使用该注解的类是应用程序上下文(Applicaiton Context)中Bean定义的来源。

  • @EnableAutoConfiguration注解根据classpath的配置、其他bean的定义或者不同的属性设置(property settings)等条件,使Spring Boot自动加入所需的bean。

  • 对于Spring MVC应用,通常需要加入 @EnableWebMvc注解,但是当spring-webmvc 存在于classpath中时,Spring Boot自动加入该注解。该注解将当前应用标记为web应用,并激活web应用的关键行为,例如开启 DispatcherServlet

  • @ComponentScan注解使Spring在 hello包(package)中搜索其他的组件、配置(configurations)和服务(service),在本例中,spring会搜索到控制器(controllers)。

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文件,其中包含所有必需的依赖关系,类,和资源,并运行。这使得在整个开发生命周期中非常容易,跨不同的环境,版本等传输和部署服务成为一个应用程序。等等。

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

  1. java -jar build/libs/gs-accessing-data-rest-0.1.0.jar

如果您使用 Maven,则可以使用该命令 ./mvnw spring-boot:run。或者您可以使用 JAR 文件来构建 ./mvnw cleanpackage。然后可以运行 JAR 文件::

  1. java -jar target/gs-accessing-data-rest-0.1.0.jar

上面的过程将创建一个可运行的JAR。您也可以选择 构建一个WAR文件。

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

测试应用程序

现在应用程序正在运行,您可以测试它。你可以使用任何你喜欢的REST客户端。下面使用*nix系统工具 curl进行测试。

首先你想看到顶级服务。

  1. $ curl http://localhost:8080

  2. {

  3.  "_links" : {

  4.    "people" : {

  5.      "href" : "http://localhost:8080/people{?page,size,sort}",

  6.      "templated" : true

  7.    }

  8.  }

  9. }

在这里,您可以首先了解此服务器提供的内容。有一个people链接位于http://localhost:8080/people。它包含了一些比如 ?page, ?size, 和 ?sort的选项。

Spring Data REST 使用 HAL format 提供 JSON 输出。它是灵活的,并提供了一种方便的方式来提供与所提供数据相邻的联系。

  1. $ curl http://localhost:8080/people

  2. {

  3.  "_links" : {

  4.    "self" : {

  5.      "href" : "http://localhost:8080/people{?page,size,sort}",

  6.      "templated" : true

  7.    },

  8.    "search" : {

  9.      "href" : "http://localhost:8080/people/search"

  10.    }

  11.  },

  12.  "page" : {

  13.    "size" : 20,

  14.    "totalElements" : 0,

  15.    "totalPages" : 0,

  16.    "number" : 0

  17.  }

  18. }

目前没有元素,因此没有页面。创建一个新 Person!

  1. $ curl -i -X POST -H "Content-Type:application/json" -d "{  \"firstName\" : \"Frodo\",  \"lastName\" : \"Baggins\" }" http://localhost:8080/people

  2. HTTP/1.1 201 Created

  3. Server: Apache-Coyote/1.1

  4. Location: http://localhost:8080/people/1

  5. Content-Length: 0

  6. Date: Wed, 26 Feb 2014 20:26:55 GMT

  • -i 确保你可以看到响应消息头。这个 URI 指向显示新创建的 Person 。

  • -X POST 发信号通知用于创建新条目的POST

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

  • -d"{ \"firstName\" : \"Frodo\", \"lastName\" : \"Baggins\" }" 是正在发送的数据。数据中的双引号需要转义为 \“

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

从这里您可以查询所有人:

  1. $ curl http://localhost:8080/people

  2. {

  3.  "_links" : {

  4.    "self" : {

  5.      "href" : "http://localhost:8080/people{?page,size,sort}",

  6.      "templated" : true

  7.    },

  8.    "search" : {

  9.      "href" : "http://localhost:8080/people/search"

  10.    }

  11.  },

  12.  "_embedded" : {

  13.    "persons" : [ {

  14.      "firstName" : "Frodo",

  15.      "lastName" : "Baggins",

  16.      "_links" : {

  17.        "self" : {

  18.          "href" : "http://localhost:8080/people/1"

  19.        }

  20.      }

  21.    } ]

  22.  },

  23.  "page" : {

  24.    "size" : 20,

  25.    "totalElements" : 1,

  26.    "totalPages" : 1,

  27.    "number" : 0

  28.  }

  29. }

persons对象包含一个包含Frodo的列表。注意它如何包括一个self链接。 Spring Data REST还使用[Evo Inflector](http://www.atteo.org/2011/12/12/Evo-Inflector.html)将分组的实体名称复数化。

您可以直接查询单个记录:

  1. $ curl http://localhost:8080/people/1

  2. {

  3.  "firstName" : "Frodo",

  4.  "lastName" : "Baggins",

  5.  "_links" : {

  6.    "self" : {

  7.      "href" : "http://localhost:8080/people/1"

  8.    }

  9.  }

  10. }

这可能看起来纯粹是基于网络的,但在幕后,有一个H2关系数据库。在生产中,您可能会使用一个真正的数据库,像PostgreSQL。

在本指南中,只有一个域对象。使用更复杂的系统,其中域对象彼此相关,Spring Data REST将呈现其他链接,以帮助导航到连接的记录。

查找所有自定义查询

  1. $ curl http://localhost:8080/people/search

  2. {

  3.  "_links" : {

  4.    "findByLastName" : {

  5.      "href" : "http://localhost:8080/people/search/findByLastName{?name}",

  6.      "templated" : true

  7.    }

  8.  }

  9. }

您可以查看查询的URL,包括HTTP查询参数 name。如果你注意到,这匹配嵌入在界面中的 @Param(“name”)注解。

要使用 findByLastName查询,请执行以下操作:

  1. $ curl http://localhost:8080/people/search/findByLastName?name=Baggins

  2. {

  3.  "_embedded" : {

  4.    "persons" : [ {

  5.      "firstName" : "Frodo",

  6.      "lastName" : "Baggins",

  7.      "_links" : {

  8.        "self" : {

  9.          "href" : "http://localhost:8080/people/1"

  10.        }

  11.      }

  12.    } ]

  13.  }

  14. }

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

您也可以发出 PUTPATCHDELETEREST调用来替换,更新或删除现有记录。

  1. $ curl -X PUT -H "Content-Type:application/json" -d "{ \"firstName\": \"Bilbo\", \"lastName\": \"Baggins\" }" http://localhost:8080/people/1

  2. $ curl http://localhost:8080/people/1

  3. {

  4.  "firstName" : "Bilbo",

  5.  "lastName" : "Baggins",

  6.  "_links" : {

  7.    "self" : {

  8.      "href" : "http://localhost:8080/people/1"

  9.    }

  10.  }

  11. }

  1. $ curl -X PATCH -H "Content-Type:application/json" -d "{ \"firstName\": \"Bilbo Jr.\" }" http://localhost:8080/people/1

  2. $ curl http://localhost:8080/people/1

  3. {

  4.  "firstName" : "Bilbo Jr.",

  5.  "lastName" : "Baggins",

  6.  "_links" : {

  7.    "self" : {

  8.      "href" : "http://localhost:8080/people/1"

  9.    }

  10.  }

  11. }

PUT替换整个记录。未提供的字段将被替换为null。 PATCH可用于更新某一项的子集。

您可以删除记录:

  1. $ curl -X DELETE http://localhost:8080/people/1

  2. $ curl http://localhost:8080/people

  3. {

  4.  "_links" : {

  5.    "self" : {

  6.      "href" : "http://localhost:8080/people{?page,size,sort}",

  7.      "templated" : true

  8.    },

  9.    "search" : {

  10.      "href" : "http://localhost:8080/people/search"

  11.    }

  12.  },

  13.  "page" : {

  14.    "size" : 20,

  15.    "totalElements" : 0,

  16.    "totalPages" : 0,

  17.    "number" : 0

  18.  }

  19. }

这个接口驱动的hypermedia一个非常方便的一面是如何使用curl(或您正在使用的任何REST客户端)发现所有RESTful端点。没有必要与客户交换正式的合同或接口文件。

总结

恭喜你!您刚刚开发了一个使用基于hypermedia 的RESTful前端和基于JPA的后端应用。

本文由spring4all.com翻译小分队创作,采用知识共享-署名-非商业性使用-相同方式共享 4.0 国际 许可 协议进行许可。


翻译公益


喜欢 Spring 的

喜欢翻译的

欢迎加我微信 13958686678

备注:翻译




最好的赞赏

就是你的关注


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存