views:

59

answers:

4

I trying to learn how to write RESTful apps in Java using Jersey and Hibernate, and I'm struggling to understand how to handle parent/child type relationships when POSTing data to a Resource. I'm using JSON to exchange data, but I don't think that's particularly relevant to my problem.

The example I'm working with models the relationship between employees and teams. An employee may, or may not, be a member of one team:

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

Behind this I have some Hibernate annotated POJOs: one for team, and one for employee, with a 1-N relationship between the two (remember that an Employee may not be a member of a team!). The same POJOs are also annotated as @XmlRootElements so that JAXB will allow me to pass them to/from the client as JSON.

The properties for the two entities look like this:

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

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

All good so far. But I'm struggling to understand how to make an employee a member of a team at creation-time by just passing in a Team ID, rather than passing in a nested team object in my JSON object.

For example, I'd like to be able to call POST /employee/ with a JSON that looks like this:

{
    "team_id":"1",
    "name":"Fred Bloggs",
    "phone":"1234567890",
    "email":"[email protected]"
}

But, instead, I have to pass in something like this:

{
    "team":{
        "id":"1",
        }
    "name":"Fred Bloggs",
    "phone":"1234567890",
    "email":"[email protected]"
}

So, my question is, how do others handle creating relationships in JSON/REST without passing around whole object graphs?

Sorry this is such a sketchy question, but as I say, I'm just starting out, and terminology is a problem for me at this stage!

+1  A: 

REST offers the possibility to use URLs as references, too, which I find really cool. So it would look like this:

{
    "team":"http://myapp.com/team/1",
    "name":"Fred Bloggs",
    "phone":"1234567890",
    "email":"[email protected]"
}

You can avoid passing nested objects, too by just supplying a

{
    "team":"1",
    "name":"Fred Bloggs",
    "phone":"1234567890",
    "email":"[email protected]"
}

In that case your converter must be smart enough to figure that if the value of the team key is a string (or integer, whatever works) and not another JSON object it should be interpreted as an id.

Daff
+1  A: 

If your framework forces your representation to include strange constructs like { "id":"1" } then I'd say it's time to switch framework!

More importantly, instead of worrying about adding a sub-JSONObject to your code, I would worry that the term "1" is indeed not really a hyperlink. Read up on the hypermedia constraint, or HATEOAS if you want.

What you want to pass in your POST is this:

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

so when the server sees this, it links the newly created employee with team #1 merely because it recognises the (relative) URI.

mogsie
A: 

I would use a dedicated link type, I modelled it in xml-link tag, but it would map to following json:


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

I prefer above link style because it is more generic (you define the relationship through the rel attribute). To me the link concept is so important in HTTP REST that I dedicate an own type for it.

Beware in some cases for performance reasons (avoiding network calls to traverse linked resource) you need to inline such relationships. For that you could offer a switch to return a inlined representation /employee/123?inline=true. But only offer such gimmicks, if really necessary. I once had to do it, but implementation wasn't trivial (though my format was XML, which is more constrained by schema definitions).

manuel aldana
A: 

There are multiple ways to approach a solution for this problem. Its a class hyperlinking problem in the domain of RESTful Web Services. Since this has to do with Jersey the first thing I would recommend is to avoid JAXB all together since JAXB (in context of XML or JSON) ain't HATEOAS.

After dwindling a lot with Jersey and HATEOAS I have come to the opinion that the best representations for a RESTful WS is Atom Syndication Format coupled with JSON. For your example of Team and Employee I would take the following approach.

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

Till here I haven't change much just specifying some representation details. The interesting part is adding/removing an employee from a team. For this I would add resources with pattern

@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) {}

}

Now what is position id, one approach - it is a unique number across all teams, i.e. primary key in a table which will have position_id, team_id, emp_id as tuple. Ensuring that there will never be 2 position_id same for 2 teams. This way the whole scenario could be turned to be HATEOAS.

In addition to be able to export URIs in JSON representation to a DTO, the approach that I take is, I have DTO representing the data model for its persistent storage and I have a representation model representing hyperlinked (de)serializable version of DTO where I store string value as hyperlink. I look at representation model as API and DTO as the SPI to the RESTful WS Data Model.

imyousuf