Spring Data et suivi des modifications

Dec 14, 2017 · 787 words · 4 minutes read spring

Recement j’ai eu a mettre en place l’enregistrerement des actions utilisateurs sur des données afin de persister les utilisateurs responsable des modification ainsi que les indicateur temporel ce celle-ci.

Initialement, je l’avais codé à la main (avec une application JavaEE puis sur un nouveau projet Spring), mais ce code au final était relativement intrusif, et presentait maintenabilité moyenne. Mais en gratant un peu, je me suis aperçu que JPA et hibernate embarquaient une solution à mes problèmes…​ Et qui plus est, facilement configurable au sein d’un projet embarquant Spring data.

Je vais vous montrer comment configurer JPA pour persister les date de création/modification ainsi que l’utilisateur responsable de celle-ci. Nous alons via quelques étapes simple voir la mise en oeuvre de cette solution.

Les annotations Spring data @CreatedBy, @CreatedDate, @LastModifiedBy et @LastModifiedDate

Notre exemple possede une entité pour gérer les todo, et je veux pouvoir tracer qui l’a créée et modifiée.

Et afin de pouvoir utliser la structure pour d’autres entités, nous allons créer une classe abstraite annotée par @MappedSuperclass afin que les attributs JPA soient partagés par tous les héritant et ainsi d’autre entité pourront avoir le même type de suivi.

J’utilise lombok afin de générer les getter, setter (et beaucoup d’autres choses)…​ Mais vous pouvez aussi les écrire à la main (ou via la génération de votre IDE).

Voici les deux classes :

Followed.java
@Data
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class Followed {
  @CreatedBy
  private String creator;

  @CreatedDate
  private Instant created;

  @LastModifiedBy
  private String modificator;

  @LastModifiedDate
  private Instant modified;
}
ToDoItem.java
@Data
@Entity @Table(name = "todo")
public class ToDoItem extends Followed {

  @Id @Column(name = "todo_id_pk")
  @GeneratedValue(strategy = AUTO)
  private Long id;

  @Column(length = 100)
  private String label;
}

J’ai annoté les champs permettant le suivi respectivement avec @CreatedBy, @CreatedDate, @LastModifiedBy et @LastModifiedDate. JPA nous fourni ces annotations et s’appui sur des callback afin de générer automatiquement les valeurs pour les champs ainsi annoté.

Utilisation d’une classe AuditingEntityListener avec @EntityListeners`

JPA fourni une classe AuditingEntityListener avec des méthode de callback (annoté avec @PrePersist et @PreUpdate), elle sera utilisé pour mettre à jour les données quand l’entité sera persistée et/ou mise à jour.

De plus, JPA met à notre disposition une annotation @EntityListeners qui nous permettra de designer la classe qui sera responsable de designer notre “AuditingEntityListener”.

Nous pouvons aussi définir notre propre classe d’affectation de ces données. Cela fera peut être l’objet d’un autre article.

Récupération de l’auteur de la création et de la modification.

JPA peut analyser les champs de "date" et les affecter sans trop de problemes (dans mon exemple, je me base sur la JSR 310 - API Time - cf plus bas). En ce qui concerne l’auteur des modifications, il me faut fournir un nom.

Spring security nous propose de recupérer l’utilisateur courrant. Et je vais écrire une classe implémentant AuditorAware afin de fournir le nom de l’utilisateur connecté (stocké par Spring security).

Pour l’exmple, je fourni le nom en dur, si Spring secrity est activé, la récupération de l’utilisateur connecté est assez trivial (je l’ai indiqué dans le code source du projet sur Github).

FolowedEntitylistener.java
public class FolowedEntitylistener implements AuditorAware<String> {
  @Override
  public String getCurrentAuditor() {
    return "rogue_one";
  }
}

Activation du suivi

Il reste maintenant à activer le suivi via une configuration. Je dois fournir la factory du bean que j’ai créer précédement. De plus je dois indiquer que j’active l’audit des entité via l’annotation @EnableJpaAuditing, en lui fournissant le contrat (interface) a respecter, via le seul parametre de l’annotation.

JpaConfiguration.java
@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorAware")
public class JpaConfiguration {
  @Bean
  public AuditorAware<String> auditorAware() {
    return new FolowedEntitylistener();
  }
}

Maintenant qand une entité sera créée, modifiée, l’auteur et la date seront valorisé sans autres intervention. Sympa et puissant.

Un seul bémol, je n’ai pas encore reussi a utiliser ce concept via une composition (je prefere largement, beaucoup plus POO qu’un simple héritage), mais dès que j’ai un peu de temps pour creuser, je mettrai cet article.

pour jouer un peu

Les sources de ce projet sont disponible sur mon repoository github : ptitbob/spring-data-auditing.

J’utilise HTTPie pour faire mes appel REST :

  • Création : http POST :8080/todo label="un truc"

  • Renvoi de la liste : http :8080/todo`

  • Modification : http PUT :8080/todo/0 (juste une mise à jour du label ;) )

Pour l’exemple, j’utiliser une base en mémoire de type H2, dont la console sera accèssible à cette adresse http://localhost:8080/h2-console/

consoleH2

Grace à la configuration suivante :

application.yml
spring:
  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
    username: sa
    password:
  h2:
    console:
      enabled: true

Et enfin, afin de profiter de l’API time de java 8, j’ai mis ces dépendance au sein de mon pom.xml :

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-java8</artifactId>
</dependency>

Vous pouvez aussi jeter un coup d’oeil ici : Spring et la JSR 310 (Date/Time).

Have fun…​