1. 概述
LinkRest 是一個開源框架,用於構建基於數據的 REST Web 服務。它基於 JAX-RS 和 Apache Cayenne ORM,並使用 HTTP/JSON 消息協議。
基本上,這個框架旨在提供一種便捷的方式來將我們的數據存儲暴露到 Web 上。
在後續部分,我們將探討如何使用 LinkRest 構建一個 REST Web 服務,以訪問數據模型。
2. Maven 依賴項
為了開始使用該庫,首先我們需要添加 link-rest 依賴項:
<dependency>
<groupId>com.nhl.link.rest</groupId>
<artifactId>link-rest</artifactId>
<version>2.9</version>
</dependency>這也會引入 cayenne-server 構件。
此外,我們還將使用 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>為了我們的示例,我們將使用內存中的 H2數據庫,因為它更容易設置;因此,我們還將添加 h2:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.196</version>
</dependency>3. Cayenne 數據模型
我們將使用的數據模型包含一個 部門 (Department) 和一個 員工 (Employee) 實體,它們代表着一對多關係:
正如前面提到的,LinkRest 與使用 Apache Cayenne ORM 生成的數據對象一起工作。 使用 Cayenne 不是本文的主要內容,有關更多信息,請參閲 Apache Cayenne 文檔。
我們將保存 Cayenne 項目到一個名為 cayenne-linkrest-project.xml 的文件中。
運行 cayenne-maven-plugin 後,它將生成兩個 _Department 和 _Employee 抽象類,這些類將繼承 CayenneDataObject 類,以及由此派生的兩個具體類,Department 和 Employee。
這些後來的類是我們可自定義和使用與 LinkRest 配合使用的類。
4. LinkRest 應用程序啓動
在下一部分,我們將編寫和測試 REST 端點,因此為了能夠運行它們,我們需要設置我們的運行時環境。
由於我們使用 Jersey 作為 JAX-RS 實現,我們將添加一個擴展 ResourceConfig 的類,並指定包含我們定義 REST 端點類的包:
@ApplicationPath("/linkrest")
public class LinkRestApplication extends ResourceConfig {
public LinkRestApplication() {
packages("com.baeldung.linkrest.apis");
// load linkrest runtime
}
}在同一個構造函數中,我們需要構建並註冊 LinkRestRuntime 到 Jersey 容器。該類基於首先加載 CayenneRuntime:
ServerRuntime cayenneRuntime = ServerRuntime.builder()
.addConfig("cayenne-linkrest-project.xml")
.build();
LinkRestRuntime lrRuntime = LinkRestBuilder.build(cayenneRuntime);
super.register(lrRuntime);最後,我們需要將該類添加到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 資源
現在我們已經定義了模型類,就可以開始編寫 REST 資源。
REST 端點使用標準的 JAX-RS 註解創建,響應構建則使用 LinkRest 類。
我們的示例將包含編寫一系列 CRUD 端點,這些端點通過不同的 HTTP 方法訪問 /department URL:
首先,讓我們創建一個 DepartmentResource 類,該類映射到 /department:
@Path("department")
@Produces(MediaType.APPLICATION_JSON)
public class DepartmentResource {
@Context
private Configuration config;
// ...
}LinkRest 類需要 JAX-RS Configuration 類的實例,該實例通過 Context 註解進行注入,該註解也由 JAX-RS 提供。
接下來,我們繼續編寫訪問 Department 對象的所有端點。
5.1. 使用 POST 創建實體
要創建實體,<em/>LinkRest</em/> 類提供 <em/>create()</em/> 方法,該方法返回一個UpdateBuilder</em/> 對象:
@POST
public SimpleResponse create(String data) {
return LinkRest.create(Department.class, config).sync(data);
}數據參數可以是單個表示 部門 的 JSON 對象,也可以是對象數組。該參數通過 sync() 方法發送到 UpdateBuilder,用於創建和插入一個或多個對象到數據庫中,方法返回一個 SimpleResponse。
該庫定義了 3 種額外的響應格式:
- DataResponse<T> – 一個表示 T 對象的集合的響應
- MetadataResponse<T> – 包含關於類型的元數據信息
- SimpleResponse – 一個包含兩個 success 和 message 屬性的對象
接下來,我們使用 curl 將 Department 記錄添加到數據庫中:
curl -i -X POST -H "Content-Type:application/json"
-d "{"name":"IT"}" http://localhost:8080/linkrest/department因此,命令返回狀態碼 201 Created 以及 success 屬性。
{"success":true}我們還可以通過發送 JSON 數組創建多個對象:
curl -i -X POST -H "Content-Type:application/json"
-d "[{"name":"HR"},{"name":"Marketing"}]"
http://localhost:8080/linkrest/department5.2. 使用 GET 讀取實體
主要用於查詢對象的代碼是 <em >select()</em> 方法,來自 <em >LinkRest</em> 類。它返回一個 <em >SelectBuilder</em> 對象,我們可以使用它來鏈接額外的查詢或過濾方法。
讓我們在 <em >DepartmentResource</em> 類中創建一個端點,以返回數據庫中的所有 <em >Department</em> 對象:
@GET
public DataResponse<Department> getAll(@Context UriInfo uriInfo) {
return LinkRest.select(Department.class, config).uri(uriInfo).get();
}uri() 調用設置 SelectBuilder 的請求信息,而 get() 則返回一個包含 Departments 的集合,並將其包裝為 DataResponse<Department> 對象。
下面我們來看一下在通過此端點添加的部門:
curl -i -X GET http://localhost:8080/linkrest/department響應形式為包含 data 數組和 total 屬性的 JSON 對象:
{"data":[
{"id":200,"name":"IT"},
{"id":201,"name":"Marketing"},
{"id":202,"name":"HR"}
],
"total":3}或者,要檢索對象集合,可以使用getOne()方法,代替get()方法。
讓我們添加一個映射到/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();
}然後,我們可以向這個 URL 發送一個 GET 請求:
curl -i -X GET http://localhost:8080/linkrest/department/200結果是一個包含一個元素的 <em >數據</em> 數組:
{"data":[{"id":200,"name":"IT"}],"total":1}5.3. 使用 PUT 更新實體
要更新記錄,可以使用 <em >update()</em> 或 <em >createOrUpdate()</em> 方法。 後者如果記錄存在,則會更新它們;如果不存在,則會創建它們。
@PUT
public SimpleResponse createOrUpdate(String data) {
return LinkRest.createOrUpdate(Department.class, config).sync(data);
}類似於之前的部分,data 參數可以是單個對象或對象數組。
讓我們更新之前添加的一個部門:
curl -i -X PUT -H "Content-Type:application/json"
-d "{"id":202,"name":"Human Resources"}"
http://localhost:8080/linkrest/department這段操作返回一個包含成功或錯誤消息的 JSON 對象。之後,我們可以驗證 ID 為 202 的部門名稱是否已更改。
curl -i -X GET http://localhost:8080/linkrest/department/202當然,這一個命令確實返回了帶有新名稱的對象:
{"data":[
{"id":202,"name":"Human Resources"}
],
"total":1}5.4. 使用 DELETE 操作刪除實體
要刪除一個對象,我們可以調用 delete() 方法,它會創建一個 DeleteBuilder 對象,然後使用 id() 方法指定要刪除的對象的主鍵:
@DELETE
@Path("{id}")
public SimpleResponse delete(@PathParam("id") int id) {
return LinkRest.delete(Department.class, config).id(id).delete();
}然後我們可以使用 curl 調用這個端點:
curl -i -X DELETE http://localhost:8080/linkrest/department/2025.5. 處理實體間的關係
LinkRest 同樣包含方法,簡化了對象間關係的處理。
由於部門與員工之間存在一對多關係,我們將添加一個 /department/{departmentId}/employees 端點,訪問一個 EmployeeSubResource 類:
@Path("{id}/employees")
public EmployeeSubResource getEmployees(
@PathParam("id") int id, @Context UriInfo uriInfo) {
return new EmployeeSubResource(id);
}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() {
}
}請注意,為了使對象能夠作為 JSON 對象序列化,需要提供默認構造函數。
接下來,讓我們定義一個從部門檢索所有員工的端點:
@GET
public DataResponse<Employee> getAll(@Context UriInfo uriInfo) {
return LinkRest.select(Employee.class, config)
.toManyParent(Department.class, departmentId, Department.EMPLOYEES)
.uri(uriInfo).get();
}在本示例中,我們使用了 toManyParent() 方法,該方法位於 SelectBuilder 中,用於查詢具有指定父級的對象。
POST、PUT、DELETE 方法的端點可以以類似的方式創建。
要將員工添加到部門,我們可以通過使用帶有 POST 方法的 departments/{departmentId}/employees 端點來實現:
curl -i -X POST -H "Content-Type:application/json"
-d "{"name":"John"}" http://localhost:8080/linkrest/department/200/employees然後,我們向查看部門員工發送一個 GET 請求:
curl -i -X GET "http://localhost:8080/linkrest/department/200/employees這會返回一個包含數據的 JSON 對象:
{"data":[{"id":200,"name":"John"}],"total":1}6. 使用請求參數自定義響應
LinkRest 提供了一種便捷的方式,通過向請求添加特定參數來自定義響應。這些參數可用於過濾、排序、分頁或限制結果集中的屬性集。
6.1. 過濾
我們可以通過使用 cayenneExp 參數來基於屬性值過濾結果。正如其名稱所示,這遵循 Cayenne 表達式 的格式。
讓我們發送一個只返回名稱為“IT”的部門的請求:
curl -i -X GET http://localhost:8080/linkrest/department?cayenneExp=name='IT'6.2. 排序
要對一組結果進行排序,需要添加的參數包括 sort 和 dir。其中,前者指定排序的屬性,後者指定排序方向。
以下是按部門名稱對所有部門進行排序的結果:
curl -i -X GET "http://localhost:8080/linkrest/department?sort=name&dir=ASC"6.3. 分頁
該庫通過添加 start 和 limit 參數來支持分頁:
curl -i -X GET "http://localhost:8080/linkrest/department?start=0&limit=26.4. 選擇屬性
使用 include 和 exclude 參數,我們可以控制結果中返回的屬性或關係。
例如,讓我們發送一個僅顯示部門名稱的請求:
curl -i -X GET "http://localhost:8080/linkrest/department?include=name為了顯示部門中的員工姓名及其姓名,僅使用姓名,我們可以使用 include 屬性兩次:
curl -i -X GET "http://localhost:8080/linkrest/department?include=name&include=employees.name7. 結論
在本文中,我們展示瞭如何通過使用 LinkRest 框架,快速地通過 REST 端點暴露數據模型。