Saltar al contenido principal

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;
EstrategiaDescripciónBDD típica
IDENTITYAuto-incrementoMySQL, PostgreSQL
SEQUENCESecuencia BDDPostgreSQL, Oracle
TABLETabla de secuenciasTodas
UUIDIdentificador UUIDTodas
AUTOElección automáticaTodas

@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;
tip

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ámetroDescripción
mappedByLado inverso de la relación (sin columna FK)
cascadePropagación de operaciones (PERSIST, MERGE, REMOVE, ALL)
fetchLAZY (carga diferida) o EAGER (carga inmediata)
orphanRemovalElimina huérfanos automáticamente
optionalLa 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ámetroDescripción
readOnlyOptimización para lecturas
propagationComportamiento de la transacción (REQUIRED, REQUIRES_NEW, etc.)
isolationNivel de aislamiento (READ_COMMITTED, SERIALIZABLE, etc.)
timeoutDuración máxima en segundos
rollbackForExcepciones que disparan rollback
noRollbackForExcepciones 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;
}
EstrategiaDescripciónRendimiento
SINGLE_TABLEUna tabla para toda la jerarquíaRápido (sin JOIN)
TABLE_PER_CLASSUna tabla por subclaseMedio
JOINEDTabla base + tablas por subclase con JOINNormalizado 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ónRol
@EntityClase = tabla BDD
@TableConfiguración de la tabla
@IdClave primaria
@GeneratedValueGeneración automática del ID
@ColumnConfiguración de una columna
@EnumeratedMapeo de un enum
@OneToOneRelación 1:1
@OneToManyRelación 1:N
@ManyToOneRelación N:1
@ManyToManyRelación N:N
@JoinColumnColumna de unión (FK)
@JoinTableTabla de unión
@QueryConsulta personalizada
@ModifyingConsulta UPDATE/DELETE
@TransactionalGestión de transacciones
@TransientCampo no persistido

➡️ Capítulo 4: Anotaciones Security, Test y Avanzadas


⬅️ Volver al Módulo 17