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 *