Spring Rest Dropping Data When Returning - java

I have a simple Spring Rest service setup to handle some JSON objects I have in a DB. I have two applications; one looking to hold that data, and the other is looking to request/store that data locally to its DB. The object used is in a shared Jar that both applications have access to, so they know the structure, expectations, etc.
When I run a request for the data I can see it coming in, but some of the data has been nulled out in the JSON. I have setup breakpoints at the last return point for the source system, and when the data is there (pulled from DB) and ready to return it is intact. Once it gets across the wire in any form via Postman or my 2nd App, some of the fields have been blanked out.
EDIT: Some code posted with specific names simplified for ease of reading
//------------------The Data "Source"------------------------------
//The source service that serves the content
#ResponseBody
#RequestMapping(value = { "/json/listObjectA" }, method = RequestMethod.GET)
public List<ObjectA> listAllObjectA() {
List<ObjectA> allObjectA = ObjectA
.getAll(thisApplicationContext);
return allObjectA; //a breakpoint here shows the data as expected
//the data in the DB is correct in source, confirmed.
}
//------------------The Requesting "Target"-----------------------------
//trying to get that data, setting up a baseURL for the service works
//when using this block of code with debuggers on, it reaches the source above.
RestTemplate rest = new RestTemplate();
ResponseEntity<ObjectA[]> sfResponse = rest.getForEntity(
coreBaseLocation+"json/listObjectA",
ObjectA[].class);
ObjectA[] sfList = sfResponse.getBody();
// right here debugging, the list has the correct number, but some of the data is missing
for (ObjectA sf: sfList) {
sf.setId(null); //clear the local id, a uuid is persisting
sf.save(thisApplicationContext);
}
//------------------The data class------------------------------
#Entity
public class ObjectA{
...
protected String ObjectBUuid;
protected String ObjectCUuid;
...
// basic POJO getters/setters/constructor etc. nothing fancy
Also for reference, ObjectA is a simple "Join" object between an ObjectB and ObjectC. A simple join table doesn't work since I am storing a lot more meta data about the association that isn't super relevant to this but needed for business reasons.
//Here is a copy of the "response" from Postman hitting that json url.
//again, in debug before this is sent back the data looks correct with the Uuids populated (like in the DB)
[
{
"id": 1, //local id of the ObjectA
"uuid": "1464d8dc-0281-4850-a51d-a375f7a2fd04", //ObjectA's uuid
... metadata stuff ...
// end of obj., no reference to the ObjectB/C uuids at all in the returned obj.
}, // more data here matching the number I expect from the DB and the source debug breakpoint, just with the reference uuid missing.

Related

Generalizing the functionality of Spring's #ModelAttribute to more than just query parameters

Recently I was working on a little RESTful API using Spring and I came across the ModelAttribute annotation.
I noticed that there is some very interesting behavior associated with it, mainly the fact that you can stick it onto a method and it will get called before the handler for a given request is called, allowing you to do anything before data is bound to the arguments of your handler method.
One usage that comes to mind is default values:
#ModelAttribute("defaultEntity")
public Entity defaultEntity() {
final var entity = new Entity();
entity.setName("default name");
return entity;
}
#PostMapping("/entity")
public Entity createNewEntity(#Valid #ModelAttribute("defaultEntity") Entity entity) {
dao.saveEntity(entity);
return entity;
}
In this case, when a POST request comes to /entity, the first thing that will happen is that defaultEntity will get called, creating an entity with some default values pre-filled. Then, Spring will bind the incoming data into it (potentially overwriting the defaults or keeping them as-is) and then pass it into the createNewEntity handler. This is actually pretty nice, IMO.
Another surprising fact is that the annotated method can actually take parameters in much the same way as the handler method. A simple way to do partial entity updates could be something like this:
// first fetch the original entity from the database
#ModelAttribute("originalEntity")
public Entity originalEntity(#PathVariable("id") long id ) {
return dao.getEntity(id);
}
// then let Spring bind data to the entity and validate it
#PostMapping("/entity/{id}")
public Entity updateEntity(#Valid #ModelAttribute("originalEntity") Entity entity) {
// and finally we save it
dao.saveEntity(entity);
return entity;
}
Again, this is surprisingly easy.
Even more surprising is that different model attributes can depend on each other, so you can have a complicated multi-stage monster if you want:
// first fetch the original entity from the database
#ModelAttribute("originalEntity")
public Entity originalEntity(#PathVariable("id") long id ) {
return dao.getEntity(id);
}
// then let Spring bind data to the entity, validate it and do some processing to it
#ModelAttribute("boundAndValidatedEntity")
public Entity boundAndValidatedEntity(#Valid #ModelAttribute("originalEntity") Entity entity) {
processEntity(entity);
return entity;
}
// finally check that the entity is still valid and then save it
#PostMapping("/entity/{id}")
public Entity updateEntity(#Valid #ModelAttribute(value = "boundAndValidatedEntity", binding = false) Entity entity) {
dao.saveEntity(entity);
return entity;
}
Obviously not all of the model attributes have to be of the same type, some can depend on multiple arguments from different places. It's like a mini-DI container within a single controller.
However, there are some drawbacks:
as far as I can tell, it only works with query parameters and there is no way to make it work with other kinds of request parameters, such as the request body or path variables
all of the ModelAttribute-annotated methods within a single controller will always be called, which can
have a performance impact
be annoying to work with, since Spring will need to be able to gather all of the method's arguments (which may be impossible, for example when they reference a path variable that doesn't exist in the current request)
So, while ModelAttribute doesn't really seem too useful by itself because of these issues, I feel like the main idea behind it - essentially allowing you to control the construction of a method's parameter before it's bound/validated while being able to easily access other request parameters - is solid and could be very useful.
So, my question is simple - is there anything in Spring that would essentially act like ModelAttribute but without the drawbacks that I mentioned? Or maybe in some 3rd party library? Or maybe I could write something like this myself?

How to update only a subset of fields and update the repository?

I'm making a spring boot application, and I'm looking to update an existing entry in the DB through my service and controller. In my service layer I have the below method. So I'm retrieving the fields associated with a caseID, creating a model mapper which maps my entity object class to my VO, and then mapping the retrieved data to my DTO. Then I save my repository. The purpose is to add only the fields which I have specified in my req message ie if I only want to update 1 field out of 20, it updates this field and leaves the rest untouched. The below runs successfully, but the field I specify in my request message in postman does not update in the DB. Why is this? I have tried mapping different objects and saving different variables to the repository but nothing seems to update the DB.
public StoredOutboundErrorCaseVO updateCase(OutboundErrorCaseVO outboundErrorCaseVO, Long caseNumber) {
OutboundErrorCaseData existingCaseData = ErrorCaseDataRepository.findById(caseNumber).get();
ModelMapper mm = new ModelMapper();
mm.getConfiguration().setAmbiguityIgnored(true);
OutboundErrorCaseData uiOutboundErrorCaseData = mm.map(outboundErrorCaseVO,
OutboundErrorCaseData.class);
mm.map(existingCaseData, uiOutboundErrorCaseData);
ErrorCaseDataRepository.save(uiOutboundErrorCaseData);
return mm.map(uiOutboundErrorCaseData, StoredOutboundErrorCaseVO.class);
}
Controller - code omitted for brevity, POST method (I usually use PUT for updates but I believe I can still use POST)
StoredOutboundErrorCaseVO updatedCase = outboundErrorService.updateCase(outboundErrorCaseVO,
caseNumber);
Repo
#Repository
public interface OutboundErrorCaseDataRepository extends JpaRepository<OutboundErrorCaseData, Long> {
You are getting data and passing it into existingCaseData and save uiOutboundErrorCaseData. So my guess is Hibernate is adding a new object into the database with new Id and with you updated value. It of course depends on your model definition. Especially id.
I also think Hibernate won't let you save uiOutboundErrorCaseData with the same Id if you already have an object in Hibernate Session associated with that id. So, why don't you update existingCaseData with the new value and save it back.
I created a working solution, although I realise it can be improved, it certainly works. The only drawback is that I need to specify all the fields which can be updated, ideally I want a solution which takes in n number of fields and updates the record.
OutboundErrorCaseData existingCaseDta = ErrorCaseDataRepository.findById(caseNumber).get();
if (outboundErrorCaseVO.getChannel() != null) {
existingCaseDta.setChannel(outboundErrorCaseVO.getChannel());
}
ErrorCaseDataRepository.save(existingCaseDta);
ModelMapper mm = new ModelMapper();
return mm.map(existingCaseDta, StoredOutboundErrorCaseVO.class);

Spring JPA - RESTful partial update and validation for entity

I've a simple RESTful API based on Spring MVC using a JPA connected MySQL database. Until now this API supports complete updates of an entity only. This means all fields must be provided inside of the request body.
#ResponseBody
#PutMapping(value = "{id}")
public ResponseEntity<?> update(#Valid #RequestBody Article newArticle, #PathVariable("id") long id) {
return service.updateById(id, newArticle);
}
The real problem here is the validation, how could I validate only provided fields while still require all fields during creation?
#Entity
public class Article {
#NotEmpty #Size(max = 100) String title;
#NotEmpty #Size(max = 500) String content;
// Getters and Setters
}
Example for a partial update request body {"content": "Just a test"} instead of {"title": "Title", "content": "Just a test"}.
The actual partial update is done by checking if the given field is not null:
if(newArticle.getTitle() != null) article.setTitle(newArticle.getTitle());
But the validation of course wont work! I've to deactivate the validation for the update method to run the RESTful service. I've essentially two questions:
How can I validate only a "existing" subset of properties in the
update method while still require all fields during creation?
Is there a more elegant way for update partially then checking for
null?
The complexity of partial updates and Spring JPA is that you may send half of the fields populated, and even that you will need to pull the entire entity from the data base, then just "merge" both entity and the pojo, because otherwise you will risk your data by sending null values to the database.
But merging itself is kind of tricky, because you need to operate over each field and take the decision of either send the new value to the data base or just keep the current one. And as you add fields, the validation needs to be updated, and tests get more complex. In one single statement: it doesn't scale. The idea is to always write code which is open for extension and closed for modifications. If you add more fields, then the validation block ideally doesn't need to change.
The way you deal with this in a REST model, is by operating over the entire entity each time you need. Let's say you have users, then you first pull a user:
GET /user/100
Then you have in your web page the entire fields of user id=100. Then you change its last name. You propagate the change calling the same resource URL with PUT verb:
PUT /user/100
And you send all the fields, or rather the "same entity" back with a new lastname. And you forget about validation, the validation will just work as a black box. If you add more fields, you add more #NotNull or whatever validation you need. Of course there may be situations where you need to actually write blocks of code for validation. Even in this case the validation doesn't get affected, as you will have a main for-loop for your validation, and each field will have its own validator. If you add fields, you add validators, but the main validation block remains untouchable.

Partial fields update REST API

There is this MongoBean: SuperBean
class SuperBean extends MongoBaseBean{
private String id;
private String title;
private String parent;
//And getters, setters
}
Need is to write an update API, which is capable of performing partial attributes update. Common approach seen across the web as well as heard from my peers is to check the fields in the request for Null and update if not null. But what if the update request is for the value to be updated to Null??
After few discussions, we came up with three approaches:
Set default value for the field in the bean. Hence instead of non-null parent field, if it does not have $ in the request, this will be considered for update.
class SuperBean extends MongoBaseBean{
private String id;
private String title;
private String parent = "$";
//And getters, setters
}
Let the update API Implementation accept a Map. The actual bean is fetched and all the fields that are present in the request map will be updated.
#Post
public SuperBean updatePartial(Map<String,Object> dataObject) {}
Let the update API accept DTO, that contains 2 maps. One to contain old values, other for new values. This could be advantageous in scenarios, where the update has to happen only if the database contains the values as sent in oldDataObj. But this increases the payload size.
class SuperBeanUpdateDTO {
private Map<String, Object> oldDataObj;
private Map<String, Object> newDataObject;
//getters, setters
}
#Post
public SuperBean updatePartial(SuperBeanUpdateDTO updateDTO) {}
What factors should be considered to chose one of these approaches? Is there another better way to approach this problem?
In my projects, we usually choose the way that similar with your second way. but not exactly the same.
for example, in your client side, you have a page or a view to modify your profile info, includes name, birthday, gender, although you just modify the name value, when you click save button, it still will send the data to server includes birthday and gender with name field, but just keep its value as old. and the server API will directly update these three values in database, won't check whether its value changed or not.
if you have another page or view to modify other parts of the profile, likes password, it need add a new method in client and a new API in server. the API URL likes PATCH /reset_password, and the sent data should include old_password and new_password field.
PS:
1. we use PUT or PATCH to update a resource, not POST, POST is used to create a new resource.
2. when you update a resource, in the above example, the API likes PATCH /profiles/:id (other's profile) or PATCH /profile (yourself profile), so the sent data doesn't need id field anymore, it includes in your API URL.

How to do JAX-RS partial update with bean validation

It may seem crazy but I'm having trouble with JAX-RS partial JSON requests. For example suppose we have an entity:
public class Data {
private String a;
private String b;
private Integer c;
/* this is flat and large object with many fields */
}
#PUT
#Consumes(MediaType.APPLICATION_JSON)
#Path("/data")
public Response updateData(Data d) {
/* update database */
}
Now I want to let user update some fields. So user sends request like this:
{ "a": "data of field a" }
This means only update field "a". When user wants to set field "a" to null he will send { "a": null }. The problem is on server side I can't decide Whether client wants to set field "a" to null or there is no field "a" in the request thus I should not touch field "a"(In both cases "a" will be null). One solution is to read Map<String, Object> instead of Data. In this way I can differentiate these two cases, but I will loose bean validation framework plus I should do lots of type casting(Object to String/Integer/List<Integer>). Is there any concise way to handle such partial update requests? I'm using Jersey 2.9 with Jackson 2.3 on Jetty.

Categories

Resources