Skip to content

Instantly share code, notes, and snippets.

@VineetReynolds
Last active December 17, 2015 00:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save VineetReynolds/5524706 to your computer and use it in GitHub Desktop.
Save VineetReynolds/5524706 to your computer and use it in GitHub Desktop.
A collection of notes describing generation of REST resources for JPA object graphs of various types.

General assumptions

This section outlines some general assumptions of the behavior of the generated REST resources:

Read all resources

Perform a GET to the REST resource collection URL (i.e. to /rest/customers and not /rest/customers/1). An array/list representation of the collection in the appropriate content-type is returned.

Create a new Resource

Perform a POST to the REST resource collection URL. A 201 Response is generated containing the resource identifier with the location of the newly created resource in the Location header. In some cases it might be beneficial to generate a 200 response so that the client does not have to issue a subsequent GET (discuss).

The semantics involving the use of a POST would need to be changed to accommodate resources where the client specifies the resource identifier (i.e. JPA entities with no GeneratedValue annotations on Id fields), instead of the server generating it.

Read a resource

Perform a GET to the REST resource URL (i.e. to /rest/customers/1). Various types of resource identifier should be considered, since Path expressions would be generated with regular expressions for PathParams. The appropriate representation of the resource is returned.

Update

Perform a PUT to the REST resource URL. The updated representation of the resource is returned.

Delete

Perform a DELETE to the REST resource URL with a 200/204 response. Subsequent DELETEs may return a 404 response.

JPA entity with basic attributes

The REST resource deserializes requests and serializes responses, to and from the JPA entity as typically done. Typical implies that the complete JPA entity is deserialized back from the request, or is serialized into the response by the JAX-RS provider, without any subsequent operations to be performed by the REST resource.

The general assumptions outlined above hold good and do not need any specific behaviour under any circumstances.

JPA entity with a 1:1 uni-directional relationship

Relationship with a standalone entity

Assume a 1:1 uni-directional relationship between A → B where B does not have any other relationships with other entities.

The REST resources created for A and B allow creation of new top-level A and B entities as usual. Read, update and deletes can also be performed on the individual top-level entities.

Coarse-grained operations may be possible. It would be possible to create new resources of B through creation of new resources of A. The request submitted to create a new A instance, could be deserialized to have a valid A → B relationship, and if the PERSIST event is cascaded, then a new instance of B would be created. However, it must be noted that if the event is not cascaded, and if a reference to B exists during creation, then an exception may be thrown since EntityManager.persist would need to be invoked on the instances of both A and B. Additionally, if the id of B is set during a create (when EntityManager.persist is invoked), then an exception would be thrown since a detached object is passed to persist; this may need error handling code to be built in.

Deletion of an instance of A might result in deletion of the related B instance if the REMOVE event is cascaded.

Deserialization of requests and serialization of responses would be done typically. During serialization, a reference to B within A, would be serialized as a property of A in JSON/XML. During deserialized, if B exists in the submitted request, then it would converted into a valid object reference in A. No issues are expected to be encountered since no cycles or bi-directional relationships exists in this object graph that is 1 level deep.

The above topic of deserialization/serialization needs a lot more discussion though, since we’re forcing clients to submit complete object graphs for both creates and updates (and more importantly for the latter, since a missing reference to B in the deserialized object graph could be construed as a DELETE of B) - should we do this? Or should we instead expect clients to submit link relations (or something similar like object references) and have these link relations deserialized into the desired entity in the object graph. Using link relations would imply that we can no longer allow coarse-grained creates and updates, since those operations would now have to be performed on top-level resources.

Note that, in the context of serialization, we’re assuming the object reference is populated by the REST resource, before serialization is attempted. This would apply even if lazy-fetching were enabled (and would work in the JPA provider). In short, the REST resource is expected to perform a fetch of the underlying object graph (at least of the immediate object), before serialization. Attempting to serialize lazily-fetched references as null is not acceptable, and neither is throwing a LazyInitializationException exception (that is indeed quite lazy!).

Relationship with an entity having other relationships

Assume a 1:1 uni-directional relationship between A → B where B has relationships with other entities.

Most of the points stated above in the section on standalone entities hold good. Coarse-grained operations and serialization/deserialization however have some additional concerns to be addressed.

If B has a relationship with an entity C, then creates and updates on A, can possibly create new instances of C, or update existing instances of C. Likewise, JPA events need to be cascaded to C, otherwise errors might be thrown during POST/PUT requests to A.

