Capítulo 3: Anotaciones Data y JPA
1. Anotaciones de Entidad
@Entity
Marca una clase como entidad JPA (tabla en base de datos).
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private BigDecimal price;
}
@Table
Personaliza el nombre de la tabla y sus restricciones.
@Entity
@Table(
name = "products",
schema = "ecommerce",
uniqueConstraints = @UniqueConstraint(columnNames = {"sku"}),
indexes = @Index(name = "idx_product_name", columnList = "name")
)
public class Product {
// ...
}
@Id
Marca el campo como clave primaria.
@Id
private Long id;
@GeneratedValue
Estrategia de generación de la clave primaria.
// Auto-incremento (PostgreSQL SERIAL, MySQL AUTO_INCREMENT)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Secuencia (recomendado para PostgreSQL)
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "product_seq")
@SequenceGenerator(name = "product_seq", sequenceName = "product_sequence", allocationSize = 1)
private Long id;
// UUID
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private String id;
// La BDD elige la estrategia
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
| Estrategia | Descripción | BDD típica |
|---|---|---|
IDENTITY | Auto-incremento | MySQL, PostgreSQL |
SEQUENCE | Secuencia BDD | PostgreSQL, Oracle |
TABLE | Tabla de secuencias | Todas |
UUID | Identificador UUID | Todas |
AUTO | Elección automática | Todas |
@Column
Personaliza el mapeo de una columna.
@Column(name = "product_name", nullable = false, length = 100)
private String name;
@Column(unique = true)
private String sku;
@Column(precision = 10, scale = 2)
private BigDecimal price;
@Column(columnDefinition = "TEXT")
private String description;
@Column(updatable = false) // No puede ser modificado después de la creación
private LocalDateTime createdAt;
@Column(insertable = false) // No incluido en los INSERT
private LocalDateTime updatedAt;
@Enumerated
Mapea un enum Java a la base de datos.
public enum Role {
USER, ADMIN, MANAGER
}
@Entity
public class User {
@Enumerated(EnumType.STRING) // Almacena "USER", "ADMIN" (RECOMENDADO)
private Role role;
@Enumerated(EnumType.ORDINAL) // Almacena 0, 1, 2 (NO RECOMENDADO)
private Role roleOrdinal;
}
@Temporal
Especifica el tipo de fecha (solo para java.util.Date, innecesario con java.time).
@Temporal(TemporalType.DATE) // Solo fecha
private Date birthDate;
@Temporal(TemporalType.TIMESTAMP) // Fecha + hora
private Date createdAt;
Con Java 8+, utilice LocalDate, LocalDateTime, Instant directamente. @Temporal no es necesario.
@Lob
Indica un campo Large Object (BLOB/CLOB).
@Lob
private String longDescription; // CLOB (texto largo)
@Lob
private byte[] profileImage; // BLOB (binario)
@Transient
Excluye un campo de la persistencia (sin columna en la BDD).
@Entity
public class User {
private String firstName;
private String lastName;
@Transient
private String fullName; // Calculado, no almacenado en la BDD
public String getFullName() {
return firstName + " " + lastName;
}
}
2. Anotaciones de Relaciones
@OneToOne
Relación uno a uno.
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "address_id")
private Address address;
}
@Entity
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String street;
private String city;
@OneToOne(mappedBy = "address")
private User user;
}
@OneToMany / @ManyToOne
Relación uno a muchos.
@Entity
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "department", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Employee> employees = new ArrayList<>();
}
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "department_id")
private Department department;
}
@ManyToMany
Relación muchos a muchos.
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses = new HashSet<>();
}
@Entity
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@ManyToMany(mappedBy = "courses")
private Set<Student> students = new HashSet<>();
}
Resumen de los parámetros de relación
| Parámetro | Descripción |
|---|---|
mappedBy | Lado inverso de la relación (sin columna FK) |
cascade | Propagación de operaciones (PERSIST, MERGE, REMOVE, ALL) |
fetch | LAZY (carga diferida) o EAGER (carga inmediata) |
orphanRemoval | Elimina huérfanos automáticamente |
optional | La relación puede ser null |
3. Anotaciones de Consultas
@Query
Define una consulta JPQL o SQL nativa personalizada.
public interface UserRepository extends JpaRepository<User, Long> {
// JPQL
@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmail(@Param("email") String email);
// JPQL con JOIN
@Query("SELECT u FROM User u JOIN u.orders o WHERE o.total > :amount")
List<User> findUsersWithOrdersAbove(@Param("amount") BigDecimal amount);
// Consulta nativa SQL
@Query(value = "SELECT * FROM users WHERE created_at > :date", nativeQuery = true)
List<User> findRecentUsers(@Param("date") LocalDate date);
// Proyección (retorna parte de los datos)
@Query("SELECT new com.example.dto.UserSummary(u.id, u.name, u.email) FROM User u")
List<UserSummary> findAllSummaries();
}
@Param
Nombra un parámetro en una consulta @Query.
@Query("SELECT u FROM User u WHERE u.name = :name AND u.role = :role")
List<User> findByNameAndRole(@Param("name") String name, @Param("role") Role role);
@Modifying
Indica una consulta de modificación (UPDATE, DELETE).
@Modifying
@Transactional
@Query("UPDATE User u SET u.active = false WHERE u.lastLogin < :date")
int deactivateInactiveUsers(@Param("date") LocalDateTime date);
@Modifying
@Transactional
@Query("DELETE FROM User u WHERE u.active = false")
void deleteInactiveUsers();
@NamedQuery
Define una consulta nombrada en la entidad.
@Entity
@NamedQuery(name = "User.findActive", query = "SELECT u FROM User u WHERE u.active = true")
@NamedQuery(name = "User.countByRole", query = "SELECT COUNT(u) FROM User u WHERE u.role = :role")
public class User {
// ...
}
4. Anotaciones de Transacción y Repositorio
@Transactional
Gestiona las transacciones de base de datos.
@Service
@Transactional(readOnly = true) // Por defecto solo lectura
public class OrderService {
public List<Order> findAll() {
return orderRepository.findAll(); // Transacción solo lectura
}
@Transactional // Transacción de escritura (override readOnly)
public Order createOrder(OrderDto dto) {
Order order = new Order();
// ... lógica de creación
return orderRepository.save(order);
}
@Transactional(
propagation = Propagation.REQUIRED, // Usa la transacción existente o crea una nueva
isolation = Isolation.READ_COMMITTED, // Nivel de aislamiento
timeout = 30, // Timeout en segundos
rollbackFor = Exception.class // Rollback en cualquier excepción
)
public void processPayment(Long orderId) {
// Si se lanza una excepción, todo se revierte
}
}
| Parámetro | Descripción |
|---|---|
readOnly | Optimización para lecturas |
propagation | Comportamiento de la transacción (REQUIRED, REQUIRES_NEW, etc.) |
isolation | Nivel de aislamiento (READ_COMMITTED, SERIALIZABLE, etc.) |
timeout | Duración máxima en segundos |
rollbackFor | Excepciones que disparan rollback |
noRollbackFor | Excepciones que NO disparan rollback |
@EnableJpaRepositories
Activa los repositories de Spring Data JPA (generalmente auto-configurado por Spring Boot).
@Configuration
@EnableJpaRepositories(basePackages = "com.example.repository")
public class JpaConfig {
}
@EnableJpaAuditing
Activa la auditoría automática (fechas de creación/modificación).
@Configuration
@EnableJpaAuditing
public class JpaAuditConfig {
}
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Article {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
@CreatedBy
@Column(updatable = false)
private String createdBy;
@LastModifiedBy
private String updatedBy;
}
5. Anotaciones de Herencia JPA
@Inheritance
Estrategia de herencia entre entidades.
// SINGLE_TABLE : todas las subclases en una sola tabla (por defecto)
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type")
public abstract class Payment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private BigDecimal amount;
}
@Entity
@DiscriminatorValue("CARD")
public class CardPayment extends Payment {
private String cardNumber;
}
@Entity
@DiscriminatorValue("PAYPAL")
public class PaypalPayment extends Payment {
private String paypalEmail;
}
| Estrategia | Descripción | Rendimiento |
|---|---|---|
SINGLE_TABLE | Una tabla para toda la jerarquía | Rápido (sin JOIN) |
TABLE_PER_CLASS | Una tabla por subclase | Medio |
JOINED | Tabla base + tablas por subclase con JOIN | Normalizado pero más lento |
6. Anotaciones específicas de Hibernate
@Formula
Columna calculada del lado SQL (sin columna física).
@Formula("(SELECT COUNT(*) FROM orders o WHERE o.user_id = id)")
private int orderCount;
@Where
Filtro SQL por defecto en una colección.
@OneToMany(mappedBy = "user")
@Where(clause = "active = true")
private List<Order> activeOrders;
@BatchSize
Optimiza la carga de colecciones (previene el problema N+1).
@OneToMany(mappedBy = "department")
@BatchSize(size = 20)
private List<Employee> employees;
Resumen Rápido
| Anotación | Rol |
|---|---|
@Entity | Clase = tabla BDD |
@Table | Configuración de la tabla |
@Id | Clave primaria |
@GeneratedValue | Generación automática del ID |
@Column | Configuración de una columna |
@Enumerated | Mapeo de un enum |
@OneToOne | Relación 1:1 |
@OneToMany | Relación 1:N |
@ManyToOne | Relación N:1 |
@ManyToMany | Relación N:N |
@JoinColumn | Columna de unión (FK) |
@JoinTable | Tabla de unión |
@Query | Consulta personalizada |
@Modifying | Consulta UPDATE/DELETE |
@Transactional | Gestión de transacciones |
@Transient | Campo no persistido |
➡️ Capítulo 4: Anotaciones Security, Test y Avanzadas