How do I restore object relationships after saving?

I am developing a RESTful web service with spring-data as a data access layer supported by JPA / Hibernate. Very often there are relationships between domain objects. For example, imagine an object Product

that has a Category

.

Now that client POST

a Product

is a JAX-RS method. This method is annotated with @Transactional

to wrap each repository operation in a transaction. Of course, the client is sending a id

pre-existing one Category

, not the whole view, just a link (foreign key).

In this method, if I do this:

entity = repository.save(entity);

      

the variable entity

now Category

only has a field set id

. It didn't surprise me. I was not expecting a save (SQL insert) to retrieve information about related objects. But I need the whole object Product

and related objects to be able to return to the user.

Then I did this:

entity = repository.save(entity);
entity = repository.findOne(entity.getId());

      

that is, to get an object after saving it, inside the same transaction / session .

To my surprise, the variable entity

didn't change anything. In fact, the database did not even receive any select queries. It has to do with the Hibernate cache. For some reason, when in the same transaction the search does not retrieve the entire object graph if that object was previously saved.

With Hibernate, the solution seems to be to use session.refresh(entity)

(see this and this ). Has the meaning.

But how can I achieve this with spring data?

I would like to avoid creating duplicate custom repositories. I think this functionality should be part of itslef spring data (some people have already posted this on the spring data forum: thread1 , thread2 ).

+3


source to share


1 answer


TL; DR

Links between objects in the web layer must be clear using links and must not hide behind instances of half-supplied objects. Links in the persistence layer are represented by object references. So there must be a dedicated step transforming one (reference) to another (fully populated object reference).

More details

This is an anti-pattern to bypass backend ids per se and assume the marshaling binding is doing the right thing. Thus, clients must work with links and pass them to the server to indicate that they want to establish a connection between an already existing resource and one that is to be created.

Suppose you have an existing Category

one open via /categories/4711

, you can send a message to your server:

POST /products
{ links : [ { rel : "category", href : "/categories/4711" } ],
  // further product data
}

      

The server will instantiate a new instance Product

, populate it with additional data, and eventually populate the associations like this:



  • Determine the properties to be populated by looking at the link relationship types (for example, property Category

    here.
  • Extract the backend id from the given URI
  • Use the appropriate repository to find the related object instance
  • Set it to root object

So in your example, boil down:

Product product = new Product();
// populate primitive properties
product.setCategory(categoryRepository.findOne(4711));
productRepository.save(product);

      

Just put something like this on the server:

POST /products
{ category : {
    id : 1, … },
}

      

is suboptimal for many reasons:

  • Do you want the persistence provider to implicitly persist the instance Product

    and at the same time "recognize" Category

    that the referenced instance (actually consisting only of an identifier) ​​should not be persisted, but updated with the data of an existing one Category

    ? This is quite a bit of magic that I would say.
  • You are essentially overlaying the data structure you use to POST to the server onto the persistence layer, expecting it to transparently figure out how you decide to do the POST. This is not the responsibility of the persistence layer, but the web layer. The whole purpose of the web tier is to soften the characteristics of the HTTP protocol using views and links to the backend service.
+1


source







All Articles