2015-01-07 38 views
28

C'è un modo per aggiornare solo alcuni campi di un oggetto entità utilizzando il metodo save da primavera dati JPA?JPA: aggiornare solo i campi specifici

Per esempio ho un entità JPA simili:

@Entity 
public class User { 

    @Id 
    private Long id; 

    @NotNull 
    private String login; 

    @Id 
    private String name; 

    // getter/setter 
    // ... 
} 

L'repo CRUD:

public interface UserRepository extends CrudRepository<User, Long> { } 

In Spring MVC Ho un controller che ottiene un oggetto User per l'aggiornamento esso :

@RequestMapping(value = "/rest/user", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE) 
@ResponseBody 
public ResponseEntity<?> updateUser(@RequestBody User user) { 

    // Assuming that user have its id and it is already stored in the database, 
    // and user.login is null since I don't want to change it, 
    // while user.name have the new value 

    // I would update only its name while the login value should keep the value 
    // in the database 
    userRepository.save(user); 

    // ... 
} 

Lo so che Potrei caricare l'utente usando findOne, quindi cambiare il suo nome e aggiornarlo usando save ... Ma se ho 100 campi e voglio aggiornarne 50, potrebbe essere molto noioso cambiare ogni valore ..

C'è non c'è modo di dire qualcosa come "saltare tutti i valori nulli quando si salva l'oggetto"?

+3

No, non c'è. L'unico modo corretto è recuperare l'oggetto, aggiornare i campi e memorizzarlo e ciò potrebbe essere dinamico. Se vuoi qualcos'altro, dovresti scrivere SQL e farlo da solo. Ma supponendo che 'User' abbia solo' null' per i campi che non si desidera salvare ciò che si potrebbe fare è il contrario, utilizzare l'utente in entrata e aggiornare i campi che non si sono modificati, quindi aggiornare quella. –

+0

C'è un motivo per cui è meglio non saltare i valori nulli aggiornando un oggetto? – Andrea

+0

Non esiste un modo affidabile per sapere cosa saltare, null può essere un'opzione valida per i campi, quindi cosa? Probabilmente potresti calzare qualcosa con ann 'EntityListener' o qualcosa del genere, ma ti consiglio vivamente di non farlo perché probabilmente causerà più problemi di quanti ne risolva. –

risposta

1

il problema non è relativo ai dati di primavera jpa ma alla lib di implementazione jpa che si sta utilizzando correlata. In caso di ibernazione si può dare un'occhiata a:

http://www.mkyong.com/hibernate/hibernate-dynamic-update-attribute-example/

+0

Grazie per il riferimento .. In realtà sto usando l'ibernazione. In quel post è suggerito di cambiare la proprietà 'dynamic-update': ho appena provato a farlo usando le annotazioni ma quella proprietà non è supportata nell'annotazione @Entity di JPA e l'annotazione @Entity di Hibernate è deprecata .... Conoscete modo corretto per impostarlo? – Andrea

+0

Come suggerito in un'altra risposta, il modo effettivo di cambiare 'dynamic-update' sta usando l'annotazione' @ DynamicUpdate' ... Ho provato ma non ho visto nessuna modifica ... Qualche suggerimento? – Andrea

23

ho avuto la stessa domanda e, come sottolinea M. Deinum, la risposta è no, non è possibile utilizzare salvare. Il problema principale è che Spring Data non saprebbe cosa fare con null. Il valore nullo non è impostato o è impostato perché deve essere cancellato?

Ora, a giudicare dalla tua domanda, presumo che tu abbia avuto lo stesso pensiero che ho avuto, ovvero che il salvataggio mi avrebbe permesso di evitare di impostare manualmente tutti i valori modificati.

Quindi è possibile evitare tutte le mappe manuel quindi? Bene, se si sceglie di aderire alla convenzione che null significa sempre "non impostato" e si ha l'id del modello originale, allora sì. È possibile evitare qualsiasi mappatura utilizzando Springs BeanUtils.

Si potrebbe fare la seguente:

  1. leggere l'oggetto esistente
  2. Usa BeanUtils per copiare i valori
  3. Salva l'oggetto

Ora, BeanUtils di Primavera reali non non supporta copiando valori nulli, quindi sovrascriverà tutti i valori non impostati con null sull'oggetto modello esistente. Per fortuna, c'è una soluzione qui:

How to ignore null values using springframework BeanUtils copyProperties?

Quindi mettere tutto insieme si finirebbe con qualcosa di simile

@RequestMapping(value = "/rest/user", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE) 
@ResponseBody 
public ResponseEntity<?> updateUser(@RequestBody User user) { 

    User existing = userRepository.read(user.getId()); 
    copyNonNullProperties(user, existing); 
    userRepository.save(existing); 

    // ... 
} 

public static void copyNonNullProperties(Object src, Object target) { 
    BeanUtils.copyProperties(src, target, getNullPropertyNames(src)); 
} 

public static String[] getNullPropertyNames (Object source) { 
    final BeanWrapper src = new BeanWrapperImpl(source); 
    java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors(); 

    Set<String> emptyNames = new HashSet<String>(); 
    for(java.beans.PropertyDescriptor pd : pds) { 
     Object srcValue = src.getPropertyValue(pd.getName()); 
     if (srcValue == null) emptyNames.add(pd.getName()); 
    } 
    String[] result = new String[emptyNames.size()]; 
    return emptyNames.toArray(result); 
} 
+4

Credo che dovrebbe essere 'userRepository.save (esistente);' not '#save (utente);' – dbahdano

+0

Il commento sopra funziona per me grazie mille. –

+0

se il tipo di dati è intero il cui valore predefinito è zero viene aggiornato in mysql. Funziona bene per stringhe su come possiamo farlo per altri tipi di dati come int, char, double. –

2

Usando JPA si può fare in questo modo.

CriteriaBuilder builder = session.getCriteriaBuilder(); 
CriteriaUpdate<User> criteria = builder.createCriteriaUpdate(User.class); 
Root<User> root = criteria.from(User.class); 
criteria.set(root.get("lastSeen"), date); 
criteria.where(builder.equal(root.get("id"), user.getId())); 
session.createQuery(criteria).executeUpdate(); 
Problemi correlati