Binding dati object aggiornamento spring parziale

Stiamo cercando di implementare una speciale funzione di aggiornamento parziale in Spring 3.2. Usiamo Spring per il backend e abbiamo un semplice frontend JavaScript. Non sono stato in grado di trovare una soluzione immediata ai nostri requisiti, ovvero la funzione update () dovrebbe contenere un numero qualsiasi di campi: valori e aggiornare il modello di persistenza di conseguenza.

Abbiamo la modifica in linea per tutti i nostri campi, in modo che quando l’utente modifica un campo e confermi, un ID e il campo modificato vengono passati al controller come json. Il controller dovrebbe essere in grado di prendere qualsiasi numero di campi dal client (da 1 a n) e aggiornare solo quei campi.

ad esempio, quando un utente con id == 1 modifica il suo displayName, i dati inviati al server sono come questo:

{"id":"1", "displayName":"jim"} 

Attualmente, abbiamo una soluzione incompleta nell’Usercontroller come descritto di seguito:

 @RequestMapping(value = "/{id}", method = RequestMethod.POST ) public @ResponseBody ResponseEntity update(@RequestBody User updateUser) { dbUser = userRepository.findOne(updateUser.getId()); customObjectMerger(updateUser, dbUser); userRepository.saveAndFlush(updateUuser); ... } 

Il codice qui funziona, ma presenta alcuni problemi: @RequestBody crea un nuovo updateUser , inserisce l’ id e il displayName . CustomObjectMerger unisce questo updateUser con il corrispondente dbUser dal database, aggiornando gli unici campi inclusi in updateUser .

Il problema è che Spring popola alcuni campi in updateUser con valori predefiniti e altri valori di campo generati automaticamente, che, una volta uniti, sovrascrive i dati validi che abbiamo in dbUser . Dichiarare esplicitamente che dovrebbe ignorare questi campi non è un’opzione, poiché vogliamo che il nostro update sia in grado di impostare anche questi campi.

Sto cercando in qualche modo che Spring ricopra automaticamente SOLO le informazioni esplicitamente inviate alla funzione update() nel dbUser (senza reimpostare i valori predefiniti / auto del campo). C’è un modo semplice per farlo?

Aggiornamento: ho già considerato la seguente opzione che fa quasi quello che chiedo, ma non del tutto. Il problema è che richiede i dati di aggiornamento come @RequestParam e (AFAIK) non esegue le stringhe JSON:

 //load the existing user into the model for injecting into the update function @ModelAttribute("user") public User addUser(@RequestParam(required=false) Long id){ if (id != null) return userRepository.findOne(id); return null; } .... //method declaration for using @MethodAttribute to pre-populate the template object @RequestMapping(value = "/{id}", method = RequestMethod.POST ) public @ResponseBody ResponseEntity update(@ModelAttribute("user") User updateUser){ .... } 

Ho considerato di riscrivere il mio customObjectMerger() per lavorare in modo più appropriato con JSON, contando e prendendo in considerazione solo i campi provenienti da HttpServletRequest . ma anche dover usare un object customObjectMerger() in primo luogo si sente hacky quando la spring fornisce quasi esattamente quello che sto cercando, meno la mancanza della funzionalità JSON . Se qualcuno sa come convincere Spring a farlo, lo apprezzerei molto!

Ho appena incontrato questo stesso problema. La mia attuale soluzione sembra così. Non ho ancora fatto molti test, ma all’ispezione iniziale sembra che stia funzionando abbastanza bene.

 @Autowired ObjectMapper objectMapper; @Autowired UserRepository userRepository; @RequestMapping(value = "/{id}", method = RequestMethod.POST ) public @ResponseBody ResponseEntity update(@PathVariable Long id, HttpServletRequest request) throws IOException { User user = userRepository.findOne(id); User updatedUser = objectMapper.readerForUpdating(user).readValue(request.getReader()); userRepository.saveAndFlush(updatedUser); return new ResponseEntity<>(updatedUser, HttpStatus.ACCEPTED); } 

ObjectMapper è un bean di tipo org.codehaus.jackson.map.ObjectMapper.

Spero che questo aiuti qualcuno,

Modificare:

Hanno incontrato problemi con oggetti figlio. Se un object figlio riceve una proprietà da aggiornare parzialmente, creerà un nuovo object, aggiornerà quella proprietà e la imposterà. Questo cancella tutte le altre proprietà su quell’object. Aggiornerò se trovo una soluzione pulita.

