ToPIA Migration Service est un module ToPIA chargé d'effectuer la migration d'une base de données existante sans perte de données.
Ce service doit disposer de quelques proprietés de configuration pour effectuer la migration d'une base de données.
Ces propriétés sont fournies au service via un TopiaContext et font donc partie de la configuration de l'application.
hibernate.dialect=org.hibernate.dialect.H2Dialect hibernate.connection.username=sa hibernate.connection.password= hibernate.connection.driver_class=org.h2.Driver topia.persistence.directories=directory1,directory2 topia.persistence.classes=classImpl1,classImpl2
Ces informations servent à créer une configuration hibernate (qui contient les informations de connexion et les mappings de l'application).
Les lignes commencant par "hibernate" sont spécifiques à hibernate et au type de base de données utilisé. Les lignes suivantes sont spécifiques à ToPIA mais contiennent les mappings indispensable pour créer le schéma de la base de données après migration.
La configuration doit contenir ces propriétés :
topia.service.migration.mappingsdir=oldmappings topia.service.migration.modelnames=model1,model2,model3
qui spécifie le répertoire de recherche des anciens mappings pour les différents modèles.
Ce dossier contient ensuite un sous-dossier par modèle comportant chacun un sous-dossier par version par version (nommé X, X étant la version), avec pour chaque dossier, l'ensemble des mappings hibernate de cette version.
Exemple :
oldmappings/
model1/
1/
Class1.hbm.xml
Class2.hbm.xml
Class3.hbm.xml
2/
Class1.hbm.xml
Class2New.hbm.xml
Class3.hbm.xml
2.2/
Class2.hbm.xml
Class3.hbm.xml
Class4New.hbm.xmlLa configuration doit contenir une propriété :
topia.service.migration.version=3.5.1 (exemple)
Cette propriété renseigne la version courante de l'application. Le modèle de classes doit contenir un tag "version" indiquant la version courante du modèle.
Lors de la migration ces deux versions seront comparées pour déterminer les mappings à utiliser.
Il est possible de définir une classe de type MigrationCallbackHandler, pour interagir, par exemple, avec l'utilisateur et lui demander s'il faut migrer la base de données.
Ces callback doivent implémenter MigrationCallbackHandler et se trouver dans la configuration:
topia.service.migration.callbackhandlers=org.nuiton.test.MyCallbackHandler
Ce module étant un service ToPIA, il doit être activé pour pouvoir s'exécuter.
Il commence par se connecter au SGBD, vérifie si les versions diffèrent, et effectue la migration si besoin.
Dans le cas où la version ne peut pas être deterninée, il considère que le schema en base est en version V0 (les mppings de cette version doivent être fournit). Dans ce cas, il effectue en plus une détection des tables pour savoir si le schéma existe deja. S'il n'existe pas, il ne tente donc pas d'effetuer une migration.
Pour savoir comment migrer les données, le développeur utilisant le module de migration doit produire des classes Java de migration (une par classe nécéssitant une modification et par version).
Ces classes doivent :
hériter de la classe AbstractMigration ou de l'interface Migration
se trouver dans un sous package des classes d'implémentation référencées par les mappings. Ce package doit se nommer VnVm où:
n est la version de départ de migration
m la version d'arrivée
respecter une convention de nommage de la forme MigrationClass où:
Class est le nom de l'entité devant être migrée
Exemple, pour migrer une Personne d'une version 2 à 2.1 le nom de la classe sera : V2V2_1.MigratePersonne.java
Note: les "." étant interdits dans le nom de fichier, ils sont remplacés par le caractère "_".
Ensuite, chaque classe doit :
implémenter au moins la méthode migrate(MapAdapter, MapHelper) cette méthode prend une MapAdapter en paramètre, pour modifier un tuple et un MapHelper pouvant servir à retrouver des informations sur le reste des tuples de la base
surcharger (si besoin) la méthode public ProxyClass migrateFrom() dans le cas où les tuples à modifier proviennent d'une classe différente de la classe courante.
Exemple :
Ici, la modification porte sur la transformation de l'attribut timestampNaissance de la classe domaine.Personne en une nouvelle table domaine.Naissance, effectée dans le changement de la version 1 à la version 2 :
// migrateFrom()
public ProxyClass migrateFrom() {
return new SimpleProxyClass("domaine.Personne");
}
// migrate(MapAdapter, MapHelper)
public void migrate(MapAdapter map, MapHelper helper) {
// map d'entree vide, conversion du timestamp
// en jour, mois, annee
try {
Long timestamp = (Long) map.getOldValue("timestampNaissance");
Calendar c = Calendar.getInstance();
c.setTimeInMillis(timestamp);
map.setValue("jour", c.get(Calendar.DAY_OF_MONTH));
map.setValue("mois", c.get(Calendar.MONTH));
map.setValue("annee", c.get(Calendar.YEAR));
} catch (ExceptionAttributeUndefined e) {
e.printStackTrace();
}
}Donc cette classe :
doit se nommer MigrateNaissance.java et se trouver dans le package domaine.V1V2
redéfinit la methode migrateFrom() pour indiquer que les donnees proviennent de domaine.Personne
implémente migrate() pour produire des tuples de type domaine.Naissance à partir de tuples domaine.Personne
Dans le cas où l'application est ammenée à créer un schéma de base de données, elle doit en informer le module de migration pour que celui-ci renseigne la version du schéma créé.
Le module s'enregistre automatiquement aupres de ToPIA pour savoir quand celui-ci a créé un nouveau schéma. Il renseigne donc automatiquement la version par la suite.