Auditoria Automática no JPA: Guia Prático com @Embeddable e @EntityListeners
Muitas vezes, precisamos rastrear quem criou ou modificou um registro e quando isso ocorreu. Embora o uso de @MappedSuperclass seja comum, a utilização de tipos embutidos (@Embeddable) oferece uma alternativa elegante para reutilizar propriedades entre múltiplas entidades.
1. O Modelo de Dados de Auditoria (@Embeddable)
O primeiro passo é encapsular os campos de auditoria em uma classe anotada com @Embeddable. Isso permite que esses quatro campos (created_by, created_on, updated_by, updated_on) sejam tratados como uma única unidade reutilizável.
@Embeddable
public class Audit {
@Column(name = "created_on")
private LocalDateTime createdOn;
@Column(name = "created_by")
private String createdBy;
@Column(name = "updated_on")
private LocalDateTime updatedOn;
@Column(name = "updated_by")
private String updatedBy;
// Getters e setters omitidos para brevidade
}
2. Definindo a Interface Auditable
Para que nosso "ouvinte" de eventos (Listener) consiga manipular os dados de auditoria de forma genérica, definimos uma interface que as entidades deverão implementar.
public interface Auditable {
Audit getAudit();
void setAudit(Audit audit);
}
3. O Ouvinte de Eventos (@EntityListeners)
O AuditListener é o coração da automação. Ele utiliza anotações de ciclo de vida do JPA para interceptar operações de banco de dados:
@PrePersist: Executado antes da inserção (INSERT).
@PreUpdate: Executado antes da atualização (UPDATE).
public class AuditListener {
@PrePersist
public void setCreatedOn(Auditable auditable) {
Audit audit = auditable.getAudit();
if (audit == null) {
audit = new Audit();
auditable.setAudit(audit);
}
audit.setCreatedOn(LocalDateTime.now());
audit.setCreatedBy(LoggedUser.get()); // Utilitário para obter usuário logado
}
@PreUpdate
public void setUpdatedOn(Auditable auditable) {
Audit audit = auditable.getAudit();
audit.setUpdatedOn(LocalDateTime.now());
audit.setUpdatedBy(LoggedUser.get());
}
}
4. Aplicando às Entidades
Agora, basta aplicar a anotação @EntityListeners e implementar a interface Auditable em suas entidades, como no exemplo da classe Post:
@Entity(name = "Post")
@Table(name = "post")
@EntityListeners(AuditListener.class)
public class Post implements Auditable {
@Id
private Long id;
@Embedded
private Audit audit;
private String title;
// Getters e setters
}
5. Resultados na Prática
Ao persistir ou atualizar uma entidade, o Hibernate cuida automaticamente das colunas de auditoria.
Exemplo de INSERT gerado automaticamente:
INSERT INTO post (created_by, created_on, updated_by, updated_on, title, id)
VALUES ('Joao', '2026-04-20 11:17:40.552', NULL, NULL, 'Título do Post', 1)
Exemplo de UPDATE gerado automaticamente:
UPDATE post
SET created_by = 'Joao', created_on = '2026-04-20 09:17:40.552',
updated_by = 'Maria', updated_on = '2026-04-20 09:17:40.605',
title = 'Título Atualizado'
WHERE id = 1
Conclusão
Esta abordagem permite reutilizar tanto a estrutura de dados (via @Embeddable) quanto o comportamento (via @EntityListeners). É uma solução limpa que mantém suas entidades de negócio focadas, enquanto a infraestrutura de auditoria funciona silenciosamente nos bastidores.
Caramelo