Relación uno a uno en JPA

1. Introducción

En este tutorial, veremos diferentes formas de crear asignaciones uno a uno en JPA.

Necesitaremos una comprensión básica del marco de trabajo de Hibernate, así que consulte nuestra Guía de Hibernate 5 con Spring para obtener información adicional.

2. Descripción

Supongamos que estamos construyendo un sistema de gestión de usuarios y nuestro jefe nos pide que almacenemos una dirección de correo para cada usuario. Un usuario tendrá una dirección de correo y una dirección de correo solo tendrá un usuario vinculado.

Este es un ejemplo de una relación uno a uno, en este caso entre el usuario y las entidades de dirección .

Veamos cómo podemos implementar esto en las secciones siguientes.

3. Uso de una clave externa

3.1. Modelado con una clave externa

Echemos un vistazo al siguiente diagrama ER que representa un mapeo uno a uno basado en una clave externa:

En este ejemplo, la columna address_id en users es la clave externa a la que se debe direccionar .

3.2. Implementar con una clave externa en JPA

Primero, creemos la clase User y anotémosla apropiadamente:

@Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "address_id", referencedColumnName = "id") private Address address; // ... getters and setters } 

Tenga en cuenta que colocamos la anotación @OneToOne en el campo de la entidad relacionada, Dirección .

Además, necesitamos colocar la anotación @JoinColumn para configurar el nombre de la columna en la tabla de usuarios que se asigna a la clave principal en la tabla de direcciones . Si no proporcionamos un nombre, Hibernate seguirá algunas reglas para seleccionar uno predeterminado.

Finalmente, tenga en cuenta en la siguiente entidad que no usaremos la anotación @JoinColumn allí. Esto se debe a que solo lo necesitamos en el lado propietario de la relación de clave externa. En pocas palabras, el propietario de la columna de clave externa obtiene la anotación @JoinColumn .

La entidad Dirección resulta un poco más simple:

@Entity @Table(name = "address") public class Address { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(mappedBy = "address") private User user; //... getters and setters }

También necesitamos colocar aquí la anotación @OneToOne . Eso es porque esta es una relación bidireccional. El lado de la dirección de la relación se denomina lado no propietario .

4. Uso de una clave principal compartida

4.1. Modelado con una clave principal compartida

En esta estrategia, en lugar de crear una nueva columna address_id, marcaremos la clave principalcolumna (user_id) de la tabla de direcciones como clave externa a la tabla de usuarios :

Hemos optimizado el espacio de almacenamiento aprovechando el hecho de que estas entidades tienen una relación uno a uno entre ellas.

4.2. Implementación con una clave principal compartida en JPA

Tenga en cuenta que nuestras definiciones cambian solo ligeramente:

@Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(mappedBy = "user", cascade = CascadeType.ALL) @PrimaryKeyJoinColumn private Address address; //... getters and setters }
@Entity @Table(name = "address") public class Address { @Id @Column(name = "user_id") private Long id; //... @OneToOne @MapsId @JoinColumn(name = "user_id") private User user; //... getters and setters } 

El atributo mappedBy ahora se mueve a la clase User ya que la clave externa ahora está presente en la tabla de direcciones . También hemos agregado la anotación @PrimaryKeyJoinColumn , que indica que la clave principal de la entidad Usuario se usa como el valor de clave externa para la entidad Dirección asociada .

Todavía tenemos que definir un campo @Id en la clase Dirección , pero tenga en cuenta que esto hace referencia a la columna user_id y ya no usa la anotación @GeneratedValue . Además, en el campo que hace referencia al Usuario , agregamos la anotación @MapsId , que indica que los valores de la clave principal se copiarán de la entidad Usuario .

5. Usar una tabla de unión

Las asignaciones uno a uno pueden ser de dos tipos: opcional y obligatorio . Hasta ahora, solo hemos visto relaciones obligatorias.

Ahora, imaginemos que nuestros empleados se asocian con una estación de trabajo. Es uno a uno, pero a veces un empleado puede no tener una estación de trabajo y viceversa.

5.1. Modelado con una tabla de unión

Las estrategias que hemos comentado hasta ahora nos obligan a poner valores nulos en la columna para manejar relaciones opcionales .

Normalmente, pensamos en relaciones de muchos a muchos cuando consideramos una tabla de combinación, pero, en este caso, usar una tabla de combinación puede ayudarnos a eliminar estos valores nulos:

Ahora, siempre que tengamos una relación, haremos una entrada en la tabla emp_workstation y evitaremos nulos.en total.

5.2. Implementar con una tabla de unión en JPA

Nuestro primer ejemplo usó @JoinColumn. Esta vez, usaremos @JoinTable :

@Entity @Table(name = "employee") public class Employee { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(cascade = CascadeType.ALL) @JoinTable(name = "emp_workstation", joinColumns = { @JoinColumn(name = "employee_id", referencedColumnName = "id") }, inverseJoinColumns = { @JoinColumn(name = "workstation_id", referencedColumnName = "id") }) private WorkStation workStation; //... getters and setters }
@Entity @Table(name = "workstation") public class WorkStation { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(mappedBy = "workStation") private Employee employee; //... getters and setters }

@ JoinTable indica a Hibernate que emplee la estrategia de tabla de unión mientras mantiene la relación.

Además, Empleado es el propietario de esta relación, ya que elegimos usar la anotación de la tabla de combinación en ella.

6. Conclusión

En este tutorial, aprendimos diferentes formas de mantener una asociación uno a uno en JPA e Hibernate y cuándo usar cada uno.

El código fuente de este tutorial se puede encontrar en GitHub.