NoSQL – [Blog] du Java et du Freelance https://java-freelance.fr Vis ma vie de freelance java : techno et entreprise Mon, 12 Oct 2015 14:45:01 +0000 fr-FR hourly 1 https://wordpress.org/?v=4.4.4 Premiers pas avec OGM + Ehcache https://java-freelance.fr/java/premiers-pas-avec-ogm-ehcache https://java-freelance.fr/java/premiers-pas-avec-ogm-ehcache#respond Fri, 05 Oct 2012 12:16:05 +0000 https://java-freelance.fr/?p=1517 Hibernate OGM se veut une surcouche sur les datastore noSQL, un moyen de persister et d’accéder aux données dans des bases noSQL, quel que soit leurs type. C’est le hibernate des bases de noSQL. L’un des buts, à terme, est de supporter les APIs native d’hibernate. Notamment :

  • les annotations de mapping
  • les apis
  • la sémantique (cascade …)
  • et le  JP-QL

Plus d’infos sur le blog d’Emmanuel Bernard

Alors qu’au début, il n’y avait que Infinispan, on peut désormais y trouver MongoDB et Ehcache. D’autres types viendront surement. Ici, Ehcache est utilisé non pas comme un cache mais comme un datastore noSQL clé/valeur.

Configuration

Pour effectuer des insertions dans Ehcache via OGM, c’est vraiment simple. Après avoir ajouter la dépendance hibernate-ogm-ehcache à votre POM, il faut ensuite faut un fichier persistence.xml.

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">

  <persistence-unit name="ogm-ehcache" transaction-type="JTA">
    <provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider>
    <properties>
      <property name="hibernate.ogm.datastore.provider"
      value="ehcache"/>
      <property name="hibernate.transaction.jta.platform"
      value="org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform"/>
    </properties>
  </persistence-unit>
</persistence>

Note : Actuellement, les transactions, qui sont gérées de manière native par Ehcache, ne fonctionnent pas avec OGM.

