1. 概述

LinkRest 是一个开源框架,专门用于构建数据驱动的 REST Web 服务。它基于 JAX-RS 和 Apache Cayenne ORM 构建,采用 HTTP/JSON 消息协议。

简单来说,这个框架提供了一种简单粗暴的方式将数据存储暴露在 Web 上。接下来我们将通过实际案例,展示如何使用 LinkRest 快速构建 REST 服务访问数据模型。

2. Maven 依赖

要开始使用 LinkRest,首先需要添加核心依赖:

<dependency>
    <groupId>com.nhl.link.rest</groupId>
    <artifactId>link-rest</artifactId>
    <version>2.9</version>
</dependency>

这个依赖会自动引入 cayenne-server。此外,我们选择 Jersey 作为 JAX-RS 实现,所以需要添加:

<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:

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.196</version>
</dependency>

3. Cayenne 数据模型

我们将使用包含 Department(部门)和 Employee(员工)两个实体的数据模型,它们是一对多关系:

tables

⚠️ 注意:LinkRest 必须配合 Apache Cayenne ORM 生成的数据对象使用。本文不深入讲解 Cayenne,详情请参考官方文档

我们将 Cayenne 项目配置保存在 cayenne-linkrest-project.xml 文件中。运行 cayenne-maven-plugin 后,会生成:

  • 两个抽象类 _Department_Employee(继承自 CayenneDataObject)
  • 两个具体类 DepartmentEmployee

这些具体类就是我们要在 LinkRest 中使用的类。

4. LinkRest 应用启动

要运行 REST 接口,需要先配置运行环境。由于我们使用 Jersey,创建一个扩展 ResourceConfig 的类:

@ApplicationPath("/linkrest")
public class LinkRestApplication extends ResourceConfig {

    public LinkRestApplication() {
        packages("com.baeldung.linkrest.apis");
        
        // 加载 linkrest 运行时
    }
}

在构造函数中初始化 LinkRestRuntime:

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 类构建

我们将为 /department 路径编写 CRUD 接口。首先创建 DepartmentResource 类:

@Path("department")
@Produces(MediaType.APPLICATION_JSON)
public class DepartmentResource {

    @Context
    private Configuration config;
    
    // ...
}

LinkRest 需要注入 JAX-RS 的 Configuration 实例。

5.1. 使用 POST 创建实体

LinkRest 的 create() 方法返回 UpdateBuilder

@POST
public SimpleResponse create(String data) {
    return LinkRest.create(Department.class, config).sync(data);
}

data 参数可以是单个 Department 的 JSON 对象,也可以是对象数组。sync() 方法会创建对象并插入数据库,返回 SimpleResponse

✅ 支持的响应类型:

  • DataResponse<T> - 表示 T 的集合
  • MetadataResponse<T> - 包含类型元数据
  • SimpleResponse - 包含 success 和 message 属性

用 curl 添加部门:

curl -i -X POST -H "Content-Type:application/json" 
  -d "{"name":"IT"}" http://localhost:8080/linkrest/department

返回状态码 201 Created 和响应体:

{"success":true}

批量创建:

curl -i -X POST -H "Content-Type:application/json" 
  -d "[{"name":"HR"},{"name":"Marketing"}]" 
  http://localhost:8080/linkrest/department

5.2. 使用 GET 读取实体

查询主要使用 select() 方法,返回 SelectBuilder。获取所有部门:

@GET
public DataResponse<Department> getAll(@Context UriInfo uriInfo) {
    return LinkRest.select(Department.class, config).uri(uriInfo).get();
}

uri() 设置请求信息,get() 返回 DataResponse<Department>。用 curl 测试:

curl -i -X GET http://localhost:8080/linkrest/department

响应格式:

{"data":[
  {"id":200,"name":"IT"},
  {"id":201,"name":"Marketing"},
  {"id":202,"name":"HR"}
], 
"total":3}

查询单个对象使用 getOne()

@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();
}

测试:

curl -i -X GET http://localhost:8080/linkrest/department/200

响应:

{"data":[{"id":200,"name":"IT"}],"total":1}

5.3. 使用 PUT 更新实体

更新使用 update()createOrUpdate()(不存在则创建):

@PUT
public SimpleResponse createOrUpdate(String data) {
    return LinkRest.createOrUpdate(Department.class, config).sync(data);
}

更新部门名称:

curl -i -X PUT -H "Content-Type:application/json" 
  -d "{"id":202,"name":"Human Resources"}" 
  http://localhost:8080/linkrest/department

验证更新:

curl -i -X GET http://localhost:8080/linkrest/department/202

结果:

{"data":[
  {"id":202,"name":"Human Resources"}
],
"total":1}

5.4. 使用 DELETE 删除实体

删除使用 delete() 方法:

@DELETE
@Path("{id}")
public SimpleResponse delete(@PathParam("id") int id) {
    return LinkRest.delete(Department.class, config).id(id).delete();
}

测试:

curl -i -X DELETE http://localhost:8080/linkrest/department/202

5.5. 处理实体关系

LinkRest 简化了关系操作。Department 和 Employee 是一对多关系,添加子资源:

@Path("{id}/employees")
public EmployeeSubResource getEmployees(
  @PathParam("id") int id, @Context UriInfo uriInfo) {
    return new EmployeeSubResource(id);
}

EmployeeSubResource 类:

@Produces(MediaType.APPLICATION_JSON)
public class EmployeeSubResource {
    private Configuration config;
    private int departmentId;

    public EmployeeSubResource(int departmentId, Configuration configuration) {
        this.departmentId = departmentId;
        this.config = configuration;
    }

    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() 方法按父实体过滤。添加员工:

curl -i -X POST -H "Content-Type:application/json" 
  -d "{"name":"John"}" http://localhost:8080/linkrest/department/200/employees

查看员工列表:

curl -i -X GET "http://localhost:8080/linkrest/department/200/employees

响应:

{"data":[{"id":200,"name":"John"}],"total":1}

6. 通过请求参数定制响应

LinkRest 支持通过请求参数定制响应,实现过滤、排序、分页和属性选择。

6.1. 过滤

使用 cayenneExp 参数过滤,遵循 Cayenne 表达式 语法:

curl -i -X GET http://localhost:8080/linkrest/department?cayenneExp=name='IT'

6.2. 排序

使用 sortdir 参数:

curl -i -X GET "http://localhost:8080/linkrest/department?sort=name&dir=ASC"

6.3. 分页

使用 startlimit 参数:

curl -i -X GET "http://localhost:8080/linkrest/department?start=0&limit=2

6.4. 选择属性

使用 includeexclude 控制返回字段:

仅返回部门名称:

curl -i -X GET "http://localhost:8080/linkrest/department?include=name

返回名称和员工姓名:

curl -i -X GET "http://localhost:8080/linkrest/department?include=name&include=employees.name

7. 总结

本文展示了如何使用 LinkRest 框架快速构建 REST 接口暴露数据模型。核心优势包括:

  • ✅ 基于 JAX-RS 和 Cayenne ORM
  • ✅ 简化 CRUD 操作
  • ✅ 支持关系处理
  • ✅ 灵活的响应定制

完整示例代码请查看 GitHub 仓库。踩坑提示:确保 Cayenne 配置正确,否则运行时会报错。


原始标题:Guide to LinkRest Java