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 ).
source to share
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 oneCategory
? 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.
source to share