6.1 Gestión profunda de dependencias
Y algunas cosas más útiles e interesantes sobre las anotaciones @OneToMany y similares. Todos ellos tienen 4 opciones de uso común:
- cascada = CascadeType.ALL
- eliminación huérfana = verdadero
- buscar = FetchType.LAZY
Ahora los analizaremos con más detalle. Y comenzaremos con el más interesante: CascadeType . Este parámetro determina qué debería pasar con las entidades dependientes si cambiamos la entidad principal.
La especificación JPA tiene los siguientes valores para este parámetro:
- ALL
- PERSIST
- MERGE
- REMOVE
- REFRESH
- DETACH
Sin embargo, Hibernate amplía esta especificación en tres opciones más:
- REPLICATE
- SAVE_UPDATE
- LOCK
Hay, por supuesto, un fuerte paralelismo con la base de datos y su CONSTRANIS. Sin embargo, también hay diferencias. Hibernate trata de ocultar el trabajo real con la base de datos tanto como sea posible, por lo que estas Cascadas de Hibernate son exactamente sobre objetos de Entidad.
6.2 Tipo de cascada
El parámetro en cascada describe lo que debería pasar con los objetos dependientes si cambiamos su padre (objeto maestro). La mayoría de las veces, este parámetro se usa junto con anotaciones que describen dependencias de objetos:
Ejemplo:
OneToOne(cascade = CascadeType.ALL)
O así:
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
También se puede escribir como una anotación separada:
@Cascade({ org.hibernate.annotations.CascadeType.ALL })
Ahora hablemos más sobre lo que significan estas anotaciones.
6.3 ALL, PERSIST, MERGE
CascadeType.ALL
significa que todas las acciones que realizamos con el objeto principal deben repetirse para sus objetos dependientes.
CascadeType.PERSIST
significa que si guardamos el objeto principal en la base de datos, entonces se debe hacer lo mismo con sus objetos dependientes. Ejemplo:
@Entity
@Table(name="employee")
class Employee {
@Column(name="id")
public Integer id;
@OneToOne(cascade = CascadeType.PERSIST, mappedBy="task")
private EmployeeTask task;
}
Un ejemplo de trabajo con esta clase:
Employee director = new Employee();
EmployeeTask task = new EmployeeTask();
director.task = task;
session.persist(director);
session.flush();
Guardamos solo un objeto de tipo Empleado, su objeto dependiente EmployeeTask se guardará en la base de datos automáticamente.
CascadeType.MERGE
significa que si actualizamos el objeto padre en la base de datos, entonces se debe hacer lo mismo con sus objetos dependientes.
6.4 REMOVE, DELETE, DETACH
CascadeType.REMOVE
significa que si eliminamos un objeto padre en la base de datos, entonces se debe hacer lo mismo con sus objetos dependientes.
CascadeType.DELETE
significa lo mismo. Estos son sinónimos. Sólo de diferentes especificaciones.
CascadeType.DETACH
significa que si eliminamos el objeto padre de la sesión, entonces se debe hacer lo mismo con sus objetos dependientes. Ejemplo:
@Entity
@Table(name="employee")
class Employee {
@Column(name="id")
public Integer id;
@OneToOne(cascade = CascadeType.DETACH, mappedBy="task")
private EmployeeTask task;
}
Un ejemplo de trabajo con esta clase:
Employee director = new Employee();
EmployeeTask task = new EmployeeTask();
director.task = task;
director.task = task;
session.flush();
assertThat(session.contains(director)).isTrue();
assertThat(session.contains(task)).isTrue();
session.detach(director);
assertThat(session.contains(director)).isFalse();
assertThat(session.contains(task)).isFalse();
CascadeType.REFRESH
y CascadeType.SAVE_UPDATE
funcionan de la misma manera que esperamos: duplican las acciones que se realizan con el objeto principal en su objeto dependiente.
6.5 Opción de eliminación de huérfanos
También a veces puede encontrar el parámetro orphan
. Esta es la abreviatura de Eliminación de huérfanos. Se utiliza para garantizar que no haya entidades secundarias sin entidades principales.
OneToOne(orphan = true)
Si este parámetro se establece en verdadero, la entidad secundaria se eliminará si ha desaparecidotodos los enlaces. No es exactamente lo mismo que Cascade.REMOVE
.
Es posible que tenga una situación en la que varias entidades principales se refieran a un hijo. Entonces es beneficioso que no se elimine junto con la eliminación de la entidad matriz, sino solo si se anulan todas las referencias a ella.
Digamos que tienes una clase:
@Entity
@Table(name="user")
class Employee {
@Column(name="id")
public Integer id;
@OneToMany(cascade = CascadeType.ALL, orphan = true)
@JoinColumn(name = "employee_id")
private Set<EmployeeTask> tasks = new HashSet<EmployeeTask>();
}
Employee director = session.find(Employee.class, 4);
EmployeeTask task = director.tasks.get(0);
director.tasks.remove(task)
session.persist(director);
session.flush();
El objeto EmployeeTask se eliminará porque no quedan referencias a él. Al mismo tiempo, nadie eliminó el objeto principal.
6.6 opción de búsqueda
La opción de obtención le permite controlar cómo se cargan los objetos dependientes. Por lo general, toma uno de dos valores:
FetchType.LAZY
FetchType.EAGER
Este es un tema muy interesante con varias trampas, por lo que será mejor que hable de él en una conferencia separada.