1. Overview
1.概述
LinkRest is an open-source framework for building data-driven REST web services. It’s built on top of JAX-RS and Apache Cayenne ORM, and uses an HTTP/JSON-based message protocol.
LinkRest是一个开源的框架,用于构建数据驱动的REST Web服务。它建立在JAX-RS和Apache Cayenne ORM之上,并使用基于HTTP/JSON的消息协议。
Basically, this framework is meant to provide an easy way of exposing our data store on the web.
基本上,这个框架是为了提供一种简单的方式,将我们的数据存储暴露在网络上。
In the following sections, we’ll take a look at how we can build a REST web service to access a data model using LinkRest.
在下面的章节中,我们将看看如何使用LinkRest建立一个REST网络服务来访问数据模型。
2. Maven Dependencies
2.Maven的依赖性
To start working with the library, first we need to add the link-rest dependency:
要开始使用该库,首先我们需要添加link-rest依赖性。
<dependency>
<groupId>com.nhl.link.rest</groupId>
<artifactId>link-rest</artifactId>
<version>2.9</version>
</dependency>
This also brings in the cayenne-server artifact.
这也带来了cayenne-server构件。
Additionally, we’ll use Jersey as the JAX-RS implementation, so we need to add the jersey-container-servlet dependency, as well as jersey-media-moxy for serializing JSON responses:
此外,我们将使用Jersey作为JAX-RS的实现,所以我们需要添加jersey-container-servlet依赖,以及jersey-media-moxy用于序列化JSON响应。
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.25.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>2.25.1</version>
</dependency>
For our example, we’ll be working with an in-memory H2 database as it’s easier to set up; as a consequence, we’ll also add h2 :
在我们的例子中,我们将使用一个内存H2数据库,因为它更容易设置;因此,我们也将添加h2 。
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.196</version>
</dependency>
3. Cayenne Data Model
3.Cayenne数据模型
The data model we’ll be working with contains a Department and an Employee entity that represents a one-to-many relationship:
我们要处理的数据模型包含一个Department和一个Employee实体,代表一对多的关系。
As mentioned, LinkRest works with data objects generated using Apache Cayenne ORM. Working with Cayenne is not the main subject of this article, so for more information check out the Apache Cayenne documentation.
如前所述,LinkRest可与使用Apache Cayenne ORM生成的数据对象一起工作。与Cayenne一起工作并不是本文的主要主题,因此要想了解更多信息,请查看Apache Cayenne文档。
We’ll save the Cayenne project in a cayenne-linkrest-project.xml file.
我们将把Cayenne项目保存在一个cayenne-linkrest-project.xml文件中。
After running the cayenne-maven-plugin, this will generate two _Department and _Employee abstract classes – which will extend the CayenneDataObject class, as well as two concrete classes derived from them, Department and Employee.
运行cayenne-maven-plugin后,将生成两个_Department和_Employee抽象类–它们将扩展CayenneDataObject类,以及由它们派生的两个具体类,Department和Employee。
These latter classes are the ones that we can customize and use with LinkRest.
后面的这些类是我们可以定制并使用的LinkRest。
4. LinkRest Application Startup
4.LinkRest应用程序启动
In the next section, we’re going to be writing and testing REST endpoints, so to be able to run them we need to setup our runtime.
在下一节,我们将编写和测试REST端点,所以为了能够运行它们,我们需要设置我们的运行时。
Since we’re using Jersey as the JAX-RS implementation, let’s add a class that extends ResourceConfig and specifies the package that will hold the classes in which we define the REST endpoints:
由于我们使用Jersey作为JAX-RS的实现,让我们添加一个扩展ResourceConfig的类,并指定容纳我们定义REST端点的类的包。
@ApplicationPath("/linkrest")
public class LinkRestApplication extends ResourceConfig {
public LinkRestApplication() {
packages("com.baeldung.linkrest.apis");
// load linkrest runtime
}
}
In the same constructor, we need to build and register the LinkRestRuntime to the Jersey container. This class is based on first loading the CayenneRuntime:
在同一个构造函数中,我们需要建立并注册LinkRestRuntime到Jersey容器。这个类是基于首先加载CayenneRuntime。
ServerRuntime cayenneRuntime = ServerRuntime.builder()
.addConfig("cayenne-linkrest-project.xml")
.build();
LinkRestRuntime lrRuntime = LinkRestBuilder.build(cayenneRuntime);
super.register(lrRuntime);
Finally, we need to add the class to the web.xml:
最后,我们需要将该类添加到 web.xml。
<servlet>
<servlet-name>linkrest</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.baeldung.LinkRestApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>linkrest</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
5. REST Resources
5.REST资源
Now that we’ve got our model classes, we can start writing REST resources.
现在我们已经有了我们的模型类,我们可以开始编写REST资源。
The REST endpoints are created using standard JAX-RS annotations, while the response is built using the LinkRest class.
REST端点是使用标准的JAX-RS注释创建的,而响应是使用LinkRest类构建的。
Our example will consist of writing a series of CRUD endpoints that access the /department URL using different HTTP methods.
我们的例子将包括编写一系列CRUD端点,使用不同的HTTP方法访问/department URL。
First, let’s create the DepartmentResource class, which is mapped to /department:
首先,让我们创建DepartmentResource类,它被映射到/department。
@Path("department")
@Produces(MediaType.APPLICATION_JSON)
public class DepartmentResource {
@Context
private Configuration config;
// ...
}
The LinkRest class needs an instance of the JAX-RS Configuration class, which is injected using the Context annotation, also provided by JAX-RS.
LinkRest类需要一个JAX-RS配置类的实例,该实例使用Context注解注入,该注解也由JAX-RS提供。
Next, let’s continue writing each of the endpoints that access Department objects.
接下来,让我们继续编写访问Department对象的每个端点。
5.1. Creating Entities Using POST
5.1.使用POST创建实体
To create an entity, the LinkRest class provides the create() method which returns an UpdateBuilder object:
为了创建一个实体,LinkRest类提供了create()方法,它返回一个UpdateBuilder对象。
@POST
public SimpleResponse create(String data) {
return LinkRest.create(Department.class, config).sync(data);
}
The data parameter can be either a single JSON object representing a Department or an array of objects. This parameter is sent to the UpdateBuilder using the sync() method to create one or more objects and insert the records into the database, after which the method returns a SimpleResponse.
数据参数可以是一个代表部门的单一JSON对象或一个对象阵列。这个参数被发送到UpdateBuilder,使用sync()方法创建一个或多个对象并将记录插入数据库,之后该方法返回一个SimpleResponse。
The library defines 3 additional formats for responses:
该库为响应定义了3种额外的格式。
- DataResponse<T> – a response that represents a collection of T
- MetadataResponse<T> – contains metadata information about the type
- SimpleResponse – an object that contains two success and message attributes
Next, let’s use curl to add a Department record to the database:
接下来,让我们使用curl向数据库添加一条Department记录。
curl -i -X POST -H "Content-Type:application/json"
-d "{"name":"IT"}" http://localhost:8080/linkrest/department
As a result, the command returns the status 201 Created and a success attribute:
结果是,命令返回状态201 Created和success属性。
{"success":true}
We can also create multiple objects by sending a JSON array:
我们还可以通过发送一个JSON数组来创建多个对象。
curl -i -X POST -H "Content-Type:application/json"
-d "[{"name":"HR"},{"name":"Marketing"}]"
http://localhost:8080/linkrest/department
5.2. Reading Entities Using GET
5.2.使用GET读取实体
The main method for querying objects is the select() method from the LinkRest class. This returns a SelectBuilder object which we can use to chain additional querying or filtering methods.
查询对象的主要方法是select()方法,来自LinkRest类。它返回一个SelectBuilder对象,我们可以用它来连锁其他查询或过滤方法。
Let’s create an endpoint in the DepartmentResource class that returns all the Department objects in the database:
让我们在DepartmentResource类中创建一个端点,返回数据库中所有的Department对象。
@GET
public DataResponse<Department> getAll(@Context UriInfo uriInfo) {
return LinkRest.select(Department.class, config).uri(uriInfo).get();
}
The uri() call sets the request information for the SelectBuilder, while get() returns a collection of Departments wrapped as a DataResponse<Department> object.
uri()调用为SelectBuilder设置请求信息,而get()返回一个Departments的集合,该集合被包装成DataResponse<Department>对象。
Let’s take a look at the departments we added before using this endpoint:
让我们看看我们在使用这个端点之前添加的部门。
curl -i -X GET http://localhost:8080/linkrest/department
The response takes the form of a JSON object with a data array and a total property:
响应采用JSON对象的形式,有一个data数组和一个total属性。
{"data":[
{"id":200,"name":"IT"},
{"id":201,"name":"Marketing"},
{"id":202,"name":"HR"}
],
"total":3}
Alternatively, to retrieve a collection of objects, we can also get back a single object by using the getOne() instead of get().
另外,为了检索一个对象的集合,我们也可以通过使用 getOne()而不是get()来取回一个单一对象。
Let’s add an endpoint mapped to /department/{departmentId} that returns an object with a given id. For this purpose, we’ll filter the records using the byId() method:
让我们添加一个映射到/department/{departmentId}的端点,返回一个具有给定id的对象。为此,我们将使用byId()方法过滤记录。
@GET
@Path("{id}")
public DataResponse<Department> getOne(@PathParam("id") int id,
@Context UriInfo uriInfo) {
return LinkRest.select(Department.class, config)
.byId(id).uri(uriInfo).getOne();
}
Then, we can send a GET request to this URL:
然后,我们可以向这个URL发送一个GET请求。
curl -i -X GET http://localhost:8080/linkrest/department/200
The result is a data array with one element:
结果是一个有一个元素的data数组。
{"data":[{"id":200,"name":"IT"}],"total":1}
5.3. Updating Entities Using PUT
5.3.使用PUT更新实体
To update records, we can use the update() or createOrUpdate() method. The latter will update records if they exist, or create them if they do not:
要更新记录,我们可以使用update()或createOrUpdate()方法。后者将更新存在的记录,如果不存在,则创建它们。
@PUT
public SimpleResponse createOrUpdate(String data) {
return LinkRest.createOrUpdate(Department.class, config).sync(data);
}
Similarly to the previous sections, the data argument can be a single object or an array of objects.
与前几节类似,data参数可以是一个单一的对象或一个对象的数组。
Let’s update one of the previously added departments:
让我们更新一下以前添加的一个部门。
curl -i -X PUT -H "Content-Type:application/json"
-d "{"id":202,"name":"Human Resources"}"
http://localhost:8080/linkrest/department
This returns a JSON object with a success or error message. Afterwards, we can verify if the name of the department with ID 202 was changed:
这将返回一个带有成功或错误信息的JSON对象。之后,我们可以验证ID为202的部门的名称是否被改变。
curl -i -X GET http://localhost:8080/linkrest/department/202
Sure enough, this command returns the object with the new name:
果然,这个命令返回了带有新名称的对象。
{"data":[
{"id":202,"name":"Human Resources"}
],
"total":1}
5.4. Removing Entities Using DELETE
5.4.使用DELETE删除实体
And, to remove an object, we can call the delete() method which creates a DeleteBuilder, then specify the primary key of the object we want to delete by using the id() method:
而且,为了删除一个对象,我们可以调用delete()方法,该方法创建一个DeleteBuilder,然后通过使用id()方法指定我们要删除的对象的主键。
@DELETE
@Path("{id}")
public SimpleResponse delete(@PathParam("id") int id) {
return LinkRest.delete(Department.class, config).id(id).delete();
}
Then we can call this endpoint using curl:
然后我们可以使用curl调用这个端点。
curl -i -X DELETE http://localhost:8080/linkrest/department/202
5.5. Working With Relationships Between Entities
5.5.处理实体之间的关系
LinkRest also contains methods that make working with relationships between objects easier.
LinkRest还包含一些方法,使处理对象之间的关系更加容易。
Since Department has a one-to-many relationship to Employee, let’s add a /department/{departmentId}/employees endpoint that accesses an EmployeeSubResource class:
由于Department与Employee有一对多的关系,让我们添加一个/department/{departmentId}/employees端点,访问一个EmployeeSubResource类。
@Path("{id}/employees")
public EmployeeSubResource getEmployees(
@PathParam("id") int id, @Context UriInfo uriInfo) {
return new EmployeeSubResource(id);
}
The EmployeeSubResource class corresponds to a department so it’ll have a constructor that sets a department id, as well as the Configuration instance:
EmployeeSubResource类对应于一个部门,所以它将有一个构造函数来设置一个部门ID,以及Configuration实例。
@Produces(MediaType.APPLICATION_JSON)
public class EmployeeSubResource {
private Configuration config;
private int departmentId;
public EmployeeSubResource(int departmentId, Configuration configuration) {
this.departmentId = departmentId;
this.config = config;
}
public EmployeeSubResource() {
}
}
Do note that a default constructor is necessary for the object to be serialized as a JSON object.
请注意,一个默认的构造函数对于对象被序列化为JSON对象是必要的。
Next, let’s define an endpoint that retrieves all the employees from a department:
接下来,让我们定义一个端点,检索一个部门的所有雇员。
@GET
public DataResponse<Employee> getAll(@Context UriInfo uriInfo) {
return LinkRest.select(Employee.class, config)
.toManyParent(Department.class, departmentId, Department.EMPLOYEES)
.uri(uriInfo).get();
}
In this example, we’ve used the toManyParent() method of the SelectBuilder to query only the objects with a given parent.
在这个例子中,我们使用了toManyParent()方法的SelectBuilder,只查询具有指定父级的对象。
The endpoints for the POST, PUT, DELETE methods can be created in a similar manner.
POST、PUT、DELETE方法的端点可以用类似的方式创建。
To add employees to a department, we can call the departments/{departmentId}/employees endpoint with POST method:
要将员工添加到一个部门,我们可以用POST方法调用departments/{departmentId}/employees端点。
curl -i -X POST -H "Content-Type:application/json"
-d "{"name":"John"}" http://localhost:8080/linkrest/department/200/employees
Then, let’s send a GET request to view the employees of the department:
然后,让我们发送一个GET请求来查看该部门的雇员。
curl -i -X GET "http://localhost:8080/linkrest/department/200/employees
This returns a JSON object with a data array::
这将返回一个带有数据数组的JSON对象:。
{"data":[{"id":200,"name":"John"}],"total":1}
6. Customizing the Response With Request Parameters
6.用请求参数定制响应
LinkRest provides an easy way to customize the response by adding specific parameters to the request. These can be used to filter, sort, paginate or restrict the set of attributes of the result set.
LinkRest提供了一种简单的方法,通过向请求添加特定参数来定制响应。这些参数可用于过滤、排序、分页或限制结果集的属性集合。
6.1. Filtering
6.1.筛选
We can filter the results based on the values of attributes by using the cayenneExp parameter. As the name suggests, this follows the format of Cayenne expressions.
我们可以通过使用cayenneExp参数,根据属性值过滤结果。顾名思义,它遵循Cayenne表达式的格式。
Let’s send a request that only returns departments with the name “IT”:
让我们发送一个请求,只返回名称为 “IT “的部门。
curl -i -X GET http://localhost:8080/linkrest/department?cayenneExp=name='IT'
6.2. Sorting
6.2.分拣
The parameters to add for sorting a set of results are sort and dir. The first of these specifies the attribute to sort by, and the second the direction of sorting.
为对一组结果进行排序而添加的参数是sort和dir。其中第一个参数指定要排序的属性,第二个参数指定排序的方向。
Let’s see all the departments sorted by name:
让我们看看所有部门按名称排序的情况。
curl -i -X GET "http://localhost:8080/linkrest/department?sort=name&dir=ASC"
6.3. Pagination
6.3.分页
The library supports pagination by adding the start and limit parameters:
该库通过添加start和limit参数支持分页。
curl -i -X GET "http://localhost:8080/linkrest/department?start=0&limit=2
6.4. Selecting Attributes
6.4.选择属性
Using the include and exclude parameters, we can control which attributes or relationships are returned in the result.
使用include和exclude参数,我们可以控制哪些属性或关系会在结果中返回。
For example, let’s send a request that only displays the names of the departments:
例如,让我们发送一个只显示部门名称的请求。
curl -i -X GET "http://localhost:8080/linkrest/department?include=name
To show the names as well as the employees of a department with only their name we can use the include attribute twice:
要显示一个部门的名字以及员工,只显示他们的名字,我们可以使用include属性两次。
curl -i -X GET "http://localhost:8080/linkrest/department?include=name&include=employees.name
7. Conclusion
7.结论
In the article, we have shown how we can quickly expose a data model through REST endpoints by using the LinkRest framework.
在文章中,我们已经展示了如何通过使用LinkRest框架,通过REST端点快速暴露数据模型。
The full source code of the examples can be found over on GitHub.
示例的完整源代码可以在GitHub上找到over。