Tengo dos entidades - Concierto y ConcertDetail conectados en uno-a-uno relación unidireccional (un concierto tiene un detalle).
@Entity
@Table(name = "concert")
public class Concert {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;
@Column(name = "artist")
private String artist;
@Column(name = "city")
private String city;
@Column(name = "place")
private String place;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "concert_detail_id")
private ConcertDetail concertDetail;
public Concert() {
}
public Concert(String artist,
String city,
String place) {
this.artist = artist;
this.city = city;
this.place = place;
}
//skipping the setters and getters
@Entity
@Table(name = "concert_detail")
public class ConcertDetail {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;
@Column(name = "description")
private String description;
@Column(name = "date")
private LocalDate date;
@Column(name = "time")
private LocalTime time;
public ConcertDetail() {
}
public ConcertDetail(String description,
LocalDate date,
LocalTime time) {
this.description = description;
this.date = date;
this.time = time;
}
//skipping the setters and getters
Lo que no funciona, es la actualización parcial con @PatchMapping
.
@PatchMapping("/concerts/{id}")
public ResponseEntity<Concert> updatePartOfConcert(@RequestBody Map<String,Object> updates,
@PathVariable("id") int id) {
Concert concert = concertRepository.findById(id);
if (concert == null) {
System.out.println("Concert with id: " + id + " not found in the system!");
return new ResponseEntity<Concert>(HttpStatus.NOT_FOUND);
}
partialUpdate(concert, updates);
return new ResponseEntity<Concert>(HttpStatus.NO_CONTENT);
}
private void partialUpdate(Concert concert, Map<String,Object> updates) {
if(updates.containsKey("artist")) {
concert.setArtist((String) updates.get("artist"));
}
if(updates.containsKey("city")) {
concert.setCity((String) updates.get("city"));
}
if(updates.containsKey("place")) {
concert.setPlace((String) updates.get("place"));
}
if(updates.containsKey("concertDetail")) {
concert.setConcertDetail((ConcertDetail) updates.get("concertDetail"));
}
concertRepository.save(concert);
}
Si quiero actualizar los campos como artista, ciudad o lugar - que funciona bien. Pero cuando paso el concertDetail como JSON para actualizar el concierto dado con ella - Me da la siguiente excepción:
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.musicao.app.entity.ConcertDetail
at com.musicao.app.restController.ConcertRestController.partialUpdate(ConcertRestController.java:109) ~[classes/:na]
at com.musicao.app.restController.ConcertRestController.updatePartOfConcert(ConcertRestController.java:94) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_242]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_242]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_242]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_242]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879) ~[spring-webmvc-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:880) ~[spring-webmvc-5.2.4.RELEASE.jar:5.2.4.RELEASE]
Mi método de parches en RestController :
@PatchMapping("/concerts/{id}")
public ResponseEntity<Concert> updatePartOfConcert(@RequestBody Map<String,Object> updates,
@PathVariable("id") int id) {
Concert concert = concertRepository.findById(id);
if (concert == null) {
System.out.println("Concert with id: " + id + " not found in the system!");
return new ResponseEntity<Concert>(HttpStatus.NOT_FOUND);
}
partialUpdate(concert, updates);
return new ResponseEntity<Concert>(HttpStatus.NO_CONTENT);
}
private void partialUpdate(Concert concert, Map<String,Object> updates) {
if(updates.containsKey("artist")) {
concert.setArtist((String) updates.get("artist"));
}
if(updates.containsKey("city")) {
concert.setCity((String) updates.get("city"));
}
if(updates.containsKey("place")) {
concert.setPlace((String) updates.get("place"));
}
if(updates.containsKey("concertDetail")) {
concert.setConcertDetail((ConcertDetail) updates.get("concertDetail"));
}
concertRepository.save(concert);
}
Ya he probado la primavera y el método PARCHE DE DESCANSO
Su método de control updatePartOfConcert
toma un Map<String, Object>
parámetro, por lo que no está diciendo la primavera qué tipo de objeto para deserializar a. Como resultado de ello no puede deserializar propiedades secundarias o bien: habría que ser necesariamente correcta para serializar el concertDetail
objeto visto anteriormente en su respuesta JSON a un ConcertDetail
objeto? ¿Hay otras clases posibles que estos tres campos podrían deserializar a una instancia de?
La primavera no puede saber esto, por lo que updates.get("concertDetail")
será una Map
que contiene los detalles del concierto campos actualizados.
Si siempre está actualizando toda la ConcertDetail
instancia de una sola vez, se podría contemplar la utilización de un Jackson ObjectMapper
para convertir la Map
en un ConcertDetail
objeto. Véase, por ejemplo esta pregunta .
Sin embargo, si sólo desea actualizar algunos de los campos del concierto detalle (que se puede hacer, ya que estás usando una solicitud parche), entonces usted tendrá que escribir un método separado de actualización que:
private void partialUpdateConcertDetail(ConcertDetail concertDetail, Map<String, Object> detailUpdates) {
if (detailUpdates.containsKey("description")) {
concertDetail.setDescription((String) detailUpdates.get("description"));
}
// ... repeat for other fields.
}
A continuación, llama a este método de su partialUpdate
método:
if (updates.containsKey("concertDetail")) {
partialUpdateConcertDetail(concert.getConcertDetail(), (Map<String, Object>) updates.get("concertDetail"));
}