2011-07-11 6 views
16

El título de la pregunta básicamente lo dice todo. ¿Es posible en JPA/Hibernate a con gracia evitar la eliminación de una entidad de la base de datos? Lo que me gustaría es marcar la entidad como "oculta" en lugar de eliminarla realmente.JPA/Hibernate: ¿evita la eliminación en el controlador PreRemove?

También quiero que se preserve la semántica Cascade, de modo que si trato de eliminar una entidad que posee una colección de alguna otra entidad, la entidad propietaria y cada entidad en su colección se marcan como ocultas sin ningún trabajo adicional Es necesario por mi parte, más allá de implementar el controlador @PreRemove que evita la eliminación y marca la entidad como oculta.

¿Es esto posible, o necesito descubrir algún otro enfoque?

+0

¿Está intentando imitar la eliminación lógica utilizando JPA? –

+0

@Vineet Reynolds - En esencia, sí. Quiero que se guarden los registros porque la aplicación se sincroniza con los clientes móviles, y debe poder decirles a los clientes qué se ha eliminado. Podría solucionar esto guardando una lista de los identificadores de todo lo que se eliminó en una tabla separada, junto con la marca de tiempo en la que se eliminó, pero preferiría poder anular el comportamiento de eliminación para que solo oculta cosas en vez de eliminarlas. – aroth

Respuesta

12

¿Es posible en JPA/Hibernate evitar graciosamente la eliminación de una entidad de la base de datos?

Sí, siempre que evite usar EntityManager.remove(entity) esto es posible. Si usa EntityManager.remove(), el proveedor de JPA marcará el objeto para su eliminación utilizando una declaración SQL DELETE correspondiente que implica que una solución elegante no será posible una vez que marque el objeto para su eliminación.

En Hibernate, puede lograrlo usando @SQLDelete and @Where annotations. Sin embargo, esto no funcionará bien con JPA, ya que se sabe que EntityManager.find() ignora el filtro especificado en la anotación @Where.

Una solución solo de JPA implicaría, por lo tanto, agregar un marcador, es decir, una columna, en las clases de Entidad para distinguir entidades eliminadas lógicamente en la base de datos de entidades "vivas". Tendrá que usar consultas apropiadas (JPQL y nativo) para asegurarse de que las entidades eliminadas lógicamente no estarán disponibles en los conjuntos de resultados. Puede usar las anotaciones @PreUpdate y @PrePersist para enganchar en los eventos del ciclo de vida de la entidad para asegurarse de que el indicador se actualice en los eventos de persistencia y actualización. De nuevo, deberá asegurarse de no invocar el método EntityManager.remove.

que habría sugerido el uso de la anotación @PreRemove a engancharse en el caso del ciclo de vida que se activa para la eliminación de las entidades, pero utilizando un oyente entidad para evitar el borrado está plagada de problemas por las razones que se indican a continuación:

  • Si necesita evitar que ocurra el SQL DELETE en un sentido lógico, deberá persistir el objeto en la misma transacción para recrearlo *. El único problema es que no es una buena decisión de diseño hacer referencia al EntityManager en un EntityListener, y por inferencia invocar EntityManager.persist en el oyente. El razonamiento es bastante simple: podría obtener una referencia de EntityManager diferente en EntityListener y esto solo dará como resultado un comportamiento vago y confuso en su aplicación.
  • Si necesita evitar que se produzca el SQL DELETE en la transacción en sí, debe lanzar una excepción en su EntityListener. Esto generalmente termina retrasando la transacción (especialmente si la excepción es una excepción de tiempo de ejecución o una excepción de aplicación que se declara que causa reversiones), y no ofrece ningún beneficio, ya que la transacción completa se retrotraerá.

Si usted tiene la opción de usar EclipseLink en lugar de hibernación, entonces parece que una solución elegante es posible si se define una adecuada DescriptorCustomizer o utilizando la anotación AdditionalCriteria. Ambos parecen funcionar bien con las invocaciones EntityManager.remove y EntityManager.find. Sin embargo, es posible que aún necesite escribir sus consultas JPQL o nativas para dar cuenta de las entidades eliminadas lógicamente.


* Esto se describe en el JPA Wikibook on the topic of cascading Persist:

Si elimina un objeto para obtener la eliminación, si a continuación, llama a persistir en el objeto, que va a resucitar el objeto, y volverá a ser persistente. Esto puede ser deseado si es intencional, pero la especificación JPA también requiere que este comportamiento persista en cascada. Por lo tanto, si elimina un objeto, pero se olvida de eliminar una referencia de una relación de persistencia en cascada, se ignorará la eliminación.

+0

Ah, tenía la esperanza de poder hacerlo funcionar con 'EntityManager.remove()'. Pero como dices, no hay una manera confiable de obtener la instancia apropiada de 'EntityManager' en el oyente. He recorrido un largo camino como sugirió y reemplazó mis eliminaciones con actualizaciones y eso está funcionando. Sin embargo, me gustaría que JPA permitiera una solución más elegante para este caso de uso. – aroth

+0

Sí, estoy de acuerdo en que se debe incorporar una solución más elegante en JPA para que 'em.remove()' se pueda mapear a una consulta SQL nativa o una declaración JPQL, con este comportamiento personalizado que se especifica en un lugar apropiado - en la entidad, o en otro lugar. –

Cuestiones relacionadas