Hibernate – OneToOne, OneToMany, ManyToOne y ManyToMany

0
865
hibernate

Analizaremos como funcionan algunas de las anotaciones que proporciona JPA que nos permiten manejar las relaciones de nuestra aplicación.

Índice de Contenidos

  1. Introducción
  2. Relaciones
    1. @OneToOne
    2. @OneToMany – @ManyToOne
    3. @OneToMany (unidireccional)
    4. @ManyToMany

 

1. Introducción

A través de las anotaciones que proporciona JPA cuando usamos Hibernate, podemos gestionar las relaciones entre dos tablas como si de objetos se tratasen. Esto facilita el mapeo de atributos de base de datos con el modelo de objetos de la aplicación. Dependiendo de la lógica de negocio y cómo modelemos, se podrán crear relaciones unidireccionales o bidireccionales.

 

2. Relaciones

2.1 @OneToOne (bidireccional)

La siguiente tabla muestra nuestro modelo de base de datos. student_id es la Foreign Key (a partir de ahora FK) que apunta a student.

modelo base de datos

Lo primero que deberíamos hacer es preguntarnos quién es el propietario de la relación, ya que esto determinará dónde irá la respectiva FK. Un estudiante tiene asociada una matrícula y esa matrícula está asociada a un único estudiante.

Una buena práctica es usar cascade en la entidad padre ya que nos permite propagar los cambios y aplicarlos a los hijos. En nuestro ejemplo, tuition no tiene sentido que exista si student no existe, por lo que student es el que tendrá el rol padre.

Si observamos la imagen anterior, he decidido que la FK la tenga tuition. Podemos decir que tuition es el propietario de la relación o propietario de esa FK (owning side) y student, la no propietaria de la relación, no posee esa FK (non-owning side). Pero, ¿Cómo creo una relación bidireccional en caso de que student quiera obtener las propiedades de tuition? Podríamos pensar en tener otra FK en student apuntando a tuition pero esto generaría una duplicidad innecesaria en nuestro modelo de base de datos. Para poder realizar este mapeo correctamente, entran en juego las anotaciones @JoinColumn y mappedBy.

@JoinColumn nos permite indicar el nombre de la columna a la que queremos hacer referencia en la tabla tuition

Con mappedBy, podemos establecer una relación bidireccional ya que a pesar de tener una única FK, podemos relacionar ambas tablas. Al final, el objetivo de las anotaciones es dejar claro donde está la clave que mapea las relaciones.

orphanRemoval= true especifica que la entidad hijo debe ser eliminada automáticamente por el propio ORM si ha dejado de ser referenciada por una entidad padre. p.ej., tenemos una colección de items y eliminamos uno, ese item ha dejado de tener una referencia y será eliminado. Ojo, no confundir con cascadeType que son operaciones a nivel de base de datos.

fetchType=LAZY, Recupera la entidad solo cuando realmente la necesitamos. Importante destacar que la sesión debe estar abierta para poder invocar al Getter correspondiente y recuperar la entidad, ya que hibernate usa el patrón Proxy (object proxying) . En caso contrario (al cerrar la sesión), la entidad pasaría de estado persistent a detach y se lanzaría una excepción LazyInitializationException.

Vamos a crear un test muy simple para comprobar la sentencia sql que se está ejecutando.

sql

El uso de cascade en la entidad padre, hace que al persistir student se persista también tuition.

Una alternativa en el ejemplo que acabamos de ver es usar @MapsId. Como especifiqué anteriormente, matrícula no tiene sentido  que exista si estudiante no existe, solo puede haber asociada una matrícula  por estudiante. Con @MapsId  estamos especificando a Hibernate que student_id es PK (Primary Key) de tuition pero también es FK de student. Ambas entidades compartirán el mismo valor de identificación y no nos haría falta @GeneratedValue para la generación de nuevos ids en tuition.

modelo base de datos

 

2.2 @OneToMany (bidireccional)

La siguiente tabla muestra nuestro modelo de base de datos. university_id es la FK que apunta a university.

modelo base de datos

Normalmente el owning side en este tipo de relaciones suele estar en el @ManyToOne y el mappedBy iría en la entidad padre.

 

2.3 @OneToMany (unidireccional)

modelo base de datos

En una relación unidireccional @OneToMany, la anotación @JoinColumn hace referencia a la tabla en base de datos del many (student en este caso). Por este motivo, vemos en la siguiente imagen @JoinColumn en la clase University. La clase Student únicamente tendrá los atributos id y name.

Vamos a hacer el test y comprobar las sentencias sql que se generan.

sql

¿Por qué se ejecutan los Update?

Al no indicar a student que debe tener una FK mapeando a university (como hicimos en el ejemplo anterior), Hibernate tiene que ejecutar sentencias adicionales para resolverlo. Una buena práctica es usar @ManyToOne si queremos que sea unidireccional o si no crear directamente una relación bidireccional, de este modo nos ahorraremos la ejecución de queries innecesarias

 

2.4 @ManyToMany (bidireccional)

Nuestro modelo de Base de datos es el siguiente

modelo base de datos

Con @ManyToMany debemos crear una tercera tabla para realizar el mapeo de ambas entidades. Esta tercera tabla tendrá dos FK apuntando a sus respectivas tablas padre. Por lo tanto, student_id apunta a la tabla student y course_id apunta a la tabla course.

En este ejemplo el owning side es student y es donde se usa la anotación @JoinTable. Con ella, especificamos el nombre de la tabla que realiza el mapeo (student_course). JoinColumns apunta a la tabla del owning side (student) e InverseJoinColumns apunta a la tabla inversa del owning side (course). He decidido usar el cascade Merge y Persist, pero no cascade.Remove ya que si elimino un curso, no quiero que elimine los estudiantes asociados a él.

Como podemos ver en el ejemplo, estoy usando un Set en vez de List en la asociación. Esto es porque usando List, Hibernate elimina las filas del objeto que queremos eliminar y vuelve a insertar las demás. Esto es completamente innecesario e ineficiente.

La siguiente imagen muestra las sentencias sql generadas usando List. En este caso, tenemos un estudiante asociado a 4 cursos y queremos eliminar uno de ellos.


 
Recordemos el uso de Set si queremos evitar este tipo de comportarmiento indeseado

Si quieres leer el tutorial en inglés, puedes encontrarlo en mi perfil de dev.to

Dejar respuesta

Please enter your comment!
Please enter your name here