Cyclic references need special consideration since A is not aware if C (or any other entity residing at a deeper level in the object graph) would have a reference back to A. This is true if the entire object graph is serialized out.

If link relations were used to represent the resource A in the requests, then the coarse-grained operations proposed above would not be possible, since the deserialized instance would be required to obtain the references to B and eventually C, from the EntityManager. The request-model would not contain enough information to convey updates to C. It would merely convey if the reference from A to B has been updated or not. using link relations would however make it easier to treat possible cyclic references in the object graph.

JPA entity with a 1:1 bi-directional relationship

Relationship with a standalone entity

Assume a 1:1 bi-directional relationship between A → B where B does not have any other relationships with other entities.

Most of the points that apply for the 1:1 uni-directional relationship apply except for the points on serialization and deserialization.

An ordinary JSON or XML serializer will obviously run into problems when presented with a bidi-relationship in the object graph.

The JPA model could be configured to have the serializer omit one side of the relationship (ala XmlTransient) but that would simply omit the relationship altogether in the response, thus leaving the client to 'somehow' figure out the existence of the relationship. However, using XmlTransient requires using Jackson annotations in addition to JAXB annotations in AS7, and is therefore not a portable solution. The counterpart in the Jackson world of JSON processing is the JsonManagedReference+JsonBackReference annotation pair, but they cannot be used only for M:M relationships (thus requiring different treatment in that event), and they also omit one side of the relationship. Furthermore, they are useful only for bidirectional references, and not for cyclical ones. Other options like XmlID and XmlIdRef have a different set of problems. More details available in this gist. The JSON Reference proposal does not seem to be implemented in JavaScript clients (including AngularJS), thus requiring the parsing ability to be built in for JSON responses with references.

No definitive generic solution exists for this problem that would solve both the serialization/deserialization issue in conjunction with the REST semantics issue for coarse-grained operations, unless of course we generate specialized serializers/deserializers that also can operate for n-level object graphs, while allowing object references to be correctly deserialized.

Link relations would not have the above problem, since the bi-directional reference would be converted into two links - one in each resource representation.

With link-relations, the relationship can be modified safely from both sides of the relationship. With coarse grained operations, it would be safe to say that modifications are typically safe only so long as they’re performed from the REST resource whose entity cascades events, or from both REST resources if they handle updates to the object graphs correctly.

Relationship with an entity having other relationships

Nothing of interest here. Same points as in 1:1 bidi-relationships involving standalone entities should apply here, despite the existence of a larger object graph and possible cycles.

JPA entity with a M:1 uni-directional relationship

Nothing to be discussed in addition to the points already noted for 1:1 uni-directional relationship.

JPA entity with a M:1 bi-directional relationship

Nothing to be discussed in addition to the points already noted for 1:1 bi-directional relationship.

JPA entity with a 1:M uni-directional relationship

Nothing to be discussed in addition to the points already noted for 1:1 uni-directional relationship. The only difference between the two is that a 1:M relationship contains a collection instead of a single property in the serialized/deserialized state. Modifications can be made either to the collection or to individual members of the collection, and may be cascaded depending on the JPA object model.

JPA entity with a 1:M bi-directional relationship

Nothing to be discussed in addition to the points already noted for 1:1 bi-directional relationship.

JPA entity with a M:M uni-directional relationship

Nothing to be discussed in addition to the points already noted for 1:M uni-directional relationship.

JPA entity with a M:M bi-directional relationship

Nothing to be discussed in addition to the points already noted for 1:M bi-directional relationship.

Transactional updates to several resources may be possible with coarse grained operations, while the opposite is true with link relations.

When using REST resources that support link relations, multiple transactions may need to be performed to have the database arrive at the desired state. With support for coarse grained operations, this may be achieved in a single operation, where the updated object graph is provided to a top-level entity; this is of course, dependent on whether the new state can be provided in the request and deserialized correctly.

Coarse grained operations in general have a limitation that they impose cycles and bidirectional references to be handled during serialization/de-serialization. The schemes adopted may not be supported or may not be intuitive for all clients. For e.g. JsonManagedReference/JsonBackReference

Link relations however have a limitation where in a request sent to the REST resource, may simply be incompatible with the JPA model. For example, the model may require that mandatory non-null references to existing objects must exist in the object graph when it is persisted. That is, if the model contains a bidirectional or cyclical relationship with optional=false constraints, then maintaining consistency in the database state would simply not be possible since one or more entities may not be created yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment