Come aggiorno un'entità usando spring-data-jpa?


196

Bene, la domanda dice praticamente tutto. Usando JPARepository come posso aggiornare un'entità?

JPARepository ha solo un metodo di salvataggio , che non mi dice se è effettivamente creato o aggiornato. Ad esempio, inserisco un semplice oggetto per l'utente del database, che ha tre campi: firstname, lastnamee age:

 @Entity
 public class User {

  private String firstname;
  private String lastname;
  //Setters and getters for age omitted, but they are the same as with firstname and lastname.
  private int age;

  @Column
  public String getFirstname() {
    return firstname;
  }
  public void setFirstname(String firstname) {
    this.firstname = firstname;
  }

  @Column
  public String getLastname() {
    return lastname;
  }
  public void setLastname(String lastname) {
    this.lastname = lastname;
  }

  private long userId;

  @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
  public long getUserId(){
    return this.userId;
  }

  public void setUserId(long userId){
    this.userId = userId;
  }
}

Quindi chiamo semplicemente save(), che a questo punto è in realtà un inserimento nel database:

 User user1 = new User();
 user1.setFirstname("john"); user1.setLastname("dew");
 user1.setAge(16);

 userService.saveUser(user1);// This call is actually using the JPARepository: userRepository.save(user);

Fin qui tutto bene. Ora voglio aggiornare questo utente, diciamo cambiare la sua età. A tale scopo, potrei usare una query, QueryDSL o NamedQuery, qualunque cosa. Ma, considerando che voglio solo usare spring-data-jpa e JPARepository, come posso dirlo che invece di un inserto voglio fare un aggiornamento?

In particolare, come faccio a dire a spring-data-jpa che gli utenti con lo stesso nome utente e nome sono in realtà EQUAL e che l'entità esistente dovrebbe essere aggiornata? Sostituire uguali non ha risolto questo problema.


1
Sei sicuro che l'ID venga riscritto quando salvi un oggetto esistente nel database ?? Mai avuto questo nel mio progetto tbh
Byron Voorbach il

@ByronVoorbach hai ragione, ho appena provato questo. aggiorna anche la domanda, grazie
Eugene il

2
Ciao amico, si può guardare questo link stackoverflow.com/questions/24420572/... si può essere un approccio simile saveOrUpdate ()
ibrahimKiraz


Penso che abbiamo una bella soluzione qui: inserisci la descrizione del link qui
Clebio Vieira,

Risposte:


208

L'identità delle entità è definita dalle loro chiavi primarie. Dato firstnamee lastnamenon sono parti della chiave primaria, non si può dire APP a trattare Users con gli stessi firstnames e lastnames come uguali se hanno differenti userIds.

Quindi, se si desidera aggiornare un Useridentificato dal suo firstnamee lastname, è necessario trovarlo Usertramite una query, quindi modificare i campi appropriati dell'oggetto trovato. Queste modifiche verranno automaticamente scaricate nel database al termine della transazione, pertanto non è necessario fare nulla per salvare queste modifiche in modo esplicito.

MODIFICARE:

Forse dovrei approfondire la semantica generale dell'APP. Esistono due approcci principali per la progettazione delle API di persistenza:

  • approccio di inserimento / aggiornamento . Quando è necessario modificare il database, è necessario chiamare esplicitamente i metodi dell'API di persistenza: chiamare insertper inserire un oggetto o updateper salvare il nuovo stato dell'oggetto nel database.

  • Approccio dell'unità di lavoro . In questo caso, hai una serie di oggetti gestiti dalla libreria di persistenza. Tutte le modifiche apportate a questi oggetti verranno automaticamente scaricate nel database alla fine di Unit of Work (ovvero alla fine della transazione corrente nel caso tipico). Quando è necessario inserire un nuovo record nel database, si gestisce l' oggetto corrispondente . Gli oggetti gestiti sono identificati dalle loro chiavi primarie, in modo che se si crea un oggetto con chiave primaria predefinita gestita , verrà associato al record del database dello stesso ID e lo stato di questo oggetto verrà propagato automaticamente a quel record.

L'APP segue quest'ultimo approccio. save()in Spring Data JPA è supportato da merge()JPA semplice, quindi rende la tua entità gestita come descritto sopra. Significa che la chiamata save()a un oggetto con ID predefinito aggiornerà il record del database corrispondente anziché inserirne uno nuovo e spiegherà anche perché save()non viene chiamato create().


