5.1 Hora da alteração dos dados
Quando você armazena vários registros em um banco de dados por muitos anos, muitas vezes surgem duas perguntas:
- Quando esta entrada foi adicionada ao banco de dados?
- Quando esta entrada foi alterada pela última vez?
Essas são tarefas tão frequentes que duas colunas são adicionadas a quase todas as tabelas do banco de dados:
- hora_criada
- hora_atualizada
A primeira armazena a data e a hora em que o registro foi criado e a segunda armazena a data e a hora em que foi modificado pela última vez. E cada classe Entity possui campos:
@Entity
@Table(name = "entities")
public class Entity {
...
@Column(name="created_time")
private Date created;
@Column(name="updated_time")
private Date updated;
}
O Hibernate pode fazer todo o trabalho de controle quando os objetos no banco de dados são atualizados com duas anotações @CreationTimestamp
e arquivos @UpdateTimestamp
.
Exemplo:
@Entity
@Table(name = "entities")
public class Entity {
...
@CreationTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "create_date")
private Date createDate;
@UpdateTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "modify_date")
private Date modifyDate;
}
As colunas marcadas com essas anotações sempre armazenarão a hora correta em que o objeto foi criado e quando foi modificado pela última vez.
5.2 Anotação @PrePersist
Se você precisar de alguns scripts mais complexos para controlar o tempo de um objeto, o Hibernate também possui anotações para esse caso. Eles podem marcar métodos de classe, e o Hibernate chamará esses métodos quando salvar o objeto no banco de dados. Existem 7 dessas anotações no total:
@PrePersist | Chamado antes que o objeto seja salvo no banco de dados. (INSERIR SQL) |
@PostPersist | Chamado imediatamente após o objeto ser salvo no banco de dados. (INSERIR SQL) |
@PreRemove | Chamado antes de excluir um objeto no banco de dados. |
@PostRemove | Chamado depois que um objeto foi excluído do banco de dados. |
@PreUpdate | Chamado antes de atualizar (SQL UPDATE) um objeto no banco de dados. |
@PostUpdate | Chamado após uma atualização (SQL UPDATE) de um objeto no banco de dados. |
@postload | Chamado depois que o objeto foi carregado do banco de dados. |
Vamos escrever um exemplo onde informamos a uma classe a hora correta de criar e atualizar seus objetos:
@Entity
@Table(name = "entities")
public class Entity {
...
@Column(name="created_time")
private Date created;
@Column(name="updated_time")
private Date updated;
@PrePersist
protected void onCreate() {
created = new Date();
}
@PreUpdate
protected void onUpdate() {
updated = new Date();
}
}
Se o Hibernate salvar o objeto pela primeira vez, ele chamará o método anotado com @PrePersist
. Se atualizar um objeto existente no banco de dados, chamará o método marcado com a anotação @PreUpdate
.
5.3 Adicionando nossos EntityListeners
Se você realmente precisar, então você pode separar os métodos que o Hibernate chama do objeto no qual ele os chama. A especificação JPA permite declarar classes de ouvintes que serão chamadas em determinados momentos ao processar objetos Entity.
Se você tiver muitos objetos Entity semelhantes, poderá mover alguns deles para a classe base e adicionar um Ouvinte que controlaria seu comportamento. Exemplo:
@MappedSuperclass
public abstract class BaseEntity {
private Timestamp createdOn;
private Timestamp updatedOn;
}
@Entity
public class User extends BaseEntity {
@Id
private Long id;
private String name;
}
Então, para a classe BaseEntity, você pode criar uma classe ouvinte especial:
public class TimeEntityListener {
public void onPersist(Object entity) {
if (entity instanceof BaseEntity) {
BaseEntity baseEntity = (BaseEntity) entity;
baseEntity.createdOn = now();
}
}
public void onUpdate(Object entity) {
if (entity instanceof BaseEntity) {
BaseEntity baseEntity = (BaseEntity) entity;
baseEntity.updatedOn = now();
}
}
private Timestamp now() {
return Timestamp.from(LocalDateTime.now().toInstant(ZoneOffset.UTC) );
}
}
E você pode conectar a classe User e seu ouvinte usando algumas anotações:
@Entity
@EntityListeners(class= TimeEntityListener.class)
public class User extends BaseEntity {
@Id
private Long id;
private String name;
}