RESTful 1-N可选关系

时间:2010-08-20 20:31:30

标签: java json rest jaxb jersey

我试图学习如何使用Jersey和Java在Java中编写RESTful应用程序 Hibernate,我很难理解如何处理父/子类型 将数据发布到资源时的关系。我正在使用JSON进行交换 数据,但我认为这与我的问题无关。

我正在使用的示例模拟员工与员工之间的关系 团队。员工可能是也可能不是一个团队的成员:

GET /team/ - returns a list of teams
POST /team/ - creates a new team
GET /team/1 - returns a list of employees in the team with the ID 1

GET /employee/ - returns a list of employees
POST /employee/ - creates a new employee
GET /employee/1 - returns details about the employee with the ID 1

在这背后我有一些Hibernate注释的POJO:一个用于团队,一个用于团队 对于员工而言,两者之间存在1-N的关系(请记住 员工可能不是团队成员!)。同样的POJO也有注释 作为@XmlRootElements,以便JAXB允许我将它们传递给/从 客户端为JSON。

这两个实体的属性如下所示:

Team
    Long id;
    String name;
    String desc;
    List<Employee> employees;

Employee
    Long id;
    Team team;
    String name;
    String phone;
    String email;

到目前为止一切顺利。但我很难理解如何培养一名员工 通过传递团队ID,在创建时成为团队成员 而不是在我的JSON对象中传入嵌套的团队对象。

例如,我希望能够使用JSON调用POST / employee / 看起来像这样:

{
    "team_id":"1",
    "name":"Fred Bloggs",
    "phone":"1234567890",
    "email":"test@example.com"
}

但是,相反,我必须传递这样的东西:

{
    "team":{
        "id":"1",
        }
    "name":"Fred Bloggs",
    "phone":"1234567890",
    "email":"test@example.com"
}

所以,我的问题是,其他人如何处理在JSON / REST中创建关系而不传递整个对象图?

对不起,这是一个粗略的问题,但正如我所说,我刚开始 在这个阶段,术语对我来说是一个问题!

4 个答案:

答案 0 :(得分:1)

如果您的框架强制您的表示包含奇怪的结构,如{ "id":"1" },那么我会说是时候切换框架了!

更重要的是,我担心术语“1”确实不是真正的超链接,而不是担心在代码中添加子JSONObject。如果需要,请阅读超媒体约束或HATEOAS。

你想在POST中传递的是:

{
    "team_href" : "/teams/1",
    "name":"can'tbebothered"
}

所以当服务器看到这个时,它只是因为它识别(相对)URI而将新创建的员工与团队#1链接起来。

答案 1 :(得分:1)

我会使用专用链接类型,我在xml-link标签中建模,但它会映射到以下json:


{
  ...
  links: 
  [
      {
          "href" : "/teams/1",
          "rel" : "team"
      },
      {
          "href" : "/teams/2",
          "rel" : "team"
      }
  ]
}

我更喜欢上面的链接样式,因为它更通用(通过rel属性定义关系)。对我来说,链接概念在HTTP REST中非常重要,我为它专门设置了一个类型。

在某些情况下请注意性能原因(避免网络调用遍历链接资源),您需要内联此类关系。为此,您可以提供一个开关来返回内联表示/employee/123?inline=true。但是,如果真的有必要,只提供这样的噱头。我曾经不得不这样做,但实现并不简单(尽管我的格式是XML,它更受模式定义的限制)。

答案 2 :(得分:0)

REST提供了使用URL作为参考的可能性,我觉得这很酷。所以它看起来像这样:

{
    "team":"http://myapp.com/team/1",
    "name":"Fred Bloggs",
    "phone":"1234567890",
    "email":"test@example.com"
}

您也可以通过提供

来避免传递嵌套对象
{
    "team":"1",
    "name":"Fred Bloggs",
    "phone":"1234567890",
    "email":"test@example.com"
}

在这种情况下,你的转换器必须足够智能,以确定如果团队密钥的值是一个字符串(或整数,无论​​什么工作),而不是另一个JSON对象,它应该被解释为id。

答案 3 :(得分:0)

有多种方法可以解决此问题。它是RESTful Web服务领域中的类超链接问题。由于这与Jersey有关,我建议首先避免JAXB,因为JAXB(在XML或JSON的上下文中)不是HATEOAS。

在使用Jersey和HATEOAS逐渐减少之后,我发现RESTful WS的最佳表示形式是Atom Syndication Format和JSON。对于Team和Employee的示例,我将采用以下方法。

GET /team/ - returns a paginated Atom Syndication Feed list of teams
POST /team/ - creates a new team receiving a JSON representation
GET /team/1 - returns a paginated Atom Syndication Feed list of employees in the 
              team with the ID 1 with an Link to team's JSON representation too.

GET /employee/ - returns a paginated Atom Syndication Feed list of employees
POST /employee/ - creates a new employee using JSON
GET /employee/1 - returns details about the employee with the ID 1 in JSON

直到这里,我没有太多改变,只是指定一些表示细节。有趣的是从团队中添加/删除员工。为此,我将添加模式

的资源
@Path("/team/{id}/employees)
class TeamEmployees {

  @Path("/{posId}")
  @GET
  @Produces(MediaType.APPLICATION_JSON)
  public Employee get(@PathParam("posId") int positonId) {}

  @Path("/{posId}")
  @DELETE
  public Employee remove(@PathParam("posId") int positonId) {}

  @POST
  @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
  //empUri sample is /employee/{id} server knows how to parse it
  public Employee add(@FormParam("employeeUri") String empUri) {}

}

现在什么是位置ID,一种方法 - 它是所有团队中的唯一编号,即表中的主键,其中position_id,team_id,emp_id为元组。确保这一点 2支队伍永远不会有2个position_id相同。这样整个场景就可以变成HATEOAS。

除了能够将JSON表示中的URI导出到DTO之外,我采用的方法是, DTO 代表其持久性存储的数据模型我有表示模型表示 DTO 的超链接(可)序列化版本,其中我将字符串值存储为超链接。我将表示模型视为API,将 DTO 视为RESTful WS数据模型的SPI。