Introduction
In the framework of web services, the typical basic set of operations on entity instances (objects) is CRUD ( C reate, R ead, U pdate and D elete). The HTTP methods POST, GET, PUT, and DELETE correspond to these operations in REST. But often the developer needs to partially change the object corresponding to the HTTP PATCH method. Its meaning is to change on the server side only those fields of the object that were passed in the request. There are various reasons for this:
a large number of fields in essence;
a high probability of simultaneous change of the same object under high load, as a result of which not only the modified fields will be overwritten;
impossibility or higher complexity of changing fields in several or all objects in the storage (bulk update);
These and, possibly, other reasons induce the developer to implement a stack of operations for partial object modification.
Let's consider the most frequently used options for solving the partial update problem.
Using a regular controller and DTO
One of the most common PATCH method implementations. In the controller, the incoming object is deserialized into a regular DTO, and further along the stack of application layers, it is considered that all fields in the DTO with a null value cannot be processed.
The advantages of this method include the "familiarity" of the implementation.
- null
( null
).
DTO . , . ObjectMapper
(/ POJO, @JsonInclude(Include.NON_NULL) ) , MapStruct, .
Map<String, Object> POJO
Map<String, Object>
. JSON . , , ( IDE).
null
.
: , , , , runtime( ).
JSON Patch JSON Merge Patch
JSON Patch JSON Merge Patch . Java EE , : JsonPatch JsonMergePatch. , json-patch. Michael Scharhag REST: Partial updates with PATCH.
: , , , , , , .
, DTO , , , , etc.
Partial Update library
: DTO Map<String, Object>
" ".
ChangeLogger ChangeLoggerProducer.
ChangeLoggerProducer
"" POJO, ChangeLogger
, Map<String, Object>
.
POJO:
public class UserModel {
private String login;
private String firstName;
private String lastName;
private String birthDate;
private String email;
private String phoneNumber;
}
@ChangeLogger
public class UserDto extends UserModel {
}
"":
ChangeLoggerProducer<UserDto> producer = new ChangeLoggerProducer<>(UserDto.class);
UserDto user = producer.produceEntity();
user.setLogin("userlogin");
user.setPhoneNumber("+123(45)678-90-12");
Map<String, Object> changeLog = ((ChangeLogger) user).changelog();
/*
changeLog in JSON notation will contains:
{
"login": "userlogin",
"phoneNumber": "+123(45)678-90-12"
}
*/
"" : Set<String>
, Map<String, Object> changelog()
, , , . , , ChangeLogger
, Map<String, Object> changelog()
.
/ "" ChangeLoggerAnnotationIntrospector
. Annotation Introspector ObjectMapper
. "" , @ChangeLogger
Map<String, Object> changelog()
. ObjectMapper
ChangeLoggerAnnotationIntrospector
.
:
ObjectMapper mapper = new ObjectMapper.setAnnotationIntrospector(new ChangeLoggerAnnotationIntrospector());
ChangeLoggerProducer<UserDto> producer = new ChangeLoggerProducer<>(UserDto.class);
UserDto user = producer.produceEntity();
user.setLogin("userlogin");
user.setPhoneNumber("+123(45)678-90-12");
String result = mapper.writeValueAsString(user);
/*
result should be equal
"{\"login\": \"userlogin\",\"phoneNumber\": \"+123(45)678-90-12\"}"
*/
:
ObjectMapper mapper = new ObjectMapper.setAnnotationIntrospector(new ChangeLoggerAnnotationIntrospector());
String source = "{\"login\": \"userlogin\",\"phoneNumber\": \"+123(45)678-90-12\"}";
UserDto user = mapper.readValue(source, UserDto.class);
Map<String, Object> changeLog = ((ChangeLogger) user).changelog();
/*
changeLog in JSON notation will contains:
{
"login": "userlogin",
"phoneNumber": "+123(45)678-90-12"
}
*/
ObjectMapper
ChangeLoggerAnnotationIntrospector
JSON . DTO, Model, Entity "". Partial Update Example.
Partial Update library , . , runtime.
:
" ", DTO, Model, Entity;
Spring, / "" DTO ( ),
ChangeLoggerAnnotationIntrospector
ObjectMapper
;
SQL/HQL bulk update ;
.
The format of this article does not allow us to take a closer look at the infrastructure for creating mappers and show the use of the library in a typical application stack. In the future, I can analyze the Partial Update Example in more detail and pay more attention to describing the internal implementation of the library.