Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@fuxingloh
Created April 17, 2020 07:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fuxingloh/97a1a883c8cdcbd7dd79847f31cb3717 to your computer and use it in GitHub Desktop.
Save fuxingloh/97a1a883c8cdcbd7dd79847f31cb3717 to your computer and use it in GitHub Desktop.
Selectively Patch Data with Jackson ObjectMapper in Spring Web & Spring Data

Selectively Patch Data with Jackson ObjectMapper in Spring Web & Spring Data

Although this is done in Spring, the feature is provided by Jackson ObjectMapper.

Libraries used in this example

  • com.fasterxml.jackson.core:jackson-databind
  • org.springframework.boot:spring-boot-starter-web
  • org.springframework.boot:spring-boot-starter-json
  • org.springframework.boot:spring-boot-starter-validation
  • org.springframework.boot:spring-boot-starter-data-jpa
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@Entity
public class Data {
@NotNull
@Id
@Column(length = 255, updatable = false, nullable = false, unique = true)
private String id;
@NotNull
@ManyToOne(fetch = FetchType.EAGER, cascade = {})
private User user;
@NotBlank
@Length(max = 255)
@Column(length = 255, updatable = true, nullable = false, unique = false)
private String title;
@Length(max = 2500)
@Column(length = 2500, updatable = true, nullable = true, unique = false)
private String content;
public Record() {
}
public Record(@NotNull String id, @NotNull User user, @NotBlank @Length(max = 255) String title, @Length(max = 2500) String content) {
this.id = id;
this.user = user;
this.title = title;
this.content = content;
}
@PrePersist
void prePersist() {
id = UUID.randomUUID().toString();
}
public String getId() {
return id;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
@RestController
@RequestMapping("/data")
public class DataController implements PatchController {
private final DataRepository repository;
public DataController(DataRepository repository) {
this.repository = repository;
}
/**
* @return Record or {@code null}
*/
@GetMapping("/{id}")
public Data get(@PathVariable("id") String id) {
return repository.findById(id)
.orElse(null);
}
@PatchMapping("/{id}")
public Data patch(@PathVariable("id") String id, @RequestBody DataPatch patch) {
return repository.findById(id)
.map(Data -> {
Data = patch(Data, patch);
return repository.save(Data);
})
.orElseThrow(ForbiddenException::new);
}
}
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class DataPatch {
public String title;
public String content;
}
public interface PatchController {
ObjectMapper objectMapper = new ObjectMapper();
default <Data, Patch> Data patch(Data data, Patch patch) throws JsonException {
ObjectReader objectReader = objectMapper.readerForUpdating(data);
JsonNode patchNode = objectMapper.valueToTree(patch);
try {
return objectReader.readValue(patchNode);
} catch (IOException e) {
throw new JsonException(e);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment