JPA 2.0 orphanRemoval = true VS en eliminar Cascade

Estoy un poco confundido sobre el atributo orphanRemoval 2.0 orphanRemoval .

Creo que puedo ver que es necesario cuando uso las herramientas de generación de base de datos de mi proveedor de JPA para crear la base de datos subyacente DDL para tener una ON DELETE CASCADE en la relación particular.

Sin embargo, si el DB existe y ya tiene un ON DELETE CASCADE en la relación, ¿no es suficiente para conectar en cascada la eliminación de forma apropiada? ¿Qué hace el orphanRemoval además?

Aclamaciones

orphanRemoval no tiene nada que ver con ON DELETE CASCADE .

orphanRemoval es una cosa completamente específica de ORM . Marca la entidad “hijo” que se eliminará cuando ya no se haga referencia a la entidad “padre”, por ejemplo, cuando se elimina la entidad hijo de la colección correspondiente de la entidad padre.

ON DELETE CASCADE es algo específico de la base de datos , elimina la fila “secundaria” en la base de datos cuando se elimina la fila “principal”.

Un ejemplo tomado de aquí :

Cuando se elimina un objeto de entidad Employee , la operación de eliminación se aplica en cascada al objeto de entidad de Address referenciado. En este sentido, orphanRemoval=true y cascade=CascadeType.REMOVE son idénticos, y si se especifica orphanRemoval=true , CascadeType.REMOVE es redundante.

La diferencia entre las dos configuraciones está en la respuesta a desconectar una relación. Por ejemplo, como cuando se establece el campo de dirección en null o en otro objeto de Address .

  • Si se especifica orphanRemoval=true la instancia de Address desconectada se elimina automáticamente. Esto es útil para limpiar objetos dependientes (por ejemplo, Address ) que no deberían existir sin una referencia de un objeto propietario (por ejemplo, Employee ).

  • Si solo se especifica cascade=CascadeType.REMOVE , no se realiza ninguna acción automática ya que desconectar una relación no es una operación de eliminación.

Para evitar referencias suspendidas como resultado de la eliminación de huérfanos, esta característica solo debe habilitarse para los campos que contienen objetos dependientes privados no compartidos.

Espero que esto lo deje más claro.

En el momento en que elimine una entidad hija de la colección, también eliminará esa entidad hija de la base de datos. orphanRemoval también implica que no puedes cambiar a los padres; si hay un departamento que tiene empleados, una vez que elimine a ese empleado para ponerlo en otro departamento, sin quererlo habrá eliminado a ese empleado del DB en flush / commit (lo que viene primero). La moral es establecer orphanRemoval en verdadero, siempre y cuando esté seguro de que los hijos de ese padre no migrarán a un padre diferente a lo largo de su existencia. Activar orphanRemoval también agrega automáticamente REMOVE a la lista de cascadas.

El mapeo JPA equivalente para el DDL ON DELETE CASCADE es cascade=CascadeType.REMOVE . La eliminación de huérfanos significa que las entidades dependientes se eliminan cuando se destruye la relación con su entidad “principal”. Por ejemplo, si un niño es eliminado de una relación @OneToMany sin eliminarlo explícitamente en el administrador de entidades.

La respuesta de @GaryK es absolutamente genial, he pasado una hora buscando una explicación de orphanRemoval = true frente a CascadeType.REMOVE y me ayudó a entender.

En resumen: orphanRemoval = true funciona de forma idéntica a CascadeType.REMOVE SOLAMENTE SI eliminamos el objeto ( entityManager.delete(object) ) y deseamos que los objetos entityManager.delete(object) se eliminen.

En una situación completamente diferente, cuando buscamos algunos datos como List childs = object.getChilds() y luego entityManager.remove(childs.get(0) un elemento secundario ( entityManager.remove(childs.get(0) ) utilizando orphanRemoval=true , la entidad correspondiente a childs.get(0) se eliminará de la base de datos.

la eliminación de huérfanos tiene el mismo efecto que ON DELETE CASCADE en el siguiente escenario: – Digamos que tenemos una relación simple de muchos a uno entre entidad estudiantil y una entidad guía, donde muchos estudiantes pueden mapearse en la misma guía y en la base de datos tenemos un la relación de clave foránea entre el Alumno y la Tabla de guía de modo que la tabla de alumnos tenga id_guide como FK.

  @Entity @Table(name = "student", catalog = "helloworld") public class Student implements java.io.Serializable { @Id @GeneratedValue(strategy = IDENTITY) @Column(name = "id") private Integer id; @ManyToOne(cascade={CascadeType.PERSIST,CascadeType.REMOVE}) @JoinColumn(name = "id_guide") private Guide guide; 

// La entidad padre

  @Entity @Table(name = "guide", catalog = "helloworld") public class Guide implements java.io.Serializable { /** * */ private static final long serialVersionUID = 9017118664546491038L; @Id @GeneratedValue(strategy = IDENTITY) @Column(name = "id", unique = true, nullable = false) private Integer id; @Column(name = "name", length = 45) private String name; @Column(name = "salary", length = 45) private String salary; @OneToMany(mappedBy = "guide", orphanRemoval=true) private Set students = new HashSet(0); 

En este escenario, la relación es tal que la entidad del alumno es el propietario de la relación y, como tal, tenemos que guardar la entidad del alumno para poder persistir todo el gráfico del objeto, por ejemplo

  Guide guide = new Guide("John", "$1500"); Student s1 = new Student(guide, "Roy","ECE"); Student s2 = new Student(guide, "Nick", "ECE"); em.persist(s1); em.persist(s2); 

Aquí estamos mapeando la misma guía con dos objetos estudiantiles diferentes y dado que se usa CASCADE.PERSIST, el gráfico del objeto se guardará como se muestra a continuación en la tabla de la base de datos (MySql en mi caso)

Mesa de ESTUDIANTE: –

ID Name Dept Id_Guide

1 Roy ECE 1

2 Nick ECE 1

Tabla GUIDE: –

ID NOMBRE Salario

1 Juan $ 1500

y ahora si quiero eliminar a uno de los estudiantes, usando

  Student student1 = em.find(Student.class,1); em.remove(student1); 

y cuando se elimina un registro del alumno, también se debe eliminar el registro guía correspondiente, ahí es donde el atributo CASCADE.REMOVE en la entidad del alumno entra en escena y lo que hace es eliminar al estudiante con el identificador 1 así como el objeto guía correspondiente (identificador 1). Pero en este ejemplo, hay un objeto estudiante más que se asigna al mismo registro de guía y, a menos que usemos el atributo orphanRemoval = true en la Entidad de guía, el código de eliminación anterior no funcionará.

La diferencia es:
– orphanRemoval = true: la entidad “Child” se elimina cuando ya no se hace referencia (es posible que su principal no se elimine).
– CascadeType.REMOVE: la entidad “Child” se elimina solo cuando se elimina su “Parent”.