Java - Freelance . fr » clé valeur https://java-freelance.fr Du java et du freelance Wed, 26 Jun 2013 11:46:03 +0000 fr-FR hourly 1 http://wordpress.org/?v=3.5 Persistance objet avec Redis et Jackson https://java-freelance.fr/java/persistance-objet-avec-redis-et-jackson https://java-freelance.fr/java/persistance-objet-avec-redis-et-jackson#comments Wed, 13 Jun 2012 22:59:31 +0000 Jean-Baptiste https://java-freelance.fr/?p=1425 Redis est la base de données NoSql que je préfère. Le concept est ultra-simple et j’aime la simplicité. Il faut voir Redis comme une grosse Map<K,V>. Avec la possibilité de faire des requêtes sur les clés.

Du coup la documentation de Redis est simple. Un autre truc que j’aime c’est qu’elle spécifie la complexité de chaque opération, ce qui permet au développeur de vérifier à chaque fois que la commande qu’il s’apprête à utiliser n’est pas trop gourmande. De plus, il n’a pas le risque d’oublier de mettre un index sur un champ (combien de fois cela arrive en SQL ou avec MongoDb ! ) car toutes les clés sont par définition « indexées » dans une (Hash) Map.

Simple et (donc ?) performant. Redis est sans doute ce qu’il y a de plus performant en terme de base de données. Nous avons donc intérêt à nous y intéresser avant de chercher des solutions plus complètes et donc plus complexes et moins performantes.

Sauf que, Redis ne stocke que des chaînes de caractères. Comment faire pour stocker nos objets métiers complexes ?


On pourrait simplement utiliser une base de données SQL pour stocker notre modèle et utiliser un ORM pour que ce soit « simple ». Ensuite dans Redis on ne stockerai que la dé-normalisation de certaines requêtes : 3 meilleurs clients => { 1 => id:130 ; 2 => id:345 ; 3 => id:456 } Ainsi la requête pour récupérer les identifiants des trois meilleurs clients se fera en temps constant O(1) puis la requête pour récupérer les données des trois clients dans la base SQL se fera également en temps constant,  car les clés primaires sont indexées dans les bases SQL.

Mais il faut avouer que c’est embêtant de devoir gérer deux bases de données, surtout quand on pourrait simplement sérialiser les objets directement dans Redis.

Stocker les objets Java dans Redis grâce à Jackson

Dans notre exemple, le besoin est de pouvoir insérer des liens dans des listes et de pouvoir récupérer en temps constant tous les liens d’une liste donnée.
Jackson est un sérialiseur Objet -> JSON. Ce qui est parfait pour pouvoir lire facilement les objets sérialisé où pour pouvoir les utiliser directement en JavaScript, sans passer par une dé-sérialisation.

On va utiliser JAX-B pour annoter nos objets Java, par exemple :

@XmlRootElement(name = "links")
@XmlAccessorType(FIELD)
public class BannerLink {
   private String label;
   private String url;

   public BannerLink(String label, String url) {
      this.label = label;
      this.url = url;
   }

   public String getLabel() {
      return label;
   }

   public String getUrl() {
      return url;
   }

   protected BannerLink(){}
}

Notez que Jackson sait aussi sérialiser des POJOs (sans JAX-B mais avec Setter)

Ensuite il suffit de sérialiser l’objet avec Jackson pour l’insérer dans une liste Redis.

      AnnotationIntrospector introspector = new JaxbAnnotationIntrospector();
      mapper.getDeserializationConfig().setAnnotationIntrospector(introspector);
      mapper.getSerializationConfig().setAnnotationIntrospector(introspector);
      jedis.rpush("listKey", mapper.writeValueAsString(link));

Pour la lecture, il suffit de dé-sérialiser les objets de la liste.

   mapStringsToLinks(jedis.lrange(key, 0, jedis.llen(key)));

   private List mapStringsToLinks(List jedisResult) {
      return Lists.transform(jedisResult, toBannerLink());
   }

   private Function<String, BannerLink> toBannerLink() {
      return new Function<String, BannerLink>() {

         @Override
         public BannerLink apply(@Nullable String link) {
            return mapper.readValue(link, BannerLink.class);
         }
      };
   }

Je ne sais pas vous, mais moi je trouve ça vraiment plus simple de persister directement et simplement les instances d’objets, telle quelle, sans se prendre la tête avec du mapping, jointure ou autre joyeuseté.

Performances

Je n’ai pas fait le test avec une base SQL et un ORM de type Hibernate mais n’hésitez pas à forker le code et à le faire, ça m’intéresse.

Aucun tuning n’a été fait sur les bases de données. Mongo est bien meilleur en écriture et peut être encore meilleur, car il ne garantie pas que les données sont effectivement écrites sur le disque, j’avoue ne pas avoir cherché à optimiser Redis pour l’écriture, j’ai gardé la configuration par défaut.

En revanche, Redis est bien meilleur en lecture, malgré le surplus de traitements dû à la désérialisation Jackson. Et c’est ce qu’on cherche dans notre cas d’usage, nos listes de liens vont être lus beaucoup plus souvent que modifiées. Et pour ceux qui se poseraient la question, oui j’ai bien créé les indexes dans Mongo.

Ce qu’il faut retenir, c’est que dans bien des cas, une base de données document ou SQL n’est pas forcément obligatoire et qu’il vaut mieux démarrer simple et (très) efficace, quitte à changer par la suite…

Un peu de lecture si vous souhaitez en savoir plus sur Redis : Redis: the Definitive Guide: Data Modeling, Caching, and Messaging

]]>
https://java-freelance.fr/java/persistance-objet-avec-redis-et-jackson/feed 9