Personalizar las relaciones con @JoinColumn

JPA anotación @JoinColumnMediante la anotación @JoinColumn es posible personalizar las columnas que será utilizadas como uniones con otras tablas. Cuando trabajamos con relaciones como @ManyToOne o @OneToOne, es necesario indicarle a JPA como es que tendrá que realizar la unión (JOIN) con la otra Entidad.

 

Nota: Este artículo pertenece a un tutorial completo de JPA, el cual puedes ver aquí.

 

La anotación @JoinColumn  se puede llegar a confundir con @Column , sin embargo, tiene una funcionalidad diferente, @JoinColumn  se utiliza para marcar una propiedad la cual requiere de un JOIN para poder accederlas, mientas que @Column  se utiliza para representar columnas simples que no están relacionadas con otra Entidad. A pesar de que se utiliza para cosas distintas, la realidad es que se parecen muchísimo, pues tiene casi las mismas propiedades, por que podríamos decir que @JoinColumn  es el equivalente de @Column  cuando utilizamos relaciones con Entidades.

 

En la entrada pasada de este tutorial explicamos el caso de la Entidad Factura (Invoice ) y Cliente (Customer ), donde dijimos que toda factura está relacionada a un cliente, y utilizamos la anotación @ManyToOne  para crear la relación, recordemos como quedaron estas Entidades:

package com.obb.jpa.jpaturorial.entity;
 
import java.util.Calendar;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
 
@Entity
@Table(name="INVOICES")
public class Invoice {
    
    @Id
    @Column(name="ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    
    @Column(name = "STATUS", length = 20, nullable = false)
    @Enumerated(EnumType.STRING)
    private Status status;
    
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "REGIST_DATE", nullable = false)
    private Calendar registDate;
    
    @ManyToOne(optional = false, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Customer customer;
 
    /** GET and SET */
}

 

Entidad Customer:

package com.obb.jpa.jpaturorial.entity;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
 
@Entity
@Table(name = "CUSTOMERS")
public class Customer {
    
    @Id
    @Column(name = "ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    
    @Column(name = "NAME", nullable = false, length = 100)
    private String name;
 
    /** GET and SET */
}

 

Podemos ver claramente que la Entidad Invoice  tiene una relación @ManyToOne  con la Entidad Customer , esto le indica a JPA que requerirá hacer un JOIN para poder recuperar al Cliente, sin embargo, no hemos definido la anotación @JoinColumn  y aun así trabaja, ¿cómo es esto posible?

Para comprender esto mejor, será necesario ver como JPA auto creo las tablas al momento de crear el schema, veamos las tablas generadas:

JPA ManyToOne tables

 

Podemos ver claramente que JPA creo la columna CUSTOMER_ID en la tabla invoices, pero no solo eso, si no que además, creo por nosotros la llave foránea, esto nos puede hacer pensar que la anotación @JoinColumn  no es necesaria, y pueda que tengas razón, sin embargo, no utilizarla nos dejará a merced de como JPA genere la columna y como realice el JOIN con la tabla de empleados, lo cual será un verdadero problema si el Schema ya existe, pues muy probablemente la consulta falle al buscar la columna con un nombre diferente del que JPA asumiría.

 

Ahora bien, con @JoinColumn , podría definir el nombre exacto de la columna, si admite nulos, si es actualizable, insertable, indicar si queremos que genere o no la llave foránea o incluso, podríamos decirle sobre que Tabla debe generar el Join. Mejor veamos como quedaría la Entidad Invoice  utilizando la anotación @JoinColumn :

package com.obb.jpa.jpaturorial.entity;
 
import java.util.Calendar;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
 
@Entity
@Table(name="INVOICES")
public class Invoice {
    
    @Id
    @Column(name="ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    
    @Column(name = "STATUS", length = 20, nullable = false)
    @Enumerated(EnumType.STRING)
    private Status status;
    
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "REGIST_DATE", nullable = false)
    private Calendar registDate;
    
    @JoinColumn(name = "fk_customer", nullable = false)
    @ManyToOne(optional = false, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Customer customer;
 
    /** GET and SET */
}

 

Ahora veamos como quedaría la nueva tabla invoices:

En esta segunda tabla ya vemos que ha cambiado el nombre de la columna, adicional se ha creado el constraint para no permitir valores nulos, aun que esto último no se pueda ver en la imagen.

 

Propiedades de @JoinColumn

Ahora pasemos a analizar las propiedades que nos brinda @JoinColumn :

  • name: Indica el nombre con el que se deberá de crear la columna dentro de la tabla.
  • referencedColumnName: Se utiliza para indicar sobre que columna se realizará el Join de la otra tabla. Por lo general no se suele utilizar, pues JPA asume que la columna es el ID de la Entidad objetivo.
  • unique: Crea un constraints en la tabla para impedir valores duplicados (default false).
  • nullable: Crea un constraints en la tabla para impedir valores nulos (default true).
  • insertable: Le indica a JPA si este valor deberá guardarse en la operación de inserción (default true)
  • updatable: Le indica a JPA si el valor deberá actualizarse durante el proceso de actualización (default true)
  • columnDefinition: Esta propiedad es utiliza para indicar la instrucción SQL que se deberá utilizar la crear la columna en la base de datos. Esta nos ayuda a definir exactamente como se creará la columna sin depender de la configuración de JPA.
  • table: Le indicamos sobre que tabla deberá realizar el JOIN, normalmente no es utilizada, pues JPA asume la tabla por medio de la entidad objetivo.
  • foreignKey: Le indica a JPA si debe de crear el Foreign Key, esta propiedad recibe uno de los siguientes valores CONSTRAINT , NO_CONSTRAINT , PROVIDER_DEFAULT  definidos en la enumeración javax.persistence.ForeignKey .

 

Tambíen los quiero invitar a ver mi curso de JPA, donde explico todos estos temas aplicados con API REST, https://codmind.com/courses/jpa

👉👉 Los invito a mi Curso de Mastering JPA, donde habla de todos estos temas y crearemos un API REST para probar todos los conceptos de persistencia.
👈👈

 

Conclusiones

Hemos podido analizar como es que JPA se apoya de @JoinColumn  para permitirle al programador personalizar cada aspecto de la columna de unión, lo cual es bastante bueno para poder respetar los estándares o nomenclaturas que usamos en nuestra empresa o proyecto. Como recomendación, yo siempre insisto en definir estas anotaciones, pues no es conveniente quedarnos a merced de la implementación de JPA para que defina como crear nuestras columnas.

 

AnteriorÍndiceSiguiente

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *