5.1 Hora de cambio de datos

Cuando almacena varios registros en una base de datos durante muchos años, a menudo surgen dos preguntas:

  • ¿Cuándo se agregó esta entrada a la base de datos?
  • ¿Cuándo se modificó por última vez esta entrada?

Estas son tareas tan frecuentes que se agregan dos columnas a casi todas las tablas de la base de datos:

  • tiempo_creado
  • tiempo_actualizado

El primero almacena la fecha y hora en que se creó el registro y el segundo almacena la fecha y hora en que se modificó por última vez. Y cada clase de Entidad tiene campos:


@Entity
@Table(name = "entities")	
public class Entity {
  ...
 
  @Column(name="created_time")
  private Date created;
 
  @Column(name="updated_time")
  private Date updated;
}

Hibernate puede hacer todo el trabajo de controlar cuándo se actualizan los objetos en la base de datos con dos anotaciones @CreationTimestampy @UpdateTimestamp.

Ejemplo:

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

Las columnas marcadas con estas anotaciones siempre almacenarán la hora correcta en que se creó el objeto y cuándo se modificó por última vez.

5.2 Anotación @PrePersist

Si necesita algunos scripts más complejos para controlar el tiempo de un objeto, Hibernate también tiene anotaciones para este caso. Pueden marcar métodos de clase e Hibernate llamará a estos métodos cuando guarde el objeto en la base de datos. Hay 7 anotaciones de este tipo en total:

@prepersistir Llamado antes de que el objeto se guarde en la base de datos. (INSERTAR SQL)
@PostPersist Llamado inmediatamente después de que el objeto se guarde en la base de datos. (INSERTAR SQL)
@PreQuitar Llamado antes de eliminar un objeto en la base de datos.
@PublicarEliminar Llamado después de que un objeto ha sido eliminado de la base de datos.
@PreActualización Llamado antes de actualizar (SQL UPDATE) un objeto en la base de datos.
@PostUpdate Llamado después de una actualización (ACTUALIZACIÓN SQL) de un objeto en la base de datos.
@PostLoad Llamado después de que el objeto se haya cargado desde la base de datos.

Escribamos un ejemplo donde le decimos a una clase la hora correcta para crear y actualizar sus 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();
  }
}

Si Hibernate guarda el objeto por primera vez, llamará al método anotado con @PrePersist. Si actualiza un objeto existente en la base de datos, llamará al método marcado con la anotación @PreUpdate.

5.3 Agregando nuestros EntityListeners

Si realmente lo necesita, puede separar los métodos que llama Hibernate del objeto en el que los llama. La especificación JPA le permite declarar clases de escucha que se llamarán en ciertos momentos al procesar objetos de Entidad.

Si tiene muchos objetos Entity similares, puede mover algunos de ellos a la clase base y agregar un Listener que controlaría su comportamiento. Ejemplo:


@MappedSuperclass
public abstract class BaseEntity {
 
    private Timestamp createdOn;
 
    private Timestamp updatedOn;
 
}


@Entity
public class User extends BaseEntity {
 
     @Id
     private Long id;
 
     private String name;
}

Luego, para la clase BaseEntity, puede crear una clase de escucha 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)   );
    }
}

Y puede conectar la clase User y su oyente usando un par de anotaciones:


@Entity
@EntityListeners(class= TimeEntityListener.class)
public class User extends BaseEntity {
 
     @Id
     private Long id;
 
     private String name;
}