unidirectional remove orphanremoval onetomany one manytoone many ejemplo delete cascadetype java hibernate jpa

java - remove - onetomany jpa



Hibernate JPA: @OneToMany delete old, insert new sin color (2)

En realidad, nunca entendí bien este comportamiento en hibernación. Estoy usando una relación @OneToMany en una entidad llamada ''Parent'', que se anota así:

@OneToMany(cascade = {CascadeType.ALL, CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE }, orphanRemoval = true) @JoinColumn(name = "entity_id", insertable = true, updatable = true, nullable = false) private List<Child> children;

Ahora quiero hacer lo siguiente dentro de una transacción:

  • Obtener la entidad padre
  • iterar a través de la lista de niños
  • eliminar uno de los niños
  • inserta un nuevo niño

Entonces, básicamente estoy reemplazando completamente a uno de los niños.

Por lo que entiendo este problema, debería ser capaz de hacer algo como esto: (tenga en cuenta que esto es solo un pseudocódigo java para ilustrar el problema)

@TransactionAttribute(TransactionAttributeType.REQUIRED) public void deleteAndAdd(Long parentId, Long childId) { Parent parent = entityManager.find(parentId); for (Iterator it = parent.children.iterator(); it.hasNext();) { Child child = it.next(); if (child.id == childId) { it.remove(); } } Child newChild = new Child(); parent.children.add(newChild); }

Sin embargo, esto falla en caso de que el nuevo Niño tenga los mismos valores clave únicos que el anterior. Así que, básicamente, parece que la entidad del antiguo hijo no se elimina correctamente, antes de que persista el nuevo.

Si agrego un entityManager.flush () entre eliminar el hijo anterior y persistir al nuevo hijo de esta manera:

@TransactionAttribute(TransactionAttributeType.REQUIRED) public void deleteAndAdd(Long parentId, Long childId) { Parent parent = entityManager.find(parentId); for (Iterator it = parent.children.iterator(); it.hasNext();) { Child child = it.next(); if (child.id == childId) { it.remove(); } } entityManager.flush(); Child newChild = new Child(); parent.children.add(newChild); }

Todo funciona bien El hijo se elimina antes de que se inserte el nuevo, como debería.

Como no quiero suponer que hibernación mezcla el orden de las declaraciones que se envían a la base de datos, debe haber algo más que supongo sobre la hibernación, que no es el caso. ¿Alguna idea de por qué el último ejemplo funciona, mientras que el primero no?

La versión de Hibernate es 3.5. DB es Mysql InnoDB


Hibernate no conoce ni respeta todas las restricciones de la base de datos (por ejemplo, las restricciones únicas de MySQL). Es un problema conocido que no planean abordar en el corto plazo.

Hibernate tiene un orden definido para la forma en que las operaciones ocurren durante una descarga.

Las eliminaciones de entidades siempre ocurrirán después de las inserciones. Las únicas respuestas que conozco son para eliminar la restricción o agregar el color adicional.

EDITAR: por cierto, la razón para el orden definido es que esta es la única manera de garantizar que las restricciones de clave externa (una de las restricciones que realmente les importan) no se violen, incluso si el usuario hace algo fuera de servicio.


Por el bien de los lectores en el futuro, una forma de resolver este problema es utilizar restricciones diferidas. PostgreSQL y Oracle los admiten, tal vez otros RDBMS también. Hibernate emitirá todas las declaraciones dentro de una transacción, y el aplazamiento garantizará que las restricciones se apliquen solo al comprometer la transacción. En PostgreSQL, por ejemplo:

ALTER TABLE company ADD CONSTRAINT name_unique UNIQUE (name) DEFERRABLE INITIALLY DEFERRED;

No es ideal, pero es simple y efectivo.