Document ajouté sur le CVS pour la nouvelle manière d'implanter la
persistence.
Voici le contenu :
======================================
Implantation d'un storage sur JDBC - 2
======================================
:Authors: Benjamin POUSSIN <poussin(a)codelutin.com> & Arnaud THIMEL
<thimel(a)codelutin.com>
:Version: $Revision: 1.1 $
:Date: $Date: 2005/08/19 16:02:28 $
Le problème avec la première implantation
-----------------------------------------
Le première version de la persistence JDBC a été réalisée. Elle a mis en
avant
quelques défauts :
- Difficulté pour certaines bases (Postgre / Derby) à convertir les
valeurs de
la colonne "value" de BLOB à un format numérique (DOUBLE PRECISION
principalement). Ces bases nécessitent un CAST explicite (où du moins la
création supplémentaire d'un CAST implicite) souvent difficilement
réalisable.
- La compléxité des requêtes envoyées à la base pour faire les find.
Certaines
bases s'emmelent les pinceaux avec les requêtes (MySQL en tête !).
Exemple de requête générée pour récupérer les Companies visibles par la
transaction '1123170253913' dont le nom est 'Code Lutin' :
|SELECT DISTINCT id FROM data
|WHERE (field='nom' AND value='Code Lutin')
|AND (id,longdate) IN (
| SELECT id, min(longdate) FROM management
| WHERE (id,longdate) IN (
| SELECT id, max(longdate) FROM management
| WHERE class='org.codelutin.test2.entities.Company'
| AND (longdate in (-1123170253913) OR (longdate>0 AND
longdate<=1123170253913))
| AND id NOT IN (
| SELECT id FROM management
| WHERE isDeleted=true
| AND (longdate in (-1123170253913) OR (longdate>0 AND
longdate<=1123170253913))
| )
| GROUP BY id
| UNION
| SELECT id, longdate FROM management
| WHERE class='org.codelutin.test2.entities.Company'
| AND isDeleted=false
| AND longdate in (-1123170253913)
| )
| GROUP BY id
|)
La solution
-----------
On se propose donc de changer la manière dont les informations sont stockées
et de se rapprocher d'une implantation plus conventionnelle : On génère
pour
chaque entité un schéma de table qui lui est propre.
Etant donné que les nom d'entité (et encore plus des classes d'associations
générées) peut être très long, on crééra un nom de table pour chaque table.
On aura donc autant de tables que d'entités, plus une table management,
dont le
rôle sera de faire l'association entre le nom des entités et le nom de
la table
associée pour une version de schéma donnée.
Dans le première version, la table management recenssait toutes les
entités à
un instant précis avec leut état à cet instant. (classe, new, deleted,
schemaVersion). On souhaite toutefois conserver ces informations. Elle
seront
donc toutes rajoutées dans les tables générées à l'exception de la
classe et du
schemaVersion (car dans management). Pour qu'il n'y ait pas d'ambiguïté avec
les champs propres à l'entité, on préfixera ces champs du caratère '_'.
Le nom des tables sera le nom de l'entité (sans le package) suivi du
schemaVersion. Pour les classes d'associations générées (non comprises
dans le
modèle au départ), le nom étant trop grand, on ne prendra que la fin des
noms
des entités (avec le nom des attributs) suivies du schemaVersion. (cf.
exemple
plus bas)
Le passage à une nouvelle version pour une entité sera effectué de la
manière
suivante :
1. On charge en mémoire toute la table actuelle ;
2. On supprime la table actuelle ;
3. On créé la nouvelle table ;
4. On convertit tous les objets remontés ;
5. On sauve tous les objets convertis dans la nouvelle table.
Pour une relation entre Company et Employee (dont la classe d'association ne
fait pas initialement partie du modèle), on obtient ainsi quatre tables :
Company123456 :
- _id : String (topiaId)
- _longdate : long
- nom : String
- adresse : String
- _isNew : boolean
- _isDeleted : boolean
Employee789012 :
- _id : String (topiaId)
- _longdate : long
- nom : String
- age : int
- sexe : char
- _isNew : boolean
- _isDeleted : boolean
Employee_company__Company_employee827923283 :
- _id : String (topiaId)
- _longdate : long
- company : String (topiaId)
- employee : String (topiaId)
- _isNew : boolean
- _isDeleted : boolean
management
- class : String (fullyQualifiedName)
- table : String
- schemaVersion : long
Le contenu de la table management sera :
+-------------------------------------+---------------------------------------------+----------------+
| class |
table | schemaVersion |
+=====================================+=============================================+================+
| org.codelutin.test2.Company |
Company123456 | 123456 |
+-------------------------------------+---------------------------------------------+----------------+
| org.codelutin.test2.Employee |
Employee789012 | 789012 |
+-------------------------------------+---------------------------------------------+----------------+
| org.codelutin.[...]Company_employee |
Employee_company__Company_employee827923283 | 827923283 |
+-------------------------------------+---------------------------------------------+----------------+
Conclusion
----------
Une telle persistence ne devrait à priori pas pauser de problème avec la
grande
majorité des bases de données. Elle simplifiera la lisibilité brute de
la base,
permettra la génération de reqêtes plus simples, et une maniulation des
données
plus aisée.
Cependant, en se rapprochant d'un schéma de stockage de données plus
classique,
on s'expose aux mêmes problèmes que les solutions précedemment écartées
telles
que JDO.