java - ejemplos - jpql order by
JPA: ELIMINAR DONDE no elimina niños y arroja una excepción (5)
¿Ha intentado usar session.delete()
, o EntityManager.remove() equivalente?
Cuando utiliza una instrucción de eliminación HQL para emitir una consulta, es posible que esté pasando por alto el mecanismo de cascada de Hibernate. Eche un vistazo a este número de JIRA: HHH-368
Posiblemente podrá lograr esto al:
Mother mother = session.load(Mother.class, id);
// If it is a lazy association,
//it might be necessary to load it in order to cascade properly
mother.getChildren();
session.delete(mother);
No estoy seguro ahora mismo si es necesario inicializar la colección para hacerla en cascada correctamente.
Estoy intentando eliminar una gran cantidad de filas de MOTHER
gracias a una consulta JPQL.
La clase Mother
se define de la siguiente manera:
@Entity
@Table(name = "MOTHER")
public class Mother implements Serializable {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "mother",
orphanRemoval = true)
private List<Child> children;
}
@Entity
@Table(name = "CHILD")
public class Child implements Serializable {
@ManyToOne
@JoinColumn(name = "MOTHER_ID")
private Mother mother;
}
Como puede ver, la clase Mother
tiene "hijos" y al ejecutar la siguiente consulta:
String deleteQuery = "DELETE FROM MOTHER WHERE some_condition";
entityManager.createQuery(deleteQuery).executeUpdate();
se lanza una excepción:
ERROR - ORA-02292: integrity constraint <constraint name> violated -
child record found
Por supuesto, podría seleccionar primero todos los objetos que quiero eliminar y recuperarlos en una lista antes de recorrerlos para eliminar todo el objeto recuperado, ¡pero el rendimiento de tal solución sería terrible!
Entonces, ¿hay alguna manera de aprovechar el mapeo anterior para eliminar todos los objetos Mother
Y todos los objetos Child
asociados con ellos de manera eficiente y sin escribir primero las consultas para todos los niños?
BORRAR (e INSERTAR) no se conecta en cascada a través de las relaciones en la consulta JPQL. Esto está claramente deletreado en la especificación:
Una operación de eliminación solo se aplica a las entidades de la clase especificada y sus subclases. No se conecta en cascada a las entidades relacionadas.
Afortunadamente persisten y eliminan a través del administrador de entidades do (cuando hay un atributo en cascada definido).
Lo que puedes hacer:
- buscar todas las instancias de la entidad madre que se deben eliminar.
- para cada uno de ellos llame a EntityManager.remove ().
El código es algo como esto:
String selectQuery = "SELECT m FROM Mother m WHERE some_condition";
List<Mother> mothersToRemove = entityManager.createQuery(selectQuery).getResultList();
for (Mother m: mothersToRemove) {
em.remove(m);
}
Debo decir que no estoy seguro de si ''eliminar'' en una consulta no eliminará todas las entidades onetomany relacionadas en su caso como dice ''MikKo Maunu''. Yo diría que sí. El problema es (lo siento por no probar esto) que lo que hará JPA / Hibernate es simplemente ejecutar la ''eliminación sql real'' y mientras esas instancias Madre e Hijo no se gestionen en ese momento, no tiene forma de saber qué Niño instancias para eliminar también. orphanRemoval es de gran ayuda, pero no en este caso. me gustaría
1) intenta agregar ''fetch = FetchType.EAGER'' en la relación onetomany (esto también podría ser un problema de rendimiento)
2) si 1) no funciona, no haga todo el recorrido de Madre / Hijo para dejar todo claro para la capa JPA, y simplemente ejecute una consulta antes de la que usa (en la misma transacción, pero no estoy seguro si no necesita ejecutar ''em.flush'' entre ellos)
DELETE FROM Child c WHERE c.mother <the condition>
(Las eliminaciones son a menudo una molestia con JPA / Hibernate y un ejemplo que utilizo para denunciar el uso de ORM, que es esencialmente una capa adicional en las aplicaciones, para facilitar las cosas. Lo único bueno es que los problemas / errores de ORM generalmente se descubren durante la fase de desarrollo. Mi dinero siempre está en MyBatis, que es mucho más limpio en mi opinión).
ACTUALIZAR:
Mikko Maunu tiene razón, la eliminación masiva en JPQL no se realiza en cascada. Sin embargo, usar dos consultas como sugerí está bien.
Lo difícil es que ese contexto de persistencia (todas las entidades administradas por EntityManager) no está sincronizado con lo que hace la eliminación masiva, por lo que (ambas consultas en el caso que sugiero) deben ejecutarse en una transacción separada.
ACTUALIZACIÓN 2: si se utiliza la eliminación manual en lugar de la eliminación masiva, muchos proveedores de JPA e Hibernate también proporcionan el método removeAll (...) o algo similar (no API) en sus implementaciones de EntityManager. Es más fácil de usar y podría ser más efectivo en cuanto a rendimiento.
En, por ejemplo, OpenJPA, solo necesita enviar su EM a OpenJPAEntityManager, lo mejor es OpenJPAPersistence.cast (em) .removeAll (...)
Esto está relacionado y puede ofrecer una solución si estás usando Hibernate.
JPA CascadeType.ALL no elimina huérfanos
EDITAR
Ya que Oracle es el que le da el error, tal vez pueda hacer uso de la eliminación en cascada de Oracle para evitar esto. Sin embargo, esto podría tener resultados impredecibles: dado que JPA no se da cuenta de que está borrando otros registros, esos objetos podrían permanecer en la memoria caché y usarse aunque se hayan eliminado. Esto solo se aplica si la implementación de JPA que está utilizando tiene un caché y está configurado para usarlo.
Aquí hay información sobre el uso de la eliminación de cascada en Oracle: http://www.techonthenet.com/oracle/foreign_keys/foreign_delete.php
Puede retransmitir en el RDBMS para eliminar esas Mother
''s usando la restricción de clave externa. Esto supone que usted genere su DDL de las entidades:
@Entity
@Table(name = "CHILD")
public class Child implements Serializable {
@ManyToOne
@JoinColumn(name = "MOTHER_ID", foreignKey = @ForeignKey(foreignKeyDefinition =
"FOREIGN KEY(MOTHER_ID) REFERENCES MOTHER(ID) ON DELETE CASCADE",
value = ConstraintMode.CONSTRAINT))
private Mother mother;
}