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.ALLsignifica que todas las acciones que realizamos con el objeto principal deben repetirse para sus objetos dependientes.

CascadeType.PERSISTsignifica 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.MERGEsignifica 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.REMOVEsignifica que si eliminamos un objeto padre en la base de datos, entonces se debe hacer lo mismo con sus objetos dependientes.

CascadeType.DELETEsignifica lo mismo. Estos son sinónimos. Sólo de diferentes especificaciones.

CascadeType.DETACHsignifica 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.REFRESHy CascadeType.SAVE_UPDATEfuncionan 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.