← Artefactos
ISIS2603 · Persistencia · JPA

@ElementCollection en JPA

Modelado de Colecciones de Tipos Básicos

¿Qué es @ElementCollection?

@ElementCollection es una anotación de JPA que permite almacenar colecciones de tipos básicos (String, Integer, Double, etc.) o embeddables que no son entidades independientes.

Característica Principal:

JPA crea automáticamente una tabla separada para almacenar los elementos de la colección, estableciendo una relación uno-a-muchos entre la entidad principal y los valores almacenados.

Diferencia con @OneToMany

A diferencia de @OneToMany, que relaciona entidades completas con su propia identidad (@Id), @ElementCollection se usa para valores simples que no necesitan existir independientemente de la entidad padre.

Aspecto @ElementCollection @OneToMany
Tipo de datos Tipos básicos o @Embeddable Entidades con @Id
Existencia independiente No, depende de la entidad padre Sí, entidad independiente
Tabla adicional Sí, automática Sí, la tabla de la entidad
Ejemplo de uso Lista de emails, tags, números Lista de pedidos, comentarios

¿Cuándo Usar @ElementCollection?

Casos de Uso Apropiados ✓

Múltiples valores de contacto

Emails, teléfonos, direcciones cuando no necesitan ser entidades independientes.

Etiquetas y categorías

Tags, palabras clave, categorías simples asociadas a un producto o publicación.

Historial de valores numéricos

Calificaciones, precios históricos, mediciones que no requieren metadatos adicionales.

Atributos multivaluados simples

URLs de redes sociales, habilidades, intereses, lenguajes de programación.

Cuándo NO Usar ✗

Relaciones con otras entidades

Si necesitas relacionar con otra entidad que tiene su propio ID, usa @OneToMany o @ManyToMany.

Datos que necesitan metadatos

Si cada elemento necesita fecha de creación, autor, estado, etc., mejor crear una entidad completa.

Grandes volúmenes de datos

Para colecciones muy grandes, el rendimiento puede verse afectado. Evalúa otras alternativas.

Consultas frecuentes independientes

Si necesitas consultar frecuentemente los elementos sin la entidad padre, considera una entidad separada.

Regla General:

Usa @ElementCollection cuando los valores en la colección no tienen significado o existencia fuera del contexto de la entidad padre.

Ejemplos de Código

Producto con Tags (Colección de Strings)

@Entity
public class Producto {

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

    private String nombre;
    private Double precio;

    @ElementCollection
    @CollectionTable(
        name = "producto_tags",
        joinColumns = @JoinColumn(name = "producto_id")
    )
    @Column(name = "tag")
    private List<String> tags;
}

Estructura en Base de Datos

Tabla: producto
idnombreprecio
1Laptop HP1200.00
2Mouse Logitech45.00
3Teclado Mecánico120.00
Tabla: producto_tags
producto_idtag
1electrónica
1computador
1portátil
2periférico
2accesorio
3periférico
3gaming

Estudiante con Calificaciones (Colección de Números)

@Entity
public class Estudiante {

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

    private String nombre;
    private String programa;

    @ElementCollection
    @CollectionTable(
        name = "calificaciones",
        joinColumns = @JoinColumn(name = "estudiante_id")
    )
    @Column(name = "nota")
    private List<Double> notas;
}

Estructura en Base de Datos

Tabla: estudiante
idnombreprograma
1María GarcíaIngeniería de Software
2Carlos LópezIngeniería de Sistemas
Tabla: calificaciones
estudiante_idnota
14.5
14.8
14.2
23.9
24.1

Ejemplo Completo: Usuario con Múltiples Colecciones

@Entity
@Table(name = "usuarios")
public class Usuario {

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

    private String nombre;
    private String nombreUsuario;

    // Colección de emails
    @ElementCollection
    @CollectionTable(
        name = "usuario_emails",
        joinColumns = @JoinColumn(name = "usuario_id")
    )
    @Column(name = "email")
    private Set<String> emails;

    // Colección de teléfonos
    @ElementCollection
    @CollectionTable(
        name = "usuario_telefonos",
        joinColumns = @JoinColumn(name = "usuario_id")
    )
    @Column(name = "telefono")
    private Set<String> telefonos;