Stiamo usando @ModelAttribute per ottenere ciò che vuoi fare.

  • Creare un metodo annotato con @ modelattribute che carica un utente in base a una directory pathrogabile a un repository.

  • crea un metodo @Requestmapping con un parametro @modelattribute

Il punto qui è che il metodo @modelattribute è l’inizializzatore per il modello. Quindi Spring unisce la richiesta con questo modello poiché lo dichiariamo nel metodo @requestmapping.

Questo ti dà funzionalità di aggiornamento parziale.

Alcuni, o anche molti? 😉 direi che questa è una ctriggers pratica in ogni caso poiché utilizziamo i DAO direttamente nel controller e non lo facciamo in un livello di servizio dedicato. Ma al momento non ci siamo imbattuti in problemi a causa di questo approccio.

Costruisco un’API che unisce gli oggetti di visualizzazione con le entity framework prima di chiamare persiste o unire o aggiornare.

È una prima versione, ma penso che sia un inizio.

Basta usare l’annotazione UIAttribute nei campi POJO `s quindi utilizzare:

MergerProcessor.merge(pojoUi, pojoDb);

Funziona con attributi e raccolte nativi.

git: https://github.com/nfrpaiva/ui-merge

Il seguente approccio potrebbe essere utilizzato.

Per questo scenario, il metodo PATCH sarebbe più appropriato in quanto l’ quadro verrà parzialmente aggiornata.

Nel metodo controller, prendi il corpo della richiesta come stringa.

Converti quella stringa in JSONObject. Quindi scorrere le chiavi e aggiornare la variabile di corrispondenza con i dati in arrivo.

 import org.json.JSONObject; @RequestMapping(value = "/{id}", method = RequestMethod.PATCH ) public ResponseEntity updateUserPartially(@RequestBody String rawJson, @PathVariable long id){ dbUser = userRepository.findOne(id); JSONObject json = new JSONObject(rawJson); Iterator it = json.keySet().iterator(); while(it.hasNext()){ String key = it.next(); switch(key){ case "displayName": dbUser.setDisplayName(json.get(key)); break; case "....": .... } } userRepository.save(dbUser); ... } 

Il lato negativo di questo approccio è che devi convalidare manualmente i valori in entrata.

Il problema principale si trova nel seguente codice:

 @RequestMapping(value = "/{id}", method = RequestMethod.POST ) public @ResponseBody ResponseEntity update(@RequestBody User updateUser) { dbUser = userRepository.findOne(updateUser.getId()); customObjectMerger(updateUser, dbUser); userRepository.saveAndFlush(updateUuser); ... } 

Nelle funzioni di cui sopra, chiamate alcune delle vostre funzioni e classi private (userRepository, customObjectMerger, …), ma non date alcuna spiegazione su come funziona o su come queste funzioni appaiono. Quindi posso solo indovinare:

CustomObjectMerger unisce questo UpdateUser con il corrispondente dbUser dal database, aggiornando gli unici campi inclusi in updateUser.

Qui non sappiamo cosa sia successo in CustomObjectMerger (questa è la tua funzione, e tu non la mostri). Ma da quello che descrivi, posso fare una supposizione: copi tutte le proprietà da updateUser al tuo object al database. Questo è assolutamente un modo sbagliato, poiché quando Spring mappa l’object, riempirà tutti i dati . E vuoi solo aggiornare alcune proprietà specifiche.

Ci sono 2 opzioni nel tuo caso:

1) Invio di tutte le proprietà (incluse le proprietà invariate) al server. Ciò potrebbe costare un po ‘più di larghezza di banda, ma tu continui a seguire la tua strada

2) È necessario impostare alcuni valori speciali come valore predefinito per l’object Utente (ad esempio, id = -1, age = -1 …). Quindi in customObjectMerger devi semplicemente impostare il valore che non è -1.

Se ritieni che le 2 soluzioni di cui sopra non siano soddisfatte, prova ad analizzare personalmente la richiesta di JSON e non preoccuparti del meccanismo di mapping degli oggetti Spring. A volte confonde molto.

Gli aggiornamenti parziali possono essere risolti usando la funzionalità @SessionAttributes , che è stata creata per fare ciò che hai fatto tu stesso con customObjectMerger .

Guarda la mia risposta qui, specialmente le modifiche, per iniziare:

https://stackoverflow.com/a/14702971/272180