Básicamente, tengo algunos objetos en esta configuración (el modelo de datos real es un poco más complejo):
- A tiene una relación de muchos a muchos con B. (B tiene
inverse="true"
) - B tiene una relación de muchos a uno con C. (me he
cascade
fijado en"save-update"
) - C es una especie de tabla de tipo / categoría.
Además, probablemente debería mencionar que las claves primarias son generadas por la base de datos al guardar.
Con mis datos, a veces me encuentro con problemas en los que A tiene un conjunto de diferentes objetos B, y estos objetos B se refieren al mismo objeto C.
Cuando llamo session.saveOrUpdate(myAObject)
, me sale un error de hibernación diciendo: "a different object with the same identifier value was already associated with the session: C"
. Sé que Hibernate no puede insertar / actualizar / eliminar el mismo objeto dos veces en la misma sesión, pero ¿hay alguna forma de evitar esto? Esta no parece ser una situación tan poco común.
Durante mi investigación de este problema, he visto a personas sugerir el uso de session.merge()
, pero cuando lo hago, cualquier objeto "en conflicto" se inserta en la base de datos como objetos en blanco con todos los valores establecidos en nulos. Claramente eso no es lo que queremos.
[Editar] Otra cosa que olvidé mencionar es que (por razones arquitectónicas fuera de mi control), cada lectura o escritura debe realizarse en una sesión separada.
Respuestas:
Lo más probable es que se deba a que los objetos B no se refieren a la misma instancia de objeto Java C. Se refieren a la misma fila en la base de datos (es decir, la misma clave primaria) pero son copias diferentes de ella.
Entonces, lo que está sucediendo es que la sesión de Hibernate, que administra las entidades, estaría realizando un seguimiento de qué objeto Java corresponde a la fila con la misma clave primaria.
Una opción sería asegurarse de que las Entidades de los objetos B que hacen referencia a la misma fila en realidad se refieren a la misma instancia de objeto de C. Alternativamente, desactive la cascada para esa variable miembro. De esta forma, cuando B persiste, C no lo es. Sin embargo, tendrá que guardar C manualmente por separado. Si C es una tabla de tipo / categoría, entonces probablemente tenga sentido que sea así.
fuente
Simplemente configure cascada en MERGE, eso debería funcionar.
fuente
Solo necesitas hacer una cosa. Ejecute
session_object.clear()
y luego guarde el nuevo objeto. Esto borrará la sesión (con el nombre apropiado) y eliminará el objeto duplicado ofensivo de su sesión.fuente
Estoy de acuerdo con @Hemant Kumar, muchas gracias. Según su solución, resolví mi problema.
Por ejemplo:
@Test public void testSavePerson() { try (Session session = sessionFactory.openSession()) { Transaction tx = session.beginTransaction(); Person person1 = new Person(); Person person2 = new Person(); person1.setName("222"); person2.setName("111"); session.save(person1); session.save(person2); tx.commit(); } }
Person.java
public class Person { private int id; private String name; @Id @Column(name = "id") public int getId() { return id; } public void setId(int id) { this.id = id; } @Basic @Column(name = "name") public String getName() { return name; } public void setName(String name) { this.name = name; } }
Este código siempre comete errores en mi aplicación:,
A different object with the same identifier value was already associated with the session
luego descubrí que olvidé aumentar automáticamente mi clave principal.Mi solución es agregar este código en su clave principal:
@GeneratedValue(strategy = GenerationType.AUTO)
fuente
Esto significa que está intentando guardar varias filas en su tabla con la referencia al mismo objeto.
verifique la propiedad de identificación de su clase de entidad.
@Id private Integer id;
a
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(unique = true, nullable = false) private Integer id;
fuente
Transfiera la tarea de asignar el ID de objeto de Hibernate a la base de datos usando:
<generator class="native"/>
Esto resolvió mi problema.
fuente
Agregue la anotación @GeneratedValue al bean que está insertando.
fuente
Una forma de resolver el problema anterior será anular el
hashcode()
.También vacíe la sesión de hibernación antes y después de guardar.
Establecer explícitamente el objeto separado en
null
también ayuda.fuente
Acabo de encontrar este mensaje pero en código c #. No estoy seguro de si es relevante (aunque exactamente el mismo mensaje de error).
Estaba depurando el código con puntos de interrupción y expandí algunas colecciones a través de miembros privados mientras el depurador estaba en un punto de interrupción. Habiendo vuelto a ejecutar el código sin buscar en las estructuras, el mensaje de error desapareció. Parece que el acto de buscar en colecciones privadas con carga diferida ha hecho que NHibernate cargue cosas que se suponía que no debían cargarse en ese momento (porque estaban en miembros privados).
El código en sí está envuelto en una transacción bastante complicada que puede actualizar una gran cantidad de registros y muchas dependencias como parte de esa transacción (proceso de importación).
Con suerte, una pista para cualquier otra persona que se encuentre con el problema.
fuente
Busque el atributo "Cascade" en Hibernate y elimínelo. Cuando establece "Cascade" disponible, llamará a otras operaciones (guardar, actualizar y eliminar) en otras entidades que tienen relación con clases relacionadas. Entonces se sucederá el mismo valor de identidades. Funcionó conmigo.
fuente
Tuve este error unos días después y dediqué demasiado tiempo a solucionarlo.
public boolean save(OrderHeader header) { Session session = sessionFactory.openSession(); Transaction transaction = session.beginTransaction(); try { session.save(header); for (OrderDetail detail : header.getDetails()) { session.save(detail); } transaction.commit(); session.close(); return true; } catch (HibernateException exception) { exception.printStackTrace(); transaction.rollback(); return false; } }
Antes de recibir este error, no mencioné el tipo de generación de ID en el objeto OrderDetil. cuando sin generar la identificación de Orderdetails, mantiene la Id como 0 para cada objeto de OrderDetail. esto es lo que explicó #jbx. Sí, es la mejor respuesta. este ejemplo de cómo sucede.
fuente
Intente colocar el código de su consulta antes. Eso soluciona mi problema. por ejemplo, cambie esto:
a esto:
fuente
es posible que no esté configurando el identificador del objeto antes de llamar a la consulta de actualización.
fuente
Me encontré con el problema porque la generación de la clave principal es incorrecta, cuando inserto una fila como esta:
public void addTerminal(String typeOfDevice,Map<Byte,Integer> map) { // TODO Auto-generated method stub try { Set<Byte> keySet = map.keySet(); for (Byte byte1 : keySet) { Device device=new Device(); device.setNumDevice(DeviceCount.map.get(byte1)); device.setTimestamp(System.currentTimeMillis()); device.setTypeDevice(byte1); this.getHibernateTemplate().save(device); } System.out.println("hah"); }catch (Exception e) { // TODO: handle exception logger.warn("wrong"); logger.warn(e.getStackTrace()+e.getMessage()); } }
Cambio la clase del generador de id a la identidad
<id name="id" type="int"> <column name="id" /> <generator class="identity" /> </id>
fuente
En mi caso, solo flush () no funcionó. Tuve que usar un clear () después de flush ().
public Object merge(final Object detachedInstance) { this.getHibernateTemplate().flush(); this.getHibernateTemplate().clear(); try { this.getHibernateTemplate().evict(detachedInstance); } }
fuente
si usa EntityRepository, use saveAndFlush en lugar de guardar
fuente
Si dejaba abierta una pestaña de expresiones en mi IDE que estaba haciendo una llamada de hibernación al objeto que causaba esta excepción. Estaba intentando eliminar este mismo objeto. También tuve un punto de interrupción en la llamada de eliminación que parece ser necesario para que ocurra este error. Simplemente hacer que otra pestaña de expresiones sea la pestaña frontal o cambiar la configuración para que el ide no se detenga en los puntos de interrupción resolvió este problema.
fuente
Asegúrese de que su entidad tenga el mismo tipo de generación con todas las entidades asignadas
Ejemplo: UserRole
public class UserRole extends AbstractDomain { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String longName; private String shortName; @Enumerated(EnumType.STRING) private CommonStatus status; private String roleCode; private Long level; @Column(columnDefinition = "integer default 0") private Integer subRoleCount; private String modification; @ManyToOne(fetch = FetchType.LAZY) private TypeOfUsers licenseType;
}
Módulo:
public class Modules implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String longName; private String shortName;
}
Entidad principal con mapeo
public class RoleModules implements Serializable{ @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE) private UserRole role; @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE) private Modules modules; @Type(type = "yes_no") private boolean isPrimaryModule; public boolean getIsPrimaryModule() { return isPrimaryModule; }
}
fuente
Además de todas las respuestas anteriores, una posible solución a este problema en un proyecto a gran escala, si usa un objeto de valor para sus clases, no establece el atributo id en la clase VO Transformer.
fuente
solo confirma la transacción actual.
ahora puede comenzar otra transacción y hacer cualquier cosa en la entidad
fuente
Otro caso en el que se puede generar el mismo mensaje de error, personalizado
allocationSize
:@Id @Column(name = "idpar") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "paramsSequence") @SequenceGenerator(name = "paramsSequence", sequenceName = "par_idpar_seq", allocationSize = 20) private Long id;
sin emparejar
alter sequence par_idpar_seq increment 20;
puede causar la validación de la restricción durante la inserción (que es fácil de entender) o este "un objeto diferente con el mismo valor de identificador ya estaba asociado con la sesión" - este caso fue menos obvio.
fuente