Here is my pojo
// Removed getters, setters, constructors for brevity, Also don't worry about Movie pojo
@Document
public class WatchList {
@Id
private String _id;
private List<Movie> currentlyWatching;
private List<Movie> completed;
}
Now in my rest controller I have a postmapping method like this
// Here id is the id of watchList I want to update
@PostMapping("/{id}/update")
public void updateItem(@PathVariable String id, @RequestBody WatchList watchList){
WatchList old = watchListRepo.findById(id).get();
// Now update old with contents from watchList. However only change those fields that are sent from @RequestBody. How to do this?
}
So from postman, I am sending a post request to this endpoint(with the id to change of course) with body like this
{
"currentlyWatching" : [...some new stuff]
}
However, my old
watchList pojo already has stuff like this:
{
"currentlyWatching" : [...some old stuff],
"completed" : [...some old stuff],
}
Now once this method gets done, I want my pojo to turn into this
{
"currentlyWatching" : [...some new stuff], // so this gets updated from post request
"completed" : [...some old stuff], // this stays the same since post request body did not contain this field called completed
}
So, I essentially only want to update currentlyWatching
field when client sends a post request with body containing currentlyWatching
. Now I can of course check for fields and map it manually, but my WatchList
is not as simple as shown here. It has more than 10 fields(not just currentlyWatching
and completed
). I want some dynamic easy way to do this. Also manual way of doing this would probably not be best design practice anyway.
Extra Info
:
I am using spring data mongo
so that @Document
annotation or repository class(extends MongoRepository) are from mongo jars. Also if it matters at all, the Movie
pojo is another pojo that has nested properties. However I want a way to do this dynamically, so Movie
pojo could be anything. Hence the solution that we figure out here should work for any Movie
pojo Schema. I have also heard about something called BeanAwareUtils
, is that something that could be used here?
Here is one approach that I found after searching online. You can use BeanUtils
. Here is maven dependency for it(make sure to check the updated version when you are using it).
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
</dependency>
Now you need to create another class(className can be anything) as follows:
public class NullAwareBeanArrayUtilsBean extends BeanUtilsBean {
@Override
public void copyProperty(Object dest, String name, Object value) throws IllegalAccessException, InvocationTargetException {
if (value == null || (value instanceof List<?> && ((List<?>) value).size() <= 0))
return;
super.copyProperty(dest, name, value);
}
}
And when you want to use it anywhere else in your code, use it like this:
BeanUtilsBean notNull = new NullAwareBeanArrayUtilsBean();
notNull.copyProperties(dest, original);
Explanation
BeanUtils
is a library which has one method to copy properties from one pojo to another pojo. It has a copyProperties
method which internally calls copyProperty
method. Here we have created a subclass called NullAwareBeanArrayUtilsBean
which extends BeanUtilsBean
and we have given a custom functionality to one of its methods named copyProperty
.
Remember that this method is called internally from copyProperties
method. You can try console logging name
in this copyProperty
method to see that name
refers to each
field from your pojo.
What are we doing in the custom overriden copyProperty method?
Consider this line
BeanUtilsBean notNull = new NullAwareBeanArrayUtilsBean();
notNull.copyProperties(dest, original);
Here we are saying that we want to copy stuff from original
pojo to dest
pojo.
For each field in our pojo, copyProperties
internally calls our custom overriden method named copyProperty
.
Consider copyProperty
method's first line:
if (value == null || (value instanceof List<?> && ((List<?>) value).size() <= 0)) return;
Here we are checking if value is null
or if value is a List
and is that list empty
, then we are skipping the copying operation. Therefore with this approach, if user sends a partial object from postman(for instance), we will copy only those fields in which the user has put some stuff all the while not changing other fields(which already have old content) in our destination pojo.