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 @CreationTimestampe 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;
}