web-dev-qa-db-fra.com

Comment mettre à jour une entité avec spring-data-jpa?

Eh bien, la question dit à peu près tout. Utilisation de JPARepository, comment mettre à jour une entité?

JPARepository a seulement une méthode save, ce qui ne me dit pas si c'est créer ou mettre à jour. Par exemple, j'insère un objet simple dans la base de données Utilisateur, qui comporte trois champs: prénom et nom de famille et âge:

 @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;
  }
}

Ensuite, je simplement "enregistrer", qui à ce stade est un insert en fait:

 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);

Ok tout va bien. Maintenant, je veux mettre à jour cet utilisateur, disons changer son âge. Eh bien, je pourrais utiliser une requête pour cela, QueryDSL ou NamedQuery, peu importe. Mais, étant donné que je veux juste utiliser spring-data-jpa et le JPARepository, comment puis-je lui dire qu'au lieu d'un insert, je veux faire une mise à jour?

Plus précisément, comment puis-je dire à spring-data-jpa que les utilisateurs qui ont le même nom d'utilisateur et le même prénom sont en réalité EQUAL et qu'il est supposé mettre à jour l'entité. Remplacer les égaux n'a pas fonctionné.

Merci!

160
Eugene

L'identité des entités est définie par leurs clés primaires. Puisque firstname et lastname ne font pas partie de la clé primaire, vous ne pouvez pas dire à JPA de traiter Users avec les mêmes firstnames et lastnames de manière égale s'ils ont différent userIds.

Ainsi, si vous souhaitez mettre à jour une User identifiée par ses firstname et lastname, vous devez rechercher cette User par une requête, puis modifier les champs appropriés de l'objet. votre trouvé. Ces modifications seront automatiquement transférées dans la base de données à la fin de la transaction, de sorte que vous n'aurez rien à faire pour enregistrer ces modifications explicitement.

EDIT:

Je devrais peut-être élaborer sur la sémantique générale de l'APP. Il existe deux approches principales pour la conception des API de persistance:

  • approche insertion/mise à jour. Lorsque vous devez modifier la base de données, vous devez appeler explicitement les méthodes de l'API de persistance: appelez insert pour insérer un objet ou update pour enregistrer le nouvel état de l'objet dans la base de données.

  • approche par unité de travail. Dans ce cas, vous avez un ensemble d'objets gérés par la bibliothèque de persistance. Toutes les modifications que vous apportez à ces objets seront automatiquement vidées dans la base de données à la fin de l'unité de travail (c'est-à-dire à la fin de la transaction en cours dans le cas typique). Lorsque vous devez insérer un nouvel enregistrement dans la base de données, vous rendez l'objet correspondant géré . Les objets gérés sont identifiés par leurs clés primaires. Ainsi, si vous créez un objet avec une clé primaire prédéfinie , elle est gérée , il sera associé à l'enregistrement de la base de données du même identifiant et l'état de cet objet sera propagé à cet enregistrement automatiquement.

JPA suit l'approche ultérieure. save() dans Spring Data JPA s'appuie sur merge() en clair, par conséquent, il rend votre entité gérée comme décrit ci-dessus. Cela signifie que l'appel de save() sur un objet avec un identifiant prédéfini mettra à jour l'enregistrement de base de données correspondant plutôt que d'en insérer un nouveau, et explique également pourquoi save() ne s'appelle pas create().

173
axtavt

Puisque la réponse de @axtavt se concentre sur JPA pas spring-data-jpa

Mettre à jour une entité en interrogeant, l'enregistrement n'est pas efficace, car il nécessite deux interrogations. En outre, l'interrogation peut être assez coûteuse, car elle peut joindre d'autres tables et charger toutes les collections contenant fetchType=FetchType.EAGER.

Spring-data-jpa prend en charge l'opération de mise à jour.
Vous devez définir la méthode dans l'interface du référentiel et l'annoter avec @Query et @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 sert à définir une requête personnalisée et @Modifying signifie à spring-data-jpa que cette requête est une opération de mise à jour et qu'elle nécessite executeUpdate() pas executeQuery().

107
hussachai

Vous pouvez simplement utiliser cette fonction avec la fonction JPA save (), mais l'objet envoyé en tant que paramètre doit contenir un identifiant existant dans la base, sinon cela ne fonctionnera pas, car save () lorsque nous envoyons un objet sans identifiant, il ajoute directement une ligne dans base de données, mais si nous envoyons un objet avec un identifiant existant, les colonnes déjà trouvées dans la base de données sont modifiées.

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);
}
14
Kalifornium

Comme ce qui a déjà été mentionné par d'autres, la save() contient à la fois les opérations de création et de mise à jour.

Je veux juste ajouter un complément sur ce qui se cache derrière la méthode save().

Tout d’abord, voyons la hiérarchie expand/Implement du CrudRepository<T,ID>, enter image description here

Ok, vérifions l'implémentation de save() à 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);
    }
}

Comme vous pouvez le voir, il va d'abord vérifier si l'ID existe ou non. Si l'entité est déjà présente, seule la mise à jour se fera par la méthode merge(entity) et si c'est le cas, un nouvel enregistrement est inséré par persist(entity) méthode.

12
Gearon

En utilisant spring-data-jpa save (), je rencontrais le même problème que @DtechNet. Je veux dire que chaque save () créait un nouvel objet au lieu d'une mise à jour. Pour résoudre ce problème, je devais ajouter "version" classée dans l'entité et la table associée.

7
smile

Voici comment j'ai résolu le problème:

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

la méthode des données de ressort save() vous aidera à effectuer les tâches suivantes: ajouter un nouvel élément et mettre à jour un élément existant.

Il suffit d'appeler la save() et de profiter de la vie :))

3
Amir Mhp
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));
}
1
BinLee

Plus précisément, comment puis-je dire à spring-data-jpa que les utilisateurs qui ont le même nom d'utilisateur et le même prénom sont en réalité EQUAL et qu'il est supposé mettre à jour l'entité. Remplacer les égaux n'a pas fonctionné.

Dans ce but particulier, on peut introduire une clé composite comme celle-ci:

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`))

Cartographie:

@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
}

Voici comment l'utiliser:

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

    private String description;

    //...
}

JpaRepository ressemblerait à ceci:

public interface UserEntityRepository extends JpaRepository<UserEntity, UserKey>

Ensuite, vous pouvez utiliser les éléments suivants idiom: accepter DTO avec informations utilisateur, extraire nom et prénom et créer une clé utilisateur, puis créer une UserEntity avec cette clé composite, puis appeler Spring Data save () qui doit tout trier. pour toi.

0
Andreas Gelever