5.1 Momento della modifica dei dati

Quando memorizzi vari record in un database per molti anni, spesso sorgono due domande:

  • Quando è stata aggiunta questa voce al database?
  • Quando è stata modificata l'ultima volta questa voce?

Queste sono attività così frequenti che due colonne vengono aggiunte a quasi tutte le tabelle del database:

  • creato_tempo
  • ora_aggiornata

Il primo memorizza la data e l'ora in cui è stato creato il record e il secondo memorizza la data e l'ora dell'ultima modifica. E ogni classe Entity ha campi:


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

Hibernate può svolgere tutto il lavoro di controllo quando gli oggetti nel database vengono aggiornati con due annotazioni @CreationTimestampe @UpdateTimestamp.

Esempio:

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

Le colonne contrassegnate con queste annotazioni memorizzeranno sempre l'ora corretta in cui l'oggetto è stato creato e quando è stato modificato l'ultima volta.

5.2 Annotazione @PrePersist

Se hai bisogno di script più complessi per controllare l'ora di un oggetto, anche Hibernate ha annotazioni per questo caso. Possono contrassegnare i metodi di classe e Hibernate chiamerà questi metodi quando salva l'oggetto nel database. Ci sono 7 di queste annotazioni in totale:

@PrePersist Chiamato prima che l'oggetto venga salvato nel database. (INSERIRE SQL)
@PostPersist Chiamato immediatamente dopo che l'oggetto è stato salvato nel database. (INSERIRE SQL)
@PreRemove Chiamato prima di eliminare un oggetto nel database.
@PostRemove Chiamato dopo che un oggetto è stato eliminato dal database.
@PreUpdate Chiamato prima dell'aggiornamento (SQL UPDATE) di un oggetto nel database.
@PostUpdate Chiamato dopo un aggiornamento (SQL UPDATE) di un oggetto nel database.
@postload Chiamato dopo che l'oggetto è stato caricato dal database.

Scriviamo un esempio in cui diciamo a una classe il momento giusto per creare e aggiornare i suoi oggetti:

@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 Hibernate salva l'oggetto per la prima volta, chiamerà il metodo annotato con @PrePersist. Se aggiorna un oggetto esistente nel database, chiamerà il metodo contrassegnato con l'annotazione @PreUpdate.

5.3 Aggiunta dei nostri EntityListener

Se ne hai davvero bisogno, puoi separare i metodi che Hibernate chiama dall'oggetto su cui li chiama. La specifica JPA consente di dichiarare classi listener che verranno chiamate in determinati momenti durante l'elaborazione di oggetti Entity.

Se hai molti oggetti Entity simili, puoi spostarne alcuni nella classe base e aggiungere un Listener che ne controllerà il comportamento. Esempio:


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


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

Quindi per la classe BaseEntity, puoi creare una classe listener speciale:


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 puoi connettere la classe User e il suo listener usando un paio di annotazioni:


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