Relación OneToOne

En esta relación, declarada mediante las anotaciones @OneToOne y @JoinColumn, podemos crear una relación de tipo uno a uno en la que una instancia de una entidad tiene una relación con una instancia de otra entidad. En este vídeo os cuento un ejemplo de uso y también el error más común.

Este curso ha sido marcado como anticuado y no está siendo revisado de forma activa. Es posible que la información pueda estar desactualizada o que los enlaces se hayan roto.

Las relaciones one-to-one en JPA nos permiten asociar dos entidades de forma exclusiva, de modo que un objeto de una entidad está vinculado a un único objeto de otra entidad. Un ejemplo clásico que podemos usar para entender este tipo de relación es el de un empleado y su dirección. En este caso, la dirección se modela como una entidad independiente, con sus propios campos, y se asocia a un empleado mediante una relación one-to-one.

Para empezar, creamos la entidad Dirección con un identificador y campos típicos como la calle, número, localidad, provincia y país. Esta entidad se mapea a una tabla llamada dirección en la base de datos, y cada campo se asocia a una columna correspondiente. Además, definimos constructores, getters, setters y un método toString para facilitar la manipulación y visualización de los datos.

@Entity
@Table(name = "direccion")
public class Direccion {

    @Id
    @Column(name = "id_direccion")
    private Long id;

    @Column(name = "calle")
    private String calle;

    @Column(name = "numero")
    private String numero;

    @Column(name = "localidad")
    private String localidad;

    @Column(name = "provincia")
    private String provincia;

    @Column(name = "pais")
    private String pais;

    public Direccion() {
    }

    public Direccion(Long id, String calle, String numero, String localidad, String provincia, String pais) {
        this.id = id;
        this.calle = calle;
        this.numero = numero;
        this.localidad = localidad;
        this.provincia = provincia;
        this.pais = pais;
    }

    // Getters y setters para cada campo

    @Override
    public String toString() {
        return "Direccion{" +
                "id=" + id +
                ", calle='" + calle + '\'' +
                ", numero='" + numero + '\'' +
                ", localidad='" + localidad + '\'' +
                ", provincia='" + provincia + '\'' +
                ", pais='" + pais + '\'' +
                '}';
    }
}

A continuación, en la entidad Empleado, añadimos un campo de tipo Direccion para establecer la relación. Para indicar que esta relación es one-to-one, usamos la anotación @OneToOne. Además, es fundamental definir cuál de las dos entidades es la dueña de la relación, es decir, cuál contiene la clave foránea que apunta a la otra. En este caso, el empleado será el dueño y tendrá una columna id_direccion que referencia a la dirección asociada.

Esto se logra con la anotación @JoinColumn, donde especificamos el nombre de la columna que actuará como clave foránea en la tabla empleado.

@Entity
@Table(name = "empleado")
public class Empleado {

    @Id
    @Column(name = "id_empleado")
    private Long id;

    @Column(name = "nombre")
    private String nombre;

    @Column(name = "apellidos")
    private String apellidos;

    @OneToOne
    @JoinColumn(name = "id_direccion")
    private Direccion direccion;

    public Empleado() {
    }

    public Empleado(Long id, String nombre, String apellidos, Direccion direccion) {
        this.id = id;
        this.nombre = nombre;
        this.apellidos = apellidos;
        this.direccion = direccion;
    }

    // Getters y setters

    @Override
    public String toString() {
        return "Empleado{" +
                "id=" + id +
                ", nombre='" + nombre + '\'' +
                ", apellidos='" + apellidos + '\'' +
                ", direccion=" + direccion +
                '}';
    }
}

Cuando intentamos persistir un empleado con una dirección asociada, puede surgir un problema si la dirección aún no está guardada en la base de datos. Por defecto, JPA no guarda automáticamente las entidades relacionadas, por lo que al intentar guardar el empleado, se produce un error porque la dirección referenciada no existe todavía.

Una solución sencilla es persistir primero la dirección y luego el empleado, asegurándonos de que la clave foránea en la tabla empleado apunte a una dirección ya existente.

Direccion direccion = new Direccion(15L, "Calle Falsa", "123", "Springfield", "Springfield", "Estados Unidos");
entityManager.persist(direccion);

Empleado empleado = new Empleado(10L, "Pepe", "Pepito", direccion);
entityManager.persist(empleado);

Sin embargo, esta forma no es muy práctica ni escalable. Para facilitar la gestión de entidades relacionadas, JPA ofrece el mecanismo de cascada. Con la propiedad cascade en la anotación @OneToOne, podemos indicarle a JPA que cuando guardemos o eliminemos un empleado, también realice esas operaciones automáticamente sobre la dirección asociada.

Por ejemplo, al usar cascade = CascadeType.ALL, cualquier operación que hagamos sobre el empleado se propagará a la dirección. Esto incluye persistir, eliminar, actualizar, etc.

@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "id_direccion")
private Direccion direccion;

Con esta configuración, podemos crear un empleado con una dirección nueva y simplemente persistir el empleado. JPA se encargará de guardar también la dirección sin que tengamos que hacerlo explícitamente.

Además, el mecanismo de cascada es útil para mantener la integridad de los datos. Por ejemplo, si eliminamos un empleado, la dirección asociada también se eliminará automáticamente, evitando que queden registros huérfanos en la base de datos.

Este enfoque nos permite trabajar con relaciones one-to-one de forma más natural y segura, simplificando el código y asegurando que las entidades relacionadas se gestionen correctamente.

En próximos pasos, podríamos explorar relaciones bidireccionales, donde la entidad Direccion también tenga una referencia al Empleado que la posee, permitiendo navegar en ambos sentidos. Pero por ahora, con lo que hemos visto, ya podemos crear y gestionar relaciones one-to-one básicas y eficientes en JPA.

Lista de reproducción
  1. 1
    La persistencia es clave
    6 minutos
  2. 2
    Instalando Hibernate
    8 minutos
  3. 3
    Crear el persistence.xml
    9 minutos
  4. 4
    Construyendo una Entity
    8 minutos
  5. 5
    Accediendo al EntityManager
    9 minutos
  6. 6
    Insertando con persist
    7 minutos
  7. 7
    Managed Entities
    6 minutos
  8. 8
    Merge y remove
    5 minutos
  9. 9
    Inciso sobre Java 8
    7 minutos
  10. 10
    Introducción a relaciones
    6 minutos
  11. 11
    Relación OneToOne
    11 minutos
  12. 12
    OneToOne inverso con mappedBy
    8 minutos
  13. 13
    OneToMany: planteamiento
    6 minutos
  14. 14
    OneToMany: anotaciones
    6 minutos
  15. 15
    OneToMany: EntityManager
    7 minutos
  16. 16
    ¡AYUDA! Error Lazy Initialization (OneToMany)
    5 minutos
  17. 17
    ¡AYUDA! No se fijan las relaciones (OneToMany)
    8 minutos
  18. 18
    Borrar hijos en un OneToMany
    11 minutos
  19. 19
    ON DELETE SET NULL
    8 minutos