Il faut ensuite instancier l’entityManager.

   Configuration cfg = new OgmConfiguration().
        setProperty("hibernate.ogm.datastore.provider", "ehcache").
        addAnnotatedClass(Appli.class).addAnnotatedClass(Platform.class);

    TransactionManager tm = getTransactionManager();
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("ogm-ehcache");

    try {
      EntityManager em = emf.createEntityManager();

Many-to-One

Le modèle est le suivant : 2 classes, Appli et Perform.

 
@Entity @Indexed
public class Appli {
  @Id
  @GeneratedValue(strategy = GenerationType.TABLE, generator = "appli")
  @TableGenerator(
      name = "appli",
      table = "sequences",
      pkColumnName = "key",
      pkColumnValue = "apply",
      valueColumnName = "seed"
  )
  public Long getId() { return id; }
  public void setId(Long id) { this.id = id; }
  private Long id;

  public String getName() { return name; }
  public void setName(String name) { this.name = name; }
  private String name;

  @ManyToOne
  @IndexedEmbedded
  public Platform getPlatform() { return platform; }
  public void setPlatform(Platform platform) { this.platform = platform; }
  private Platform platform;
}
 
@Entity  @Indexed
public class Platform {

  @Id
  @GeneratedValue(generator = "uuid")
  @GenericGenerator(name="uuid", strategy="uuid2")
  public String getId() { return id; }
  public void setId(String id) { this.id = id; }
  private String id;

  @Field
  public String getName() { return name; }
  public void setName(String name) { this.name = name; }
  private String name;
}

On crée ensuite un objet learnAnimals de type Appli qui a pour plateforme itunes.

 Platform itunes = new Platform();
 itunes.setName("iTunes");
 Appli learnAnimals = = new Appli();
 learnAnimals.setName("J apprends les animaux" + i);
 learnAnimals.setPlatform(itunes);

On persiste de la meme manière qu’avec hibernate.

 tm.begin();
 em.persist(learnAnimals);
 Long leanAnimalsId = learnAnimals.getId();
 em.flush();
 em.close();
 tm.commit();

Et voilà, l’objet learnAnimals est persisté. Mais à la différence d’une persistance classique avec Ehcache, qui fonctionne par sérialisation/déserialisation des objets, le modèle est déshydraté.

tl;dr

Pour une entité, elle est donc stockée dans une store ENTITIES avec le modèle clé/valeurs suivant :
Clé
Chaque clé est de type EntityKey, classe qui contient entre autre :

private final String table;
private String[] columnNames;
private Object[] columnValues;

On aura par exemple : {table=’Appli’,columnNames=[‘id’],columValues=[‘1’]} pour l’appli qui a pour identifiant la valeur 1.

Pour la valeur, elle est construite sous la forme d’une map.
Ainsi pour un object Appli qui contient une Plateforme, la map aura cette forme :

Clé           |  Valeur
id            | 1
name          | monAppli
platform_id   | 65d2183a-3a73-4079-83fb-57f9072e0915

A l’insertion c’est un peu plus compliqué. On passera par des objets transitoires, des aggregats nommé TupleOperation qui contiennent le nom de la colonne, sa valeur et son type TupleOperationType, celui-ci pouvant prendre 3 valeurs, PUT, PUT_NULL et REMOVE.
On a donc en fait :

Key         | Value
id          | {columnName='id',columnValue='1'.columnType=TupleOperationType.PUT}
name        | {columnName='name',columnValue='monAppli'.columnType=TupleOperationType.PUT}
platform_id | {columnName='platform_id',columnValue='2886a75c-11ae-4f3d-a132-8d58010382b3'.columnType=TupleOperationType.PUT}

La valeur PUT indiquera qu’il faut faire un

  map.put( action.getColumn(), action.getValue() );

C’est à dire pour le premier exemple :

  map.put('id',1)

Un remove aurait entrainé une suppression de la paire clé/valeur. Un PUT_NULL fait la même chose qu’un PUT.

On a donc bien inséré en base  :

Clé           |  Valeur
id            | 1
name          | monAppli
platform_id   | 65d2183a-3a73-4079-83fb-57f9072e0915

On aura une entrée similaire dans le meme cache ENTITIES pour les objets de types Platform. Pour résumer, on a donc :

Map<EntityKey,Map<String,Object>>

La deuxième store est la store ASSOCIATION. Elle n’est utile que dans les relations plus complexes.On y reviendra par la suite. Dans ce cas, elle reste vide.

La troisième store est la store des IDENTIFIERS, qui stocke les informations relatives aux séquences, notamment toutes celles qui permettent de gérer de manière automatique les identifiants.

Many-to-Many

On définit maintenant une application comme pouvant être associée à N plateforme. Le modèle de Platform ne change pas, celui d’Appli légerement.

 
@Entity @Indexed
public class AppliManyToMany {
  @Id
  @GeneratedValue(strategy = GenerationType.TABLE, generator = "appli")
  @TableGenerator(
      name = "appli",
      table = "sequences",
      pkColumnName = "key",
      pkColumnValue = "apply",
      valueColumnName = "seed"
  )
  public Long getId() { return id; }
  public void setId(Long id) { this.id = id; }
  private Long id;

  public String getName() { return name; }
  public void setName(String name) { this.name = name; }
  private String name;

  @ManyToMany
  @IndexedEmbedded
  public List getPlatforms() { return platforms; }
  public void setPlatforms(List platforms) { this.platforms = platforms; }
  private List platforms=null;

}

En ce qui concerne la table ENTITY, elle est un peu modifié.
La clé reste identique, par contre l’enregistrement ayant pour clé platform_id n’existe plus. On a uniquement :

Clé           |  Valeur
id            | 1
name          | monAppli

L’association est désormais portée par un enregistrement dans la table ASSOCIATION.
La clé, de type AssociationKey, similaire à une EntityKey, représentant l’id 5 :
{table=’AppliManyToMany_Platform’, columnNames=[AppliManyToMany_id], columnValues=[5]}

La valeur est toujours une Map :
Clé (de type RowKey) : {table=’AppliManyToMany_Platform’, columnNames=[AppliManyToMany_id, platforms_id], columnValues=[5, 6301e8be-307f-4884-b3a1-5ec0dad7c3e5]}
Valeur (sous la forme d’une Map): {AppliManyToMany_id=5, platforms_id=2886a75c-11ae-4f3d-a132-8d58010382b3}}
On a donc une structure de la forme :

  Map<AssociationKey,Map<RowKey,Map<String,Object>>>

Pour cet exemple, il n’y a qu’une seule plateforme, celle qui a l’id 6301e8be-307f-4884-b3a1-5ec0dad7c3e5, ainsi, on a donc un unique enregistrement dans la map.

Si on prends un exemple où une application est associée à deux personnes (appli avec id= 6):
On pourrait avoir comme clé principale :
table=’AppliManyToMany_Platform’, columnNames=’AppliManyToMany_id’, columnValues=’6′

Puis comme value les couples clés/valeurs suivants :
RowKey 1 :
Clé (RowKey) : {table=’AppliManyToMany_Platform’, columnNames=[AppliManyToMany_id, platforms_id], columnValues=[6, b705d241-b6dd-4a81-9fb2-2f9f732530d7]}
Valeur associée (sous forme de map): {AppliManyToMany_id=6, platforms_id=b705d241-b6dd-4a81-9fb2-2f9f732530d7}
RowKey 2 :
Clé (RowKey) : {table=’AppliManyToMany_Platform’, columnNames=[AppliManyToMany_id, platforms_id], columnValues=[6, 2886a75c-11ae-4f3d-a132-8d58010382b3]}
Valeur associée (sous forme de map): {AppliManyToMany_id=6, platforms_id=2886a75c-11ae-4f3d-a132-8d58010382b3}

On a donc bien pour l’application 6 deux plateformes, la plateforme b705d241-b6dd-4a81-9fb2-2f9f732530d7 et la 2886a75c-11ae-4f3d-a132-8d58010382b3 . Le framework se servira des identifiants dans un deuxième temps pour retrouver dans la table ENTITIES les plateformes correspondantes et les ‘ajoutera’ dans la liste des plateformes de l’application id=6. Pour avoir une idée encore plus concrète, il y a des schémas dans la documentation d’OGM.

A venir : Hibernate Search, Lucene et autres recherches avec Hibernate OGM.

]]>
https://java-freelance.fr/java/premiers-pas-avec-ogm-ehcache/feed 0
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 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
Devoxx Day 1 https://java-freelance.fr/java/devoxx-2009-day1 https://java-freelance.fr/java/devoxx-2009-day1#respond Mon, 16 Nov 2009 23:44:41 +0000 https://java-freelance.fr/?p=209 logodevoxx

Qu’est ce que c’est ?

C’est 5 jours de conférences, principalement orientés java à Anvers en Belgique.  La réception des devoxians à lieu au Metropolis, une salle de cinéma dans la banlieue d’Anvers, à environ 20 min de la gare centrale en tramway. Après avoir récupéré nos badges, on nous fournit un sac de goodies (sac/stylo/bloc note/Tee shirt).

Kanban

La première conférence à laquelle j’ai assisté sur Kanban a duré 3 heures. Kanban est une méthode agile avec un fonctionnement plus simple que Scrum, dans le sens où elle est beaucoup moins contraignante. C’est une méthode avec une forte composante visuel où un radiateur d’information constitue un point central de la méthode. Ses trois principes sont la confiance, l’esprit d’équipe et la transparence. Il n’y a pas de notions de sprint ou d’itération . Les demandes arrivent en permanence et sont traitées au fur et à mesure.

JSF

La deuxième conférence sur JSF 2 était assez pointue, organisée en 3 parties : une heure sur la vue, une heure sur le controleur, une heure sur le modèle. L’aspect orientation composant au lieu de contenu comme c’est le cas avec JSP est assez interessant. L’inconvénient de JSF 1.2 était la quantité très importantes d’XML, JSF 2.0 passe aux annotations, ce qui la rend beaucoup plus simple à manipuler. D’autres améliorations : pouvoir passer des paramètres en GET (sans commentaire), gérer des bookmarkables URLs …
Un des points qui a été souvent abordé lors de la présentation est le futur de JSF. Par exemple, le fait d’avoir une API standard pour AJAX mais également les points bloquants (certaines lourdeurs de configurations). J’ai beaucoup aimé la franchise de Peter, qui a bien montré ce que l’on peut faire et ce qui peut être améliorer.

Hadès & NoSQL

Place ensuite à 2 petites présentations qui m’ont laissé sur ma faim. En une quarantaine de minutes, difficile de rentrer dans un sujet technique, tout au moins d’en montrer ses limites.
Une présentation scolaire mais malgrès tout intéressante d’Hades, qui permet de simplifier l’utilisation de JPa n’a cependant pas montré les cas limites d’utilisation.
De même, la présentation NoSQL n’est pas rentrée assez dans les détails pour que cela deviennent vraiment interessant.

Et le meilleur pour la fin

De 19h à 22h, place à 3 BoFs : Une session très intéressante sur le NoSQL qui a abordé beaucoup de points, comme l’efficacité, les différents types d’outils, l’importance de choisir un système qui correspond à nos attentes. Pour choisir un bon type de base de données, il est conseillé de faire ressortir 2 priorités entre : Consistency, Partition Tolerance et Availability. Ainsi, Cassandra  par exemple,  dans le cas de serveurs distribués, va remonter la dernière information mais sans que cela ne soit garantie (choix fait sur Partition Tolerance et Availability ). L’utilisation pour les boutiques en ligne est tout indiquée (c’est le système d’Amazon). Alors que d’autres systèmes tel que HBase garantissent eux une consistance des données, ils seront plutôt utilisés dans des environnements type CMS, où ce qui est visible à l’écran doit correspondre aux dernières données.

Sur JSF2, la BoF est venu compléter la présentation de l’après midi.

La BoF d’hibernate a été assez intéressante. Une présentation assez longue de l’API Criteria (qui n’est pas très sexy au premier abord) ne m’a pas vraiment fait changé d’avis mais néanmoins, le fait que le meta-modèle soit généré automatiquement par l’IDE simplement est quand même important. Le principal intêret de cet specification est le fait que cela soit typé, néanmoins, peut être parceque je ne suis plus habituée à utiliser un ORM (JDBC pur), je trouve cela très (trop) verbeux. Cet aspect verbeux provient du fait que l’API critéria est très puissante et supporte énormément de choses. On peut l’imaginer encapsulée dans un framework plus simple pour ne retenir que les choses ‘utiles’ à son projet dans une forme verbeuse plus simple.

Et voilà le bilan de cette première journée. D’autre articles arriveront sous peu !

]]>
https://java-freelance.fr/java/devoxx-2009-day1/feed 0