Entity¶
Una Entity es una clase Java que representa una tabla dentro de una base de datos relacional. En el contexto de JPA, una entidad permite mapear los datos de la base de datos a objetos Java, facilitando el trabajo con la información mediante programación orientada a objetos en lugar de sentencias SQL directas.
Cada instancia de una entidad corresponde a un registro de la tabla y cada uno de sus atributos representa una columna. JPA, a través de un proveedor como Hibernate, se encarga automáticamente de gestionar este mapeo, así como las operaciones de inserción, consulta, actualización y eliminación.
Ejemplo¶
package co.dynaco.marshando.auth.auth.entity;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Where(clause = "status = 0")
@SQLDelete(sql = "UPDATE application SET status = 1 WHERE id=?")
public class Application {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private String url;
private String code;
private String internalCode;
private Status status;
}
Llave primaria (Primary Key)¶
Toda entidad debe tener una llave primaria, la cual identifica de forma única cada registro. En este caso, la llave primaria es un campo numérico generado automáticamente por la base de datos. Este identificador permite a JPA reconocer cada objeto y gestionar correctamente su persistencia y relaciones.
Soft Delete (Borrado lógico)¶
@Where(clause = "status = 0")
@SQLDelete(sql = "UPDATE application SET status = 1 WHERE id=?")
private long id;
El soft delete es una estrategia de eliminación lógica en la que los registros no se eliminan físicamente de la base de datos. En lugar de eso, se actualiza un campo de estado para indicar que el registro ya no está activo.
Gracias a esta estrategia: - Los datos no se pierden. - Se puede mantener trazabilidad e historial. - Las consultas normales solo retornan registros activos.
Los registros marcados como eliminados permanecen en la base de datos, pero son excluidos automáticamente de las consultas estándar.
Filtro automático de registros¶
Mediante un filtro a nivel de entidad, JPA se asegura de que solo se consulten los registros con estado activo. Esto evita que el desarrollador tenga que agregar condiciones manuales en cada consulta, manteniendo el código más limpio y consistente.
Uso de Lombok en la Entity¶
Lombok se utiliza para reducir código repetitivo, generando automáticamente constructores, getters y setters. Esto mejora la legibilidad de la entidad y permite enfocarse en el modelado de los datos en lugar de en código boilerplate.
Idea clave¶
Una Entity es el punto central del mapeo objeto–relacional en JPA. Permite representar tablas como clases Java, manejar claves primarias, aplicar estrategias como soft delete y mantener una capa de persistencia desacoplada y limpia dentro de la aplicación.
Relaciones¶
package co.inphotech.marshando.user.user.entity;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToOne;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Where(clause = "status = 0")
@SQLDelete(sql = "UPDATE user SET status = 1 WHERE id=?")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String companyCode;
private String code;
private String names;
private String lastNames;
private String email;
private String phone;
private String password;
private Status status;
@OneToOne
@JoinColumn(name = "id_company_unique")
private Company company_unique;
@ManyToOne
@JoinColumn(
name = "id_company",
referencedColumnName = "id",
unique = false,
nullable = false
)
private Company company;
}
Relación @OneToOne¶
La relación @OneToOne representa una asociación uno a uno entre dos entidades. Esto significa que un registro de una entidad está asociado con un único registro de otra entidad, y viceversa.
Este tipo de relación se utiliza cuando los datos están estrechamente ligados y no tiene sentido que existan múltiples registros relacionados. Un ejemplo típico es una entidad principal que tiene exactamente un conjunto de datos adicionales asociados.
En la base de datos, esta relación suele implementarse mediante una clave foránea única, garantizando que no existan múltiples asociaciones para un mismo registro.
Relación @ManyToOne¶
La relación @ManyToOne indica que muchas instancias de la entidad actual están asociadas a una única instancia de otra entidad. Es el lado "muchos" en una relación muchos-a-uno y suele ser el lado propietario (owning side) de la relación, ya que contiene la clave foránea.
Ejemplo: comentarios pertenecen a un post
@Entity
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String text;
@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "post_id", nullable = false)
private Post post;
}
En este ejemplo cada Comment referencia a un Post. La columna post_id en la tabla comment mantiene la relación.
En el lado inverso (one-to-many) del Post se puede mapear así:
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();
}
Notas importantes:
- mappedBy indica que Post es el lado inverso y no mantiene la clave foránea.
- cascade y orphanRemoval ayudan a propagar operaciones y gestionar hijos huérfanos.
- Por defecto @ManyToOne es EAGER en JPA tradicional, pero es buena práctica usar FetchType.LAZY para evitar consultas innecesarias.
Relación @OneToMany¶
La relación @OneToMany es el lado "uno" de una relación uno-a-muchos. Normalmente se declara en la entidad padre y suele ser el lado inverso cuando se usa mappedBy.
Ejemplo práctico (ya mostrado arriba): Post tiene muchas Comment.
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();
Consejos:
- Evitar @OneToMany con @JoinColumn en colecciones grandes porque puede complicar actualizaciones; usar el patrón bidireccional con la clave foránea en la entidad hija.
- Usar FetchType.LAZY en colecciones para mejorar el rendimiento.
Relación @ManyToMany¶
La relación @ManyToMany se usa cuando muchas instancias de una entidad están relacionadas con muchas instancias de otra entidad. En la base de datos esto se modela mediante una tabla intermedia (join table).
Ejemplo: Student y Course tienen una relación muchos-a-muchos.
Lado propietario con @JoinTable:
@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<>();
}
Lado inverso:
@Entity
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@ManyToMany(mappedBy = "courses")
private Set<Student> students = new HashSet<>();
}
Notas:
- Por defecto @ManyToMany usa FetchType.LAZY.
- Para añadir atributos a la relación (por ejemplo fecha de inscripción) es recomendable modelar la relación con una entidad intermedia (tabla asociativa) en lugar de @ManyToMany directo.