本指南将引导您完成创建通过基于超媒体的RESTful前端访问关系JPA Data的应用程序的过程。
您将构建一个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作为后端数据存储,但这些不属于本指南的一部分。
程序结构
└── src
└── main
└── java
└── hello
pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-accessing-data-rest</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Spring Boot将会你做如下的事:
- 将 classpath 里面所有用到的jar包构建成一个可执行的 JAR 文件,方便执行你的程序
- 搜索public static void main()方法并且将它当作可执行类
- 根据springboot版本,去查找相应的依赖类版本,当然你可以定义其它版本。
创建域对象
创建一个新的域对象来代表一个人。
src/main/java/hello/Person.java
package hello;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.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类有 firstName和lastName。还有一个id对象被配置为自动生成,因此您不必处理它。
创建Person存储库
接下来,您需要创建一个简单的存储库:
src/main/java/hello/PersonRepository.java
package hello;
import java.util.List;
import org.springframework.data.repository.PagingAndSortingRepository;
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> {
List<Person> findByLastName(@Param("name") String name);
}
此存储库是一个接口,允许您执行涉及Person对象的各种操作。它通过扩展Spring Data Commons中定义的PagingAndSortingRepository接口来获得这些操作。
在运行时,Spring Data Rest将自动创建该接口的实现。然后它将使用@RepositoryRestResource 注释来指导spring mvc在/people处创建RESTful端点。
要导出存储库,不需要@RepositoryRestoreSource。@RepositoryRestoreSource仅用于更改导出详细信息,例如使用/people而不是/persons的默认值。
在这里,您还定义了一个自定义查询来检索基于lastName的Person对象列表。您将在本指南中进一步了解如何调用它。
创建Application
src/main/java/hello/Application.java
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
SpringBoot自动使用Spring Data JPA来创建一个具体的PersonRepository实现,并将其配置为使用JPA与后端内存数据库通信。
Spring Data Rest构建在SpringMVC之上。它创建了一个SpringMVC控制器、JSON转换器和其他提供一个RESTful前端所需的bean的集合。这些组件链接到Spring Data JPA后端。使用SpringBoot,这一切都是自动配置的;如果您想调查这是如何工作的,可以从Spring Data Rest中的RepositoryRestmvcConfiguration开始。
运行及测试程序
运行Application.java,下面使用curl来测试(也可以在浏览器):
首先,您希望看到顶级服务:
$ curl http://localhost:8080
{
"_links" : {
"people" : {
"href" : "http://localhost:8080/people{?page,size,sort}",
"templated" : true
}
}
}
这里,您可以第一眼看到这个服务器必须提供什么。在http://localhost:8080/people上有一个people链接。它有一些选择,比如?第几页,?获得多少个,还有?是否排序。
Spring Data Rest使用HAL格式进行JSON输出。它是灵活的,并且提供了一种方便的方式来提供与所服务的数据相邻的链接。
$ curl http://localhost:8080/people
{
"_links" : {
"self" : {
"href" : "http://localhost:8080/people{?page,size,sort}",
"templated" : true
},
"search" : {
"href" : "http://localhost:8080/people/search"
}
},
"page" : {
"size" : 20,
"totalElements" : 0,
"totalPages" : 0,
"number" : 0
}
}
当前没有元素,因此没有页面。是时候创造一个新的Person了!
$ curl -i -X POST -H "Content-Type:application/json" -d '{"firstName": "Frodo", "lastName": "Baggins"}' http://localhost:8080/people
HTTP/1.1 201 Created
Server: Apache-Coyote/1.1
Location: http://localhost:8080/people/1
Content-Length: 0
Date: Wed, 26 Feb 2014 20:26:55 GMT
-i 确保您可以看到包含报头的响应消息,显示新创建Person的URI
-X post向这个post发出信号,用于创建一个新条目
-H "Content-Type:application/json" 设置内容类型,以便应用程序知道有效负载包含一个json对象。
-d '{"firstName": "Frodo", "lastName": "Baggins"}' 是要发送的数据
请注意,上一个 POST操作如何包含位置头部。这包含新创建的资源的URI。Spring Data Rest在RepositoryRestConfiguration.setReturnBodyOnCreate(…)和setReturnBodyOnUpdate(…)上也有两种方法,您可以使用它们来配置框架,以便立即返回刚创建的资源的表示形式。
RepositoryRestConfiguration.setReturnBodyForPutAndPost(…) 是一种快捷方法,用于为创建和更新启用表示响应。
从中可以查询所有人:
$ curl http://localhost:8080/people
{
"_links" : {
"self" : {
"href" : "http://localhost:8080/people{?page,size,sort}",
"templated" : true
},
"search" : {
"href" : "http://localhost:8080/people/search"
}
},
"_embedded" : {
"persons" : [ {
"firstName" : "Frodo",
"lastName" : "Baggins",
"_links" : {
"self" : {
"href" : "http://localhost:8080/people/1"
}
}
} ]
},
"page" : {
"size" : 20,
"totalElements" : 1,
"totalPages" : 1,
"number" : 0
}
}
Persons对象包含一个带有Frodo的列表。注意它如何包含一个自链接。Spring Data Rest还使用 Evo Inflector 将分组实体的名称复数化。
您可以直接查询单个记录:
$ curl http://localhost:8080/people/1
{
"firstName" : "Frodo",
"lastName" : "Baggins",
"_links" : {
"self" : {
"href" : "http://localhost:8080/people/1"
}
}
}
这可能纯粹是基于Web的,但在内部,是有一个H2关系数据库。在生产中,您可能会使用真正的,如MYSQL。
在本指南中,只有一个域对象。在域对象相互关联的更复杂的系统中,Spring DataRest将呈现其他链接,以帮助导航到连接的记录。
查找所有自定义查询:
$ curl http://localhost:8080/people/search
{
"_links" : {
"findByLastName" : {
"href" : "http://localhost:8080/people/search/findByLastName{?name}",
"templated" : true
}
}
}
您可以看到查询的URL,包括HTTP查询参数name。如果您注意到,这与嵌入在接口中的@Param("name") 注释匹配。
要使用findByLastName查询,请执行以下操作
$ curl http://localhost:8080/people/search/findByLastName?name=Baggins
{
"_embedded" : {
"persons" : [ {
"firstName" : "Frodo",
"lastName" : "Baggins",
"_links" : {
"self" : {
"href" : "http://localhost:8080/people/1"
}
}
} ]
}
}
因为您在代码中将其定义为返回List<Person>
,所以它将返回所有结果。如果您定义了它只返回Person,它将选择要返回的Person对象之一。因为这可能是不可预测的,所以对于可以返回多个条目的查询,您可能不希望这样做。
您还可以发出PUT、PATCH和 DELETE REST 调用来替换、更新或删除现有记录。
$ curl -X PUT -H "Content-Type:application/json" -d '{"firstName": "Bilbo", "lastName": "Baggins"}' http://localhost:8080/people/1
$ curl http://localhost:8080/people/1
{
"firstName" : "Bilbo",
"lastName" : "Baggins",
"_links" : {
"self" : {
"href" : "http://localhost:8080/people/1"
}
}
}
$ curl -X PATCH -H "Content-Type:application/json" -d '{"firstName": "Bilbo Jr."}' http://localhost:8080/people/1
$ curl http://localhost:8080/people/1
{
"firstName" : "Bilbo Jr.",
"lastName" : "Baggins",
"_links" : {
"self" : {
"href" : "http://localhost:8080/people/1"
}
}
}
PUT替换整个记录。未提供的字段将替换为null。PATCH程序可用于更新项目.
您可以删除记录:
$ curl -X DELETE http://localhost:8080/people/1
$ curl http://localhost:8080/people
{
"_links" : {
"self" : {
"href" : "http://localhost:8080/people{?page,size,sort}",
"templated" : true
},
"search" : {
"href" : "http://localhost:8080/people/search"
}
},
"page" : {
"size" : 20,
"totalElements" : 0,
"totalPages" : 0,
"number" : 0
}
}
这个超媒体驱动接口的一个非常好的方面是如何使用curl(或者您使用的任何REST客户端)发现所有的RESTful端点,不需要提供正式接口文档。