Author: echatellier Date: 2011-05-06 11:26:31 +0200 (Fri, 06 May 2011) New Revision: 2273 Url: http://nuiton.org/repositories/revision/topia/2273 Log: Update topia migration doc Modified: trunk/topia-persistence/src/site/rst/devel/SchemaMigration.rst Modified: trunk/topia-persistence/src/site/rst/devel/SchemaMigration.rst =================================================================== --- trunk/topia-persistence/src/site/rst/devel/SchemaMigration.rst 2011-05-04 14:39:50 UTC (rev 2272) +++ trunk/topia-persistence/src/site/rst/devel/SchemaMigration.rst 2011-05-06 09:26:31 UTC (rev 2273) @@ -5,7 +5,7 @@ .. * $Id$ .. * $HeadURL$ .. * %% -.. * Copyright (C) 2004 - 2010 CodeLutin +.. * Copyright (C) 2004 - 2011 CodeLutin, Chatellier Eric .. * %% .. * This program is free software: you can redistribute it and/or modify .. * it under the terms of the GNU Lesser General Public License as @@ -22,113 +22,152 @@ .. * <http://www.gnu.org/licenses/lgpl-3.0.html>. .. * #L% .. - -===================================== -Comment migrer d'un schéma à un autre -===================================== +Comment migrer les version d'un schema de base de données +========================================================= +Lorsque des modifications sont effectuer dans une application qui impacte +le schema de base de données, celui doit subir des migrations. +ToPIA fournit une API visant a assister le developpeur pour migrer un schema +de base de données. -Une application modifie et ajoute des objets. La persistence doit donc être -adaptée à ces changements. Le problème est la migration des données -existantes dans le nouveau schéma. +Approche +-------- -Voici quelques idées d'implantations. +TODO approche migration par copy +TOOD approche migration par requette sql -Hibernate et dynamic-map -======================== +Configuration +------------- -Hibernate permet de mapper les objets de la base de données dans des -dynamic-map. Nous n'avons donc pas besoin de pojo pour les représenter, il -nous suffit d'avoir le mapping hibernate. +Pour commencer, topia doit connaitre l'ensemble des versions de la base +de données, et la version actuelle de l'application. Si la version de l'application +diffère de celle de la base de données, une migration sera effectuée. +Après la migration, la version de la base de données est égale à celle de +l'application. -L'idée est donc, lors de la modification de schéma, de regrouper tous les -anciens mapping dans un seul fichier de mapping qui contiendra le numero de -version du schema. +Dépendances +~~~~~~~~~~~ -On utilisera ces mappings pour charger l'ancienne configuration et ainsi les -réenregistrer dans le nouveau schéma. +La migration est effectuée par le service de migration de ToPIA. Il est +necessaire de l'ajouter explicitement. -Une classe java de migration devra être écrite pour transformer les données -d'un schéma vers un autre schéma. +:: -Avantage --------- + <dependency> + <groupId>org.nuiton.topia</groupId> + <artifactId>topia-service-migration</artifactId> + <version>2.5.3</version> + <scope>compile</scope> + </dependency> -Le changement de schéma est fait en java +Class de definition des migration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Désavantage ------------ +Pour commencer, il faut créer la classe definissant les versions. -Il faut dans la base une table qui indique dans quelle version de schéma -actuelle est la base (Topia->schemaVersion). -Une autre solution est de faire une base -par version, le numero de version étant dans le nom de la base (inconveniant : -le nom de la base change a chaque version). -Une autre solution possible est de faire un -changement de schéma dans une transaction, donc on peut lire les anciennes -données dans une transaction et on écrit les nouvelles dans une autre -transaction sur la même table mais qui aurait des schéma différents. +:: -Il faut, dans les noms des tables, le numéro de version du schéma pour que les -les anciennes tables ne servent pas comme nom pour les nouvelles. + public class DatabaseMigrationClass extends TopiaMigrationCallbackByClass { + + protected static final Version VERSION_1 = new Version("1"); + protected static final Version VERSION_2 = new Version("2"); + protected static final Version VERSION_3 = new Version("3"); + + public DatabaseMigrationClass() { + super(new MigrationResolver()); + } -Il faut soit faire l'aggrégation des mapping dans un seul fichier, soit -n'utiliser qu'un seul fichier pour les mappings, soit avoir un fichier par -classe et par version, mais il est moins simple de retrouver tous les -fichiers de mapping et surtout moins simple + protected static class MigrationResolver implements MigrationCallBackForVersionResolver { + + @Override + public Class<? extends MigrationCallBackForVersion> getCallBack(Version version) { + Class<? extends MigrationCallBackForVersion> result = null; + + if (version.equals(VERSION_1)) { + result = MigrationV0V1.class; + } + else if (version.equals(VERSION_2)) { + result = MigrationV1V2.class; + } + else if (version.equals(VERSION_3)) { + result = MigrationV2V3.class; + } + return result; + } + + } + + @Override + public Version[] getAvailableVersions() { + Version[] result = new Version[] { VERSION_1, VERSION_2, VERSION_3 }; + return result; + } + + @Override + public Version getApplicationVersion() { + Version appVersion = new Version(MyAppDAOHelper.getModelVersion()); + return appVersion; + } + + @Override + public boolean askUser(Version dbVersion, List<Version> versions) { + return true; + } + + } -En fait il n'y a pas vraiment de desavantage. La solution parrait simple et -efficace. - -Exemple de classe de conversion +Classes de migration de version ------------------------------- +Ensuite, il faut créer une classe par migration. + +Par exemple, voici la classe migrant le shema de la version 0 à la version 1 : +MigrationV0V1. + :: - class Convert { - convert() { - // recherche de la version actuelle de la base topia->schemaVersion - // recherche de la version utilisé par l'application config->schemaVersion - // si les versions divergent, appel de toutes les méthodes convert - // pour arriver à la version de schema actuelle dans config + public class MigrationV0V1 extends MigrationCallBackForVersion { + + public MigrationV0V1(Version version, TopiaMigrationCallbackByClass callBack) { + super(version, callBack); } - convert_2_0_1_to_2_0_2(){ - // chargement schema 2_0_1 en utilisant les dynamic-map - // chargement schema 2_0_2 en utilisant les dynamic-map - ... - // code de migration d'un schema a un autre - // si pas de modification pour une classe, il faut au moins faire une - // copie pour remplir les nouvelles tables - ... - // modification de version dans topia->schemaVersion - // commit nouvelle données - // on peut supprimer les anciennes tables + @Override + protected void prepareMigrationScript(TopiaContextImplementor tx, + List<String> queries, boolean showSql, boolean showProgression) + throws TopiaException { + queries.add("alter table SETOFVESSELS add column TECHNICALEFFICIENCYEQUATION VARCHAR(255);"); + queries.add("alter table STRATEGY add column INACTIVITYEQUATIONUSED BIT default false;"); + queries.add("alter table STRATEGY add column INACTIVITYEQUATION VARCHAR(255);"); + queries.add("alter table STRATEGYMONTHINFO alter NUMBEROFTRIPS double;"); + queries.add("alter table STRATEGYMONTHINFO alter MININACTIVITYDAYS double;"); } - - convert_2_0_2_to_2_1_0(){ - // idem - } } -Hibernate et XML -================ +Configuration du topia context +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Il est aussi possible de lire une base en XML. On utiliserait donc la même -technique que pour les dynamic-map, mais on utiliserait du XSL pour -convertir le XML vers le schéma souhaité. De cette facon il n'y a pas besoin -de table intermédiaire ni de numéro de version de schéma dans les tables. +Pour finir, il faut ajouter l'utilisation de la migration par ces classe +dans le topia context. En effet, la migration est effectuée automatiquement +lors de l'ouverture d'un TopiaContext. -- Export XML en utilisant le vieux mapping -- converion en enchainant les transformations XSL -- Import du nouveau fichier XML +Configuration: -Inconvéniant ------------- +:: -si le volume est important il peut-etre long de tout transformer en XML. - + config.put(TopiaMigrationService.TOPIA_SERVICE_NAME, TopiaMigrationEngine.class.getName()); + config.put(TopiaMigrationService.MIGRATION_CALLBACK, DatabaseMigrationClass.getName()); + +ou dans un fichier de properties : + +:: + + topia.service.migration=org.nuiton.topia.migration.TopiaMigrationEngine + topia.service.migration.callback=org.test.myapp.DatabaseMigrationClass + + Kettle -====== +------ Une autre idee est de ne pas utiliser hibernate mais kettle pour la migration des données. Nous aurions de la même façon dans le nom des tables