Saltar al contenido principal

JPA y Hibernate

Mapeo Objeto-Relacional (ORM) y Entidades

Para interactuar con bases de datos relacionales en aplicaciones Java modernas se utilizan tecnologías ORM (Object-Relational Mapping), las cuales traducen las clases y objetos del lenguaje a tablas y filas en el motor de base de datos.


1. Diferencia entre JPA y Hibernate

Existe una distinción fundamental entre la especificación y su implementación concreta:

Clic para ampliar
Explicación Detallada del Diagrama de Persistencia
  1. JPA (Jakarta Persistence API): Es una especificación del lenguaje Java (un conjunto de interfaces del paquete jakarta.persistence.* como @Entity, @Id, @Column). JPA no contiene lógica ejecutable; simplemente define el estándar de anotaciones y contratos.
  2. Hibernate ORM: Es la implementación concreta (el motor) elegida por defecto en Spring Boot. Lee las anotaciones de JPA, traduce las operaciones de objetos a sintaxis SQL según el dialecto configurado y administra la sesión de persistencia.
  3. Base de Datos Relacional: Es el motor físico donde residen los datos tabulares.

2. Mapeo de Entidades JPA y sus Anotaciones

Una Entidad es una clase POJO (Plain Old Java Object) que representa una tabla en la base de datos. Cada instancia de la clase corresponde a una fila en la tabla.

2.1. Anotaciones de Mapeo Fundamental

AnotaciónPropósitoAtributos Comunes
@EntityDeclara que la clase Java es una entidad administrada por JPA.name (nombre lógico de la entidad en JPQL)
@TableEspecifica la tabla física en la base de datos.name, schema, uniqueConstraints
@IdMarca un atributo como la clave primaria (Primary Key).N/A
@GeneratedValueEspecifica la estrategia de generación automática de la clave primaria.strategy (IDENTITY, SEQUENCE, AUTO, TABLE)
@ColumnConfigura los detalles de mapeo de la columna física.name, nullable, unique, length, precision
@JoinColumnDefine la columna de clave foránea (Foreign Key) en relaciones.name, referencedColumnName, nullable
@TransientIndica que el atributo no debe persistirse en la base de datos.N/A

3. Modelo de Base de Datos Relacional (Diagrama ER)

Antes de escribir el código en Java, visualizamos el diseño del esquema relacional de la base de datos para un dominio de catálogo de productos:

Clic para ampliar
Explicación Detallada del Diagrama Entidad-Relación
  • CATEGORIAS: Tabla padre que almacena la clasificación del producto. Relación 1 a N con PRODUCTOS.
  • PROVEEDORES: Tabla padre que representa la empresa suministradora. Relación 1 a N con PRODUCTOS.
  • PRODUCTOS: Tabla principal que contiene dos Claves Foráneas (FK): categoria_id y proveedor_id, las cuales enlazan físicamente hacia las claves primarias (PK) de las tablas padres.

4. Mapeo Completo en Código Java con JPA y Lombok

A continuación se traduce el diagrama ER a código de entidades Java utilizando anotaciones de JPA y Lombok:

4.1. Entidad Category (Tabla categorias)

src/main/java/com/icesi/app/model/Category.java
package com.icesi.app.model;

import jakarta.persistence.*;
import lombok.*;

// Marca la clase como Entidad administrada por el contexto de JPA
@Entity
// Define el nombre físico de la tabla en la base de datos relacional
@Table(name = "categorias")
// Lombok: Genera métodos de lectura Getters para todos los campos
@Getter
// Lombok: Genera métodos de modificación Setters para todos los campos
@Setter
// Lombok: Constructor vacío obligatorio requerido por el motor de reflexión de JPA
@NoArgsConstructor
// Lombok: Constructor completo con todos los atributos
@AllArgsConstructor
// Lombok: Construcción fluida de instancias mediante el patrón Builder
@Builder
public class Category {

// Define la clave primaria (Primary Key)
@Id
// Estrategia IDENTITY: Delega la autogeneración al campo AUTO_INCREMENT/SERIAL de la BD
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

// Columna obligatoria (NOT NULL) con longitud máxima de 80 caracteres
@Column(name = "nombre", nullable = false, length = 80)
private String name;

// Columna opcional de descripción detallada
@Column(name = "descripcion", length = 255)
private String description;
}

4.2. Entidad Supplier (Tabla proveedores)

src/main/java/com/icesi/app/model/Supplier.java
package com.icesi.app.model;

import jakarta.persistence.*;
import lombok.*;

@Entity
@Table(name = "proveedores")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Supplier {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

// Nombre comercial de la empresa proveedora
@Column(name = "nombre_empresa", nullable = false, length = 100)
private String companyName;

// Número de identificación tributaria (único en la base de datos)
@Column(name = "nit_ruc", unique = true, nullable = false, length = 50)
private String taxId;
}

4.3. Entidad Product con Relaciones @ManyToOne (Tabla productos)

src/main/java/com/icesi/app/model/Product.java
package com.icesi.app.model;

import jakarta.persistence.*;
import lombok.*;

@Entity
@Table(name = "productos")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Product {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

// Nombre del producto no nulo con límite de 120 caracteres
@Column(name = "nombre_producto", nullable = false, length = 120)
private String name;

// Precio unitario del producto
@Column(name = "precio", nullable = false, precision = 10, scale = 2)
private Double price;

// Código SKU único de inventario
@Column(name = "codigo_sku", unique = true, nullable = false, length = 50)
private String sku;

// Campo calculado o temporal en memoria (JPA ignorará este campo y NO creará columna en la BD)
@Transient
private String temporaryDiscountCode;

// ==========================================
// MAPEO DE RELACIONES RELACIONALES (FOREIGN KEYS)
// ==========================================

// Relación N:1 -> Muchos productos pertenecen a una Categoría
// FetchType.LAZY evita cargar la categoría de la BD hasta que sea explícitamente consultada
@ManyToOne(fetch = FetchType.LAZY)
// Especifica la columna de clave foránea (Foreign Key) 'categoria_id' en la tabla 'productos'
@JoinColumn(name = "categoria_id", nullable = false)
private Category category;

// Relación N:1 -> Muchos productos son suministrados por un Proveedor
@ManyToOne(fetch = FetchType.LAZY)
// Especifica la columna de clave foránea (Foreign Key) 'proveedor_id' en la tabla 'productos'
@JoinColumn(name = "proveedor_id", nullable = false)
private Supplier supplier;
}

Cuestionario de Autoevaluación

Cargando cuestionario...