5.1 Moment du changement de données

Lorsque vous stockez divers enregistrements dans une base de données pendant de nombreuses années, deux questions se posent souvent :

  • Quand cette entrée a-t-elle été ajoutée à la base de données ?
  • Quand cette entrée a-t-elle été modifiée pour la dernière fois ?

Ce sont des tâches si fréquentes que deux colonnes sont ajoutées à presque toutes les tables de la base de données :

  • heure_créée
  • heure_mise_à_jour

Le premier stocke la date et l'heure de création de l'enregistrement, et le second stocke la date et l'heure de sa dernière modification. Et chaque classe Entity a des champs :


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

Hibernate peut faire tout le travail de contrôle du moment où les objets de la base de données sont mis à jour avec deux annotations @CreationTimestampet @UpdateTimestamp.

Exemple:

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

Les colonnes marquées avec ces annotations stockeront toujours l'heure correcte à laquelle l'objet a été créé et quand il a été modifié pour la dernière fois.

5.2 Annotation @PrePersist

Si vous avez besoin de scripts plus complexes pour contrôler l'heure d'un objet, alors Hibernate a également des annotations pour ce cas. Ils peuvent marquer des méthodes de classe, et Hibernate appellera ces méthodes lorsqu'il enregistrera l'objet dans la base de données. Il y a 7 annotations de ce type au total :

@PrePersist Appelé avant que l'objet ne soit enregistré dans la base de données. (SQL INSÉRER)
@PostPersist Appelé immédiatement après l'enregistrement de l'objet dans la base de données. (SQL INSÉRER)
@PreRemove Appelé avant de supprimer un objet dans la base de données.
@PostRemove Appelé après qu'un objet a été supprimé de la base de données.
@PreUpdate Appelé avant la mise à jour (SQL UPDATE) d'un objet dans la base de données.
@PostUpdate Appelé après une mise à jour (SQL UPDATE) d'un objet dans la base de données.
@postload Appelé après le chargement de l'objet à partir de la base de données.

Écrivons un exemple où nous indiquons à une classe le bon moment pour créer et mettre à jour ses objets :

@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 enregistre l'objet pour la première fois, il appellera la méthode annotée avec @PrePersist. S'il met à jour un objet existant dans la base de données, il appellera la méthode marquée avec l'annotation @PreUpdate.

5.3 Ajout de nos EntityListeners

Si vous en avez vraiment besoin, vous pouvez séparer les méthodes qu'Hibernate appelle de l'objet sur lequel il les appelle. La spécification JPA vous permet de déclarer des classes d'écouteur qui seront appelées à certains moments lors du traitement des objets Entity.

Si vous avez beaucoup d'objets Entity similaires, vous pouvez en déplacer certains dans la classe de base et ajouter un écouteur qui contrôlerait leur comportement. Exemple:


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


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

Ensuite, pour la classe BaseEntity, vous pouvez créer une classe d'écoute spéciale :


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

Et vous pouvez connecter la classe User et son écouteur à l'aide de quelques annotations :


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