bene sì, penso di saperlo. Mi riferivo rigorosamente a spring-data-jpa. Ho due problemi con questa risposta ora: 1) i valori aziendali non dovrebbero far parte della chiave primaria - questa è una cosa nota, giusto? Quindi avere il nome e il cognome come chiave primaria non va bene. E 2) Perché questo metodo non viene quindi chiamato create, ma salva invece in spring-data-jpa?
Eugene,

1
"save () in Spring Data JPA è supportato da merge () in semplice JPA" hai effettivamente guardato il codice? L'ho appena fatto ed è stato eseguito il backup di persistenza o unione. Persisterà o si aggiornerà in base alla presenza dell'id (chiave primaria). Questo, penso, dovrebbe essere documentato nel metodo save. Quindi il salvataggio è effettivamente TUTTO unisci o persisti.
Eugene,

Penso che sia anche chiamato save, perché dovrebbe salvare l'oggetto indipendentemente dallo stato in cui si trova: eseguirà un aggiornamento o un inserimento che equivale a salvare lo stato.
Eugene,

1
Questo non funzionerà per me. Ho provato a salvare un'entità con una chiave primaria valida. Accedo alla pagina con "order / edit /: id" e in realtà mi dà l'oggetto corretto per ID. Nulla che io provi per l'amore di Dio aggiornerà l'entità. Pubblica sempre una nuova entità .. Ho anche provato a creare un servizio personalizzato e ad utilizzare "unisci" con il mio EntityManager e ancora non funzionerà. Invierà sempre una nuova entità.
DtechNet,

1
@DTechNet Stavo riscontrando un problema simile a te, DtechNet, e il mio problema è stato che avevo il tipo di chiave primaria errato specificato nell'interfaccia del repository Spring Data. Diceva extends CrudRepository<MyEntity, Integer>invece di extends CrudRepository<MyEntity, String>come avrebbe dovuto. Questo aiuta? So che è quasi un anno dopo. Spero che aiuti qualcun altro.
Kent Bull,

141

Poiché la risposta di @axtavt si concentra su JPAnospring-data-jpa

Aggiornare un'entità eseguendo una query, quindi il salvataggio non è efficiente perché richiede due query e probabilmente la query può essere piuttosto costosa poiché può unirsi ad altre tabelle e caricare tutte le raccolte che hanno fetchType=FetchType.EAGER

Spring-data-jpasupporta l'operazione di aggiornamento.
Devi definire il metodo nell'interfaccia del repository e annotarlo con @Querye @Modifying.

@Modifying
@Query("update User u set u.firstname = ?1, u.lastname = ?2 where u.id = ?3")
void setUserInfoById(String firstname, String lastname, Integer userId);

@Queryè per la definizione query personalizzata ed @Modifyingè per dire spring-data-jpache questa query è un'operazione di aggiornamento e richiede executeUpdate()no executeQuery().

È possibile specificare altri tipi di restituzione:
int- il numero di record da aggiornare.
boolean- vero se è presente un record in fase di aggiornamento. Altrimenti, falso.


Nota : eseguire questo codice in una transazione .


10
Assicurati di eseguirlo in transazione
hussachai,

1
Hey! Grazie sto usando i bean di dati di primavera. Quindi si occuperà automaticamente del mio aggiornamento. <S estende T> S salva (entità S); si occupa automaticamente dell'aggiornamento. non ho dovuto usare il tuo metodo! Grazie comunque!
bks4line,

3
In qualsiasi momento :) Il metodo di salvataggio funziona se si desidera salvare l'entità (delegherà la chiamata a em.persist () o em.merge () dietro la scena). Comunque, la query personalizzata è utile quando si desidera aggiornare solo alcuni campi nel database.
hussachai,

che ne dite quando uno dei vostri parametri è id di subentità (manyToOne) come dovrebbe aggiornarlo? (il libro ha un autore e hai passato l'ID del libro e l'ID dell'autore per aggiornare l'autore del libro)
Mahdi

1
To update an entity by querying then saving is not efficientqueste non sono le uniche due scelte. C'è un modo per specificare id e ottenere l'oggetto riga senza interrogarlo. Se fai un row = repo.getOne(id)e poi row.attr = 42; repo.save(row);e guardi i registri, vedrai solo la query di aggiornamento.
Nurettin,

21

Puoi semplicemente usare questa funzione con la funzione JPA save (), ma l'oggetto inviato come parametro deve contenere un ID esistente nel database altrimenti non funzionerà, perché save () quando inviamo un oggetto senza ID, aggiunge direttamente una riga in database, ma se inviamo un oggetto con un ID esistente, cambia le colonne già trovate nel database.

