El uso de saveOrUpdate () en Hibernate crea nuevos registros en lugar de actualizar los existentes

Tengo un usuario de clase

class User { int id; String name; } 

donde id es el generador nativo en User.hbm.xml y el name es la clave principal en DB.

En mi base de datos guardé alguna información sobre los usuarios.

Que quiero conectar con esta información sobre el usuario.

Por ejemplo, en mi base de datos tengo una fila INSERT INTO User VALUES ('Bill');

Main.java

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

Este código siempre intenta insertar una nueva fila de Bill en la tabla en lugar de actualizar la fila de Bill existente.

Este código siempre intenta insertar la factura en la base de datos, en lugar de actualizarse cuando existe una fila sobre la factura en la base de datos …

De la sección 10.7. Detección automática de estado de la documentación del núcleo de Hibernate:

saveOrUpdate() hace lo siguiente:

  • Si el objeto ya es persistente en esta sesión, no haga nada.
  • si otro objeto asociado con la sesión tiene el mismo identificador, lance una excepción
  • si el objeto no tiene propiedad de identificador, save()
  • Si el identificador del objeto tiene el valor asignado a un objeto de instancia reciente, save()
  • si el objeto está versionado por una o , y el valor de la propiedad de la versión es el mismo valor asignado a un objeto creado recientemente, save()
  • de lo contrario update() el objeto

Cuando tu lo hagas:

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

Esta instancia recién creada no tiene ningún valor de identificador asignado y saveOrUpdate() lo save() como se documenta. Si esto no es lo que quieres, haz que el name la clave principal.

Entonces quieres:

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

¿Notar que u1 y u2 son la misma factura y solo guardarla una vez? Hibernate no tiene forma de saber que los Bills son la misma persona. Solo mira el id del usuario u2, ve que no está configurado, concluye que se debe insertar u2, intenta eso e informa una excepción.

SaveOrUpdate conserva tanto un objeto completamente nuevo como un objeto cargado que actualmente está adjunto a la sesión. Así que esto funciona (asumiendo que tienes un método findByName en algún lugar y hay otro atributo, digamos favoriteColor):

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

Pero eso no es lo que quieres, ¿verdad?

Su problema se agrava debido a que su entidad tiene una clave principal sustituta y, por separado, una clave de negocio (impuesta a través de una restricción de unicidad). Si no tenías un campo de identificación y solo un nombre como clave principal, podrías usar merge () (para una discusión de saveOrUpdate vs merge, mira aquí ):

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

Pero no tiene eso, necesita imponer tanto la restricción de PK en id como la singularidad en nombre. Así que necesitarás alguna variación de fusión que haga eso. A grandes rasgos, te gustaría algo como esto:

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

Entonces, ¿la identificación no está guardada en la base de datos?

¿Por qué no es Id la clave principal en la base de datos, si la asigna en Hibernate como identificador?

¿Por qué no acaba de poner una restricción única en ‘Nombre’ en la base de datos, y crea una restricción de clave primaria en la identificación?

Primera pregunta: si necesita buscar un usuario llamado “Bill”. ¿Cómo lo harías tú? Eso debería darte una idea.

Para actualizar, debe tener una identidad asociada con el objeto de usuario. Tener solo el conjunto de atributos de nombre no va a ayudar. Si desea actualizar sin importar el identificador, use HQL como consulta.

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

Debe usar session.update (objeto) mientras está actualizando, y session.save (objeto) mientras está guardando

Si el campo de nombre es clave primaria. Luego, si establece el mismo nombre más de una vez, se creará un nuevo registro. Puede que no estés entendiendo bien el concepto.