Utiliser Envers avec Play Framework

Ça fait un petit moment que je “joue” avec Play Framework (ok, elle était facile…) et ce que j’apprécie dans cet outil, c’est sa dualité.

Pour la majorité des besoins, on a tout ce qu’il faut sous la main et le framework est là pour nous faciliter la vie (pas besoin de définir les getters/setters, rechargement à chaud, serveur web intégré,…). On se retrouve alors très productif, à l’image de frameworks pur web comme symfony, RoR,…

Avec une telle productivité, ce qu’on oublierait presque, c’est qu’on est en train de faire du Java. Et si on fait du Java, ça veut dire qu’on a beaucoup beaucoup de librairies disponibles pour faire à peu près tout ce qu’on veut. C’est d’autant plus facile pour tout ce qui touche à l’accès aux données puisque Play repose sur JPA / Hibernate, La brique la plus utilisée dans ce domaine.

Si, par exemple, vous avez développé une petite application avec Play et que vous vous rendez compte que vous avez besoin d’historiser toutes les modifications effectuées sur certaines données. Sans chercher bien loin, vous pourriez facilement vous lancer dans un petit développement “maison” de cette fonctionnalité : ajout d’un champ version, mise en place de copie de données à chaque enregistrement, … Petit développement mais quand même assez fastidieux et qui risque d’apporter son lot de bug et de lignes de code à maintenir.
Mais comme vous êtes sur une plateforme Java et que Play repose sur Hibernate, vous pouvez encore plus simplement utiliser une librairie robuste, testée, maintenue, … dans notre cas : Hibernate Envers.

Pour l’utiliser, c’est très simple : il suffit de copier la jar dans le répertoire lib de l’application Play. Le framework va automatiquement la charger et permettre son utilisation. Ensuite, il suffit d’ajouter la configuration hibernate nécessaire au fichier application.conf :

...
hibernate.ejb.event.post-insert=org.hibernate.ejb.event.EJB3PostInsertEventListener,org.hibernate.envers.event.AuditEventListener
hibernate.ejb.event.post-update=org.hibernate.ejb.event.EJB3PostUpdateEventListener,org.hibernate.envers.event.AuditEventListener
hibernate.ejb.event.post-delete=org.hibernate.ejb.event.EJB3PostDeleteEventListener,org.hibernate.envers.event.AuditEventListener
hibernate.ejb.event.pre-collection-update=org.hibernate.envers.event.AuditEventListener
hibernate.ejb.event.pre-collection-remove=org.hibernate.envers.event.AuditEventListener
hibernate.ejb.event.post-collection-recreate=org.hibernate.envers.event.AuditEventListener
...

La librairie est maintenant chargée. Il ne reste plus qu’à ajouter les annotations qui vont bien sur les entités à auditer :

@Entity
@Audited
public class Profile extends Model {
...
}

Avec cette annotation, Envers va automatiquement garder une trace de toutes les modifications effectuées sur votre entité. Ensuite, il vous suffit par exemple d’implémenter une méthode qui liste toutes les révisions de votre entité :

@Entity
@Audited
public class Profile extends Model {
  ...
  public Map<Number, Profile> getRevisions() {
    Map<Number, Profile> revisions = new HashMap<Number, Profile>();
    // Récupération de l'AuditReader à partir de l'EntityManager
    AuditReader ar = AuditReaderFactory.get(em());
    // Liste des révisions de l'entité courante
    List<Number> revisionNumbers = ar.getRevisions(this.getClass(), id);
    for (Number revisionNumber : revisionNumbers) {
      // Pour chaque révision, on remonte l'entité
      Profile profile = ar.find(this.getClass(), id, revisionNumber);
      revisions.put(revisionNumber, profile);
    }
    return revisions;
  }

  public static Profile findByIdAndRevision(Long id, Number revision) {
    AuditReader ar = AuditReaderFactory.get(em());
    Profile profile = ar.find(Profile.class, id, revision);
    return profile;
  }
  ...
}

Maintenant, on peut très facilement dans notre contrôleur lister les révisions d’une entité et accéder à une de ces versions. Par exemple :

public class Profiles extends Controller {
  ...
  public static void show(Long id) {
    Profile profile = Profile.findById(id);
    Map<Number, Profile> revisions = profile.getRevisions();
    render(profile, revisions);
  }

  public static void showRevision(Long id, Number revision) {
    Profile profile = Profile.findByIdAndRevision(id, revision);
    render(profile);
  }
  ...
}

Et voilà ! Il ne reste plus qu’à traiter la vue et les éventuelles méthodes métiers particulières. Facile, non ?

Développement web , ,

7 comments


  1. Pingback: Les tweets qui mentionnent Utiliser Envers avec Play Framework | Web, Bass & Rock'n'Roll -- Topsy.com

  2. Bon article,
    tu présentes bien les points forts de play et si j’etais pas déjà ultra fan je pense que ça m’aurait donné envie d’essayer 😉

  3. opensas

    Wow!!!

    you could make a module to provide that functionality…

    it’s really amazing…

  4. Pingback: philwilson.org » Blog Archive » Logging database content changes in a Play Framework app

  5. Pingback: philwilson.org | Logging database content changes in a Play Framework app

  6. Merci d’avoir posté cette article! Je suis votre site depuis longtemps mais je n’avais jamais
    songé à laisser un commentaire.Je me suis inscrit à votre blog et j’ai partager votre
    article sur mon compte Facebook. Merci encore pour ce post!

    Il est très bien rédigé et l’information est complète :
    -)

Leave a Reply to Hebergement Solutions Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>