public void updateUser(Userinfos u) {
    User userFromDb = userRepository.findById(u.getid());
    // crush the variables of the object found
    userFromDb.setFirstname("john"); 
    userFromDb.setLastname("dew");
    userFromDb.setAge(16);
    userRepository.save(userFromDb);
}

4
le prestazioni non sono un problema se devi caricare l'oggetto dal database prima di eseguire l'aggiornamento? (scusate il mio inglese)
agosto 0490,

3
ci sono due query invece di una, che non è di
preferenza

so che ho mostrato un altro metodo da fare! ma perché jpa ha implementato la funzione di aggiornamento quando l'id è lo stesso?
Kalifornium,

17

Come già accennato da altri, lo save()stesso contiene sia operazioni di creazione che aggiornamento.

Voglio solo aggiungere un supplemento su cosa c'è dietro il save()metodo.

Innanzitutto, vediamo la gerarchia di estensione / implementazione di CrudRepository<T,ID>, inserisci qui la descrizione dell'immagine

Ok, controlliamo l' save()implementazione su SimpleJpaRepository<T, ID>,

@Transactional
public <S extends T> S save(S entity) {

    if (entityInformation.isNew(entity)) {
        em.persist(entity);
        return entity;
    } else {
        return em.merge(entity);
    }
}

Come puoi vedere, controllerà se l'ID è esistito o meno in primo luogo, se l'entità è già lì, verrà eseguito solo l'aggiornamento per merge(entity)metodo e, in caso contrario, verrà inserito un nuovo record per persist(entity)metodo.



5

Ecco come ho risolto il problema:

User inbound = ...
User existing = userRepository.findByFirstname(inbound.getFirstname());
if(existing != null) inbound.setId(existing.getId());
userRepository.save(inbound);

Utilizzare il @Transactionmetodo sopra per diverse richieste di database. In questo caso non è necessario userRepository.save(inbound);, le modifiche vengono cancellate automaticamente.
Grigory Kislin,

5

Il save()metodo dei dati di primavera ti aiuterà a eseguire entrambi: l'aggiunta di un nuovo elemento e l'aggiornamento di un elemento esistente.

Chiama il save()e goditi la vita :))


In questo modo se ho inviato diversi Idlo salverò, come posso evitare di salvare un nuovo record.
Abd Abughazaleh,

1
@AbdAbughazaleh controlla se esiste un ID in entrata nel tuo repository o meno. puoi usare 'repository.findById (id) .map (entità -> {// fai qualcosa restituisce repository.save (entità)}) .orElseGet (() -> {// fai qualcosa restituisce;}); '
Amir Mhp

1
public void updateLaserDataByHumanId(String replacement, String humanId) {
    List<LaserData> laserDataByHumanId = laserDataRepository.findByHumanId(humanId);
    laserDataByHumanId.stream()
            .map(en -> en.setHumanId(replacement))
            .collect(Collectors.toList())
            .forEach(en -> laserDataRepository.save(en));
}

@JoshuaTaylor in effetti, mi è sfuggito del tutto :) rimuoverà il commento ...
Eugene,

1

In particolare, come faccio a dire a spring-data-jpa che gli utenti che hanno lo stesso nome utente e nome sono in realtà EQUAL e che dovrebbe aggiornare l'entità. Sovrascrivere uguali non ha funzionato.

Per questo scopo particolare si può introdurre una chiave composita come questa:

CREATE TABLE IF NOT EXISTS `test`.`user` (
  `username` VARCHAR(45) NOT NULL,
  `firstname` VARCHAR(45) NOT NULL,
  `description` VARCHAR(45) NOT NULL,
  PRIMARY KEY (`username`, `firstname`))

Mappatura:

@Embeddable
public class UserKey implements Serializable {
    protected String username;
    protected String firstname;

    public UserKey() {}

    public UserKey(String username, String firstname) {
        this.username = username;
        this.firstname = firstname;
    }
    // equals, hashCode
}

Ecco come usarlo:

@Entity
public class UserEntity implements Serializable {
    @EmbeddedId
    private UserKey primaryKey;

    private String description;

    //...
}

JpaRepository sarebbe simile a questo:

public interface UserEntityRepository extends JpaRepository<UserEntity, UserKey>

Quindi, è possibile utilizzare il seguente linguaggio: accettare DTO con le informazioni utente, estrarre nome e nome e creare UserKey, quindi creare un UserEntity con questa chiave composita e quindi invocare Spring Data save () che dovrebbe sistemare tutto per te.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.