    // Colección de habilidades
    @ElementCollection
    @CollectionTable(
        name = "usuario_habilidades",
        joinColumns = @JoinColumn(name = "usuario_id")
    )
    @Column(name = "habilidad")
    private List<String> habilidades;
}

Estructura en Base de Datos

Tabla: usuarios
idnombrenombre_usuario
1Juan Pérezjperez
Tabla: usuario_emails
usuario_idemail
1juan@empresa.com
1jperez@personal.com
Tabla: usuario_telefonos
usuario_idtelefono
1+57 300 1234567
1+57 310 7654321
Tabla: usuario_habilidades
usuario_idhabilidad
1Java
1Spring Boot
1JPA
1SQL

Estructura de Base de Datos

JPA genera automáticamente las tablas necesarias para almacenar las colecciones. Veamos cómo quedan estructuradas:

Ejemplo 1: Producto con Tags

Tabla: producto
idnombreprecio
1Laptop HP1200.00
2Mouse Logitech45.00
3Teclado Mecánico120.00
Tabla: producto_tags
producto_idtag
1electrónica
1computador
1portátil
2periférico
2accesorio
3periférico
3gaming

Ejemplo 2: Estudiante con Calificaciones

Tabla: estudiante
idnombreprograma
1María GarcíaIngeniería de Software
2Carlos LópezIngeniería de Sistemas
Tabla: calificaciones
estudiante_idnota
14.5
14.8
14.2
23.9
24.1

Ejemplo 3: Usuario con Múltiples Colecciones

Tabla: usuarios
idnombrenombre_usuario
1Juan Pérezjperez
Tabla: usuario_emails
usuario_idemail
1juan@empresa.com
1jperez@personal.com
Tabla: usuario_telefonos
usuario_idtelefono
1+57 300 1234567
1+57 310 7654321
Tabla: usuario_habilidades
usuario_idhabilidad
1Java
1Spring Boot
1JPA
1SQL
Observación Importante:

Cada tabla de colección tiene una foreign key (usuario_id, producto_id, estudiante_id) que referencia a la tabla principal. Los elementos de la colección no tienen su propio ID único, dependen completamente de la entidad padre.

Anotaciones de JPA para Colecciones

@ElementCollection

Obligatoria. Marca un atributo como una colección de elementos básicos o embeddables. Sin esta anotación, JPA no procesará la colección correctamente.

@ElementCollection
private List<String> tags;
@CollectionTable

Opcional. Especifica el nombre y estructura de la tabla que almacenará los elementos. Si no se especifica, JPA usa convenciones por defecto.

@ElementCollection
@CollectionTable(
    name = "producto_tags",           // Nombre de la tabla
    joinColumns = @JoinColumn(name = "producto_id")  // FK
)
private List<String> tags;
@Column

Opcional. Define el nombre de la columna donde se almacenarán los valores de la colección.

@ElementCollection
@Column(name = "email_address")
private Set<String> emails;
@OrderColumn

Opcional. Para listas (List), mantiene el orden de los elementos creando una columna adicional para el índice.

@ElementCollection
@OrderColumn(name = "posicion")
private List<String> habilidades;
@Enumerated

Para enums. Define cómo almacenar valores de tipo enum (como String o como entero).

@ElementCollection
@Enumerated(EnumType.STRING)
private Set<Rol> roles;

Configuración por Defecto vs. Personalizada

AspectoValor por DefectoPersonalizado
Nombre tablaentidad_atributo@CollectionTable(name="...")
Nombre columna FKentidad_id@JoinColumn(name="...")
Nombre columna valornombre_atributo@Column(name="...")

Ejemplo: Configuración Mínima

// JPA genera nombres automáticamente
@ElementCollection
private List<String> tags;

// Tabla generada: Producto_tags
// Columnas: Producto_id, tags

Ejemplo: Configuración Completa

// Control total sobre nombres
@ElementCollection
@CollectionTable(
    name = "producto_etiquetas",
    joinColumns = @JoinColumn(name = "id_producto")
)
@Column(name = "etiqueta")
private List<String> tags;

// Tabla generada: producto_etiquetas
// Columnas: id_producto, etiqueta
Recomendación:

Usa configuración personalizada en proyectos profesionales para mantener convenciones de nombres claras y consistentes con el diseño de tu base de datos.