web-dev-qa-db-fra.com

L'utilisation de saveOrUpdate () dans Hibernate crée de nouveaux enregistrements au lieu de mettre à jour les enregistrements existants

J'ai un utilisateur de classe

class User { 
  int id;
  String name;
}

id est le générateur natif dans User.hbm.xml et name est la clé primaire dans DB.

Dans ma base de données, j'ai enregistré des informations sur les utilisateurs.

Que je souhaite me connecter avec ces informations sur l'utilisateur.

Par exemple, dans ma base de données, j'ai une ligne INSERT INTO User VALUES ('Bill');

Main.Java

User bill = new User();
bill.setName("Bill");
session.saveOrUpdate(bill);

Ce code essaie toujours d'insérer une nouvelle ligne Bill dans la table plutôt que de mettre à jour la ligne Bill existante.

16
kunkanwan

Ce code essaie toujours d'insérer le projet de loi dans la base de données, plutôt que de le mettre à jour lorsqu'une ligne sur le projet de loi existe dans la base de données ...

De la section 10.7. Détection automatique de l'état de la documentation de base d'Hibernate:

saveOrUpdate() fait ce qui suit:

  • si l'objet est déjà persistant dans cette session, ne faites rien
  • si un autre objet associé à la session a le même identifiant, lève une exception
  • si l'objet n'a pas de propriété d'identifiant, save() it
  • si l'identifiant de l'objet a la valeur attribuée à un objet nouvellement instancié, save() it
  • si l'objet est versionné par un <version> ou <timestamp>, et que la valeur de la propriété version est la même valeur affectée à un objet nouvellement instancié, save() it
  • sinon update() l'objet

Quand vous faites:

User bill = new User();
bill.setName("Bill");
session.saveOrUpdate(bill);

Cette instance nouvellement créée n'a pas de valeur d'identifiant affectée et saveOrUpdate() la save(), comme indiqué. Si ce n'est pas ce que vous voulez, faites de name la clé primaire.

30
Pascal Thivent

Alors tu veux:

User u1 = new User("Bill");
session.save(u1);
User u2 = new User("Bill");
session.saveOrUpdate(u2);

remarquer que u1 et u2 sont la même facture et ne la stocker qu'une seule fois? Hibernate n'a aucun moyen de savoir que les factures sont la même personne. Il ne regarde que l'ID de l'utilisateur u2, voit qu'il n'est pas défini, conclut que u2 doit être inséré, essaie cela et signale une exception.

SaveOrUpdate conserve à la fois un objet complètement nouveau et un objet chargé qui est actuellement attaché à la session. Donc cela fonctionne (en supposant que vous ayez une méthode findByName quelque part et qu'il y ait un autre attribut, disons favoriteColor):

User u1 = new User("Bill");
u1.setFavoriteColor("blue");
session.saveOrUpdate(u1);
User u2 = findByName("Joe");
u2.setFavoriteColor("red");
session.saveOrUpdate(u2);

Mais ce n'est pas ce que tu veux, non?

Votre problème est aggravé par le fait que votre entité possède une clé primaire de substitution et, séparément, une clé d'entreprise (appliquée via une contrainte d'unicité). Si vous n'aviez pas de champ id et que seul le nom comme clé primaire, vous pouvez utiliser merge () (pour une discussion sur saveOrUpdate vs merge, regardez ici ):

User u1 = new User("Bill");
u1.setFavoriteColor("blue");
session.save(u1);
User u2 = new User("Bill");
u2.setFavoriteColor("red");
u2 = session.merge(u2);

Mais vous n'avez pas cela, vous devez appliquer à la fois la contrainte PK sur id et l'unicité sur le nom. Vous aurez donc besoin d'une variante de fusion qui le fera. En gros, vous aimeriez quelque chose dans ce sens:

public User mergeByName(User user) {
    User merged;
    User candidate = findByName(user.getName());
    if (candidate != null) {
        user.setId(candidate.getId());
        merged = session.merge(user);
    } else {
        session.save(user);
        merged = user;
    }
    return merged;
}
17
wallenborn

Donc, l'ID n'est pas enregistré dans la base de données?

Pourquoi l'ID n'est-il pas la clé primaire dans de DB, si vous la mappez dans Hibernate comme étant l'identifiant?

Pourquoi n'avez-vous pas simplement mis une contrainte unique sur "Nom" dans la base de données et créé une contrainte de clé primaire sur Id?

1

Première question: si vous devez récupérer un utilisateur nommé "Bill". Comment feriez-vous cela? Cela devrait vous donner une idée.

Pour mettre à jour, vous devez avoir une identité associée à l'objet utilisateur. Le fait de n'avoir que le jeu d'attributs de nom ne va pas aider. Si vous souhaitez mettre à jour sans avoir l'identifiant, utilisez HQL comme requête.

Query query = session.createQuery("update User u set u.name = :newName where u.name=:name");
query.setString("name", "Bill");
query.setString("newName", "John");
query.executeUpdate();
1
Kartik