samedi 27 avril 2013

Hibernate

Hibernate

La description d'un ORM peut sans doute paraître éloignée des statistiques et pourtant, cette technologie est au coeur d'un projet statistique que je suis en train de développer. Au vu de l'aspect critique de la persistance des données dans un projet de capture de donnée à des fins d'analyse statistique, je pense qu'il n'est pas inutile d'exposer dans ce blog le résultat d'essais, le résumé de lectures, ... concernant la technologie Hibernate. L'ouvrage de référence que j'utilise ici, outre les divers billet du blog de mkyong, est Java Persistence with Hibernate de Christian Bauer et Gavin King (maning's publication). La plupart des références au modèle ou encore les extraits de codes proviennent du projet SNT, qui un petit outil de capture de données spécifiques aux tablettes et smartphone, en vue d'analyse statistique avec R.

Object Relational Mapping (ORM)
Les points présentés ici sont un ensemble de problème pouvant apparaître lors du mapping objet/table et des points sur lesquels l'utilisation d'un ORM peut s'avérer très intéressante.

granularité

SQL permet de définir ses propres type de données suivant le RDMS choisi. Par exemple on peut utiliser un String pour stocker une adresse mais on pourrait définir une structure de données
1:  class address {  
2:    String street;  
3:    String zip;  
4:    Long number;  
5:  }  
Une façon brute de mapper cela dans une base de donnée consiste à créer une table pour adresse, ce qui est assez lourd...
Toutefois, ce type de structure (anciennement appelé Record en Pascal, ou struct en C) nécessiteront une définition SQL très différentes d'un SQL à l'autre. L'utilisation d'un ORM est très intéressante pour uniformiser ce genre de problème.

identité et égalité

L'identité de deux objets au sens de Java est qu'ils partagent la même addresse:
1:  String a = "my string";  
2:  String b = a;  
3:  if (a==b) System.out.println("identiques"); else System.out.println("pas identiques");  

a et b sont identiques car ils référencent tous deux un seul et même objet.

1:  String a= "my string";  
2:  String b= "my string";  
3:  if (a.equals(b)) System.out.println("égaux"); else System.out.println("pas égaux");  

Lorsque l'on utilise des equals, il est très important de définir correctement les fonctions hashCode et equals en cas de modification de classe, heureusement, l'environnement eclipse permet de les générer automatiquement. Notez que cette remarque est tout aussi valable lorsque l'on utilise des Set en java (liste ne contenant que des éléments différents, la non équivalence se calcule grâce à la fonction equals des objets contenus dans la liste).

Au niveau base de donnée l'identité se fait via la clé primaire (et pour éviter tout ennui on utilisera plutôt une surrogate key, plutôt qu'une natural key)

Ce qui signifie que lors de l'exécution d'un programme java, on peut avoir des tas de références non identiques sur des objets ayant une même clé primaire et donc identique en DB. Cette gestion peut parfois s'avérer problématique.

associations
Association en programmation orientée objet = référence.
La navigation d'une association en programmation orientée objet peut-être unidirectionnelle ou bidirectionnelle.
Unidirectionnelle : A partir d'un TreeNode on ne peut naviguer que dans ces childs
1:  class TreeNode {  
2:   protected List childList=null;  
3:  ...  
4:  }  
Bidirecionnelle:
1:  class TreeNode {  
2:    protected List childList =null;  
3:    protected TreeNode parent = null;  
4:  ...  
5:  }  
Dans ce cas de figure, on peut remonter.

En base de donnée = contrainte de type foreign key sur une colonne qui assure l'intégrité, cette colonne ne peut exister si la clé primaire associée n'existe pas.

La navigation uni ou bidirectionnelle n'a pas de sens dans les bases de données relationnelles, on peut parcourir le graphe des entités comme on veut.

Relations many-to-many:
Typiquement lorsqu'une entité A peut avoir n entités B associées et que chaque entité B peut avoir m entité A associées. En base de données relationnelle, on passe par une table de lien, dans un modèle objet on ne voit pas cette table.
1:  class A {  
2:   Set bList;  
3:  }  
4:  class B {  
5:   Set aList;  
6:  }  
héritage
une base de donnée ne connaît que l'association  "has a", l'association "is a", propre au concept de programmation orientée objet n'est pas reconnue par les bases de données relationnelles.
Cette problématique quant à elle à déjà été soulevée dans un précédent billet.

n+1 selects problem
lorsque l'on accède des objets en java on le fait de cette façon:
objet1.objet2.objet3 ...
Le problème est que cela va générer un grand nombre de queries dans la base de données et massacrer les performances de l'application.
Là aussi les ORM peuvent apporter pas mal d'optimisations et améliorations de performances.