Sections

  1. Réingénérie de la base de donnée
  2. Débuter avec la migration de données
  3. Trucs et astuces de migrations

Gestion de la base de données

Démarrer un nouveau projet avec grails est vraiment quelque chose de simple puisque vos modèles définissent le schéma de votre BD, vous n'avez donc pas à vous en occuper vous-même. Mais qu'arrive-t-il lorsque vous devez travailler avec une BD existante ? Dans ce cas, vous devrez mapper chaque propriété de vos modèles à la main. Cette tâche peut être plutôt longue si vous partez d'un schéma volumineux.

Ensuite, que se passera-t-il lorsque vous voudrez mettre à jour votre application ? Vous ne pourrez pas vous limiter au schéma initial sans quoi, votre application ne pourra évoluer adéquatement. Il vous faudra donc une solution pour migrer votre schema et votre base de donnée.

Heureusement, des plugins existents pour répondre à ces deux scénarii. Ce guide vous aidera tirer le maximum de ces plugins.

Réingénérie de la base de donnée

TBC

Débuter avec la migration de données

Le plugin de migration vous permettra de suivre les modifications faite à la structure de votre base de données. Les changements créés automatiquement par Grails deviennent visibles, suivables et peuvent être versionnés dans votre outil de contrôle des sources.

Dans cette section, nous allons vous accompagner dans une application simple utilisant le plugin de migration pour voir comment il fonctionne.

Créer votre application et ajoutez un modèle

Supposons que vous avez déjà Grails d'installé, ouvrez un terminal et écrivez :

grails create-app myApp
cd myApp
grails create-domain-class com.example.Book
grails generate-all "*"

Vous venez tout juste de créer une simple application Grails avec un modèle appelé Book. Vous avez aussi généré les vues pour cet objet.

Installer le plugin

Dans votre terminal, écrivez

grails install-plugin database-migration

Le plugin 'Database Migration' sera installé.

Créer un changelog

Ce plugin fait un suivi de tous les changements fait dans votre application via un changelog. Voyez le changelog comme un journal des changements effectués à votre BD tout au long du cycle de vie de votre application.

Dans un terminal, écrivez

grails dbm-generate-changelog changelog.groovy

Déplacez-vous dans le répertoire grails-app/migrations/, vous devriez y trouver le fichier changelog.groovy.

Ouvrez ce fichier, vous y verrez quelque chose comme ceci :

databaseChangeLog = {
}

Rien de très passionnant, seulement un liste vide de changements.

Capturer un changelog initiale de vos modèles

Si vous retournez quelques paragraphes plus haut, vous verrez que nous avons créer un modèle Book. Comment l'ajouter au journal ?

Pour générer une copie de votre BD actuelle, vous devez appeler la commande dbm-gorm-diff. Cette commande va générer un nouveau fichier contenant les changements qui ne se trouvent pas de le changelog. Pensez à un mini commit avec git.

Vérifiez que vous êtes bien dans le répertoire racine de votre projet et lancez la commande suivante :

grails dbm-gorm-diff 2012-02-01-initial-database.groovy --add

Le premier paramètre 2012-02-01-initial-database.groovy est le nom du fichier qui contiendra les changements initial de la BD.

Notez que j'ai préfixé la date avec le nom du fichier. Cette ajout devient pratique lorsqu'on travaille avec d'autres membres d'une équipe afin de voir de façon séquentiel les changements effectués à la BD.

Le deuxième paramètre add indique au plugin d'ajouter la nouvelle migration dans la liste des migrations.

Ouvrez le répertoire grails-app/migrations. Vous devriez voir les deux fichiers suivants :

changelog.groovy

databaseChangeLog = {

include file: '2012-02-01-initial-database.groovy' }

2012-02-01-initial-database.groovy

databaseChangeLog = {

changeSet(author: "tomaslin (generated)", id: "1328137085507-1") { createTable(tableName: "book") { column(autoIncrement: "true", name: "id", type: "bigint") { constraints(nullable: "false", primaryKey: "true", primaryKeyName: "bookPK") }

column(name: "version", type: "bigint") { constraints(nullable: "false") } } } }

Comme vous pouvez le constater, un nouveau changelog a été généré avec votre BD initiale et a été inclu dans votre fichier de migration original.

Désactiver la gestion automatique de la BD par Grails

Par défaut, Grails est configuré pour gérer automatiquement votre BD. Cela marche bien en développement, mais en production, il vaut mieux utiliser le plugin de migration seulement.

Pour désactiver la mise à jour automatique des modèles par grails, ouvrez le fichier grails-app/conf/DataSource.groovy et supprimez les références à dbCreate.

Par exemple, votre environnement de développement ressemblait à ceci:

development {
         dataSource {
              dbCreate = "create-drop" // one of 'create', 'create-drop', 'update', 'validate', ''
              url = "jdbc:h2:mem:devDb;MVCC=TRUE"
          }
      }

Maintenant, il doit plutôt être comme ceci :

development {
         dataSource {
              url = "jdbc:h2:mem:devDb;MVCC=TRUE"
          }
      }

Pour que les migrations fonctionnent avec H2, vous devez utiliser une BD persistente.

Supprimer les lignes qui font référence à la BD en mémoire dans votre chaîne de connexion (ou changez pour mysql ou postgres). Pour se faire, supprimez :mem: dans l'url de votre datasource.

Votre environnement de développement final devrait ressembler à ceci:

development {
         dataSource {
              url = "jdbc:h2:devDb;MVCC=TRUE"
          }
      }

NB : Cela va créer un fichier que h2 va utiliser comme BD. Assurez-vous de ne pas versionner ce fichier dans votre outil de contrôle des sources.

Activer les migrations automatiques

Mettez en marche votre application avec la commande grails run-app. Naviguez avec votre fureteur à la liste des Book via http://localhost:8080/myApp/book/list

Oh non! Il y a une erreur car nous avons indiqué à Grails de ne pas effectuer automatiquement la mise à jour de la BD. Présentement, la table Book n'existe pas et nos clients aiguises leurs fourches. Pour corriger ce problème, nous devons activer la migration automatique au démarrage de l'application.

Ouvrez le fichier Config.groovy et ajoutez ceci :

grails.plugin.databasemigration.updateOnStart = true
grails.plugin.databasemigration.updateOnStartFileNames = ['changelog.groovy']

Ces lignes permettront à votre application de mettre à jour la BD avec vos migrations à chaque démarrage. Redémarrez votre application et redirigez à nouveau votre fureteur à l'url http://localhost:8080/myApp/book/list. Il ne devrait plus y avoir d'erreur - vos clients mettent de côté leurs fourches aiguisées.

Ajouter et suivre les changements

Maintenant que vous avez une copie de votre BD initial et votre application Grails se met à jour automatiquement avec les plus récentes modifications du schéma, ajoutons quelques scénarii qui ont besoin de migrations de la BD pour voir ce qui arrivera.

Ouvrez le fichier grails-app/domain/com/example/Book.groovy

Ajoutez les propriétés suivantes :

String name
	String backCover
	Date publicationDate

Votre modèle devrait maintenant ressembler à ceci :

package com.example

class Book {

String name String backCover Date publicationDate

static constraints = { } }

Ouvrez votre terminal et ajouter une migration

grails dbm-update
grails dbm-gorm-diff 2012-02-01-added-new-fields-to-book.groovy --add

Il y a une nouvelle commande, dbm-update. Dbm-update indique à l'application Grails de se mettre à jour avec la plus récente version de la BD. Vous DEVEZ ABSOLUMENT exécuter cette commande avant de créer une nouvelle migration afin de s'assurer que toutes vos modifications ont été appliqués avant d'en faire une migration.

Ouvrez le fichier grails-app/migrations/2012-02-01-added-new-fields-to-book.groovy

Vous devriez voir quelque chose comme:

databaseChangeLog = {

changeSet(author: "tomaslin (generated)", id: "1328139836195-1") { addColumn(tableName: "book") { column(name: "back_cover", type: "varchar(255)") { constraints(nullable: "false") } } }

changeSet(author: "tomaslin (generated)", id: "1328139836195-2") { addColumn(tableName: "book") { column(name: "name", type: "varchar(255)") { constraints(nullable: "false") } } }

changeSet(author: "tomaslin (generated)", id: "1328139836195-4") { addColumn(tableName: "book") { column(name: "publication_date", type: "timestamp") { constraints(nullable: "false") } } } }

Comme vous pouvez le constater, le plugin de migration a créé plusieurs changements qui suivent les colonnes qui doivent être ajoutée à cause des nouvelles propriétés du modèle Book.

Regardez le fichier changelog.groovy

databaseChangeLog = {

include file: '2012-02-01-initial-database.groovy'

include file: '2012-02-01-added-new-fields-to-book.groovy' }

La nouvelle migration a été ajouté après la migration initiale. L'ordre des fichiers dans le changelog permet de suivre l'ordre d'exécution de chaque migration.

Mais attendez un instant, qu'arrive-t-il si nous changeons notre modèle ? Supposons que nous voulons utiliser un champs TEXT au lieu de VARCHAR.

Ajoutons le 'mapping' pour le 'bookCover':

static mapping = {
	backCover sqlType: 'text'
}

Notre nouveau modèle ressemble à ceci :

package com.example

class Book {

String name String backCover Date publicationDate

static constraints = { }

static mapping = { backCover sqlType: 'text' } }

Exécutez une nouvelle migration pour voir ce qui arrive

grails dbm-update
grails dbm-gorm-diff 2012-02-01-changed-property-types.groovy --add

Vous pouvez voir qu'une nouvelle migration a été créé afin de modifier la table.

changeSet(author: "tomaslin (generated)", id: "1328141759596-1") {
	modifyDataType(columnName: "BACK_COVER", newDataType: "text(255)", tableName: "BOOK")
}

En continuant votre développement, continuez d'utiliser le mécanisme de dbm-gorm-diff pour garder un suivi de vos changements. Et c'est terminé!

Credits supplémentaires

Trucs et astuces de migrations

Assurez vous d'enregistrer vos migrations

Un des problèmes d'utilisation des outils Grails est que, la plupart du temps, les migrations sont oubliées sur le bureau des développeurs. N'oubliez pas d'enregistrer vos migrations après les avoir créées.

Prévisualiser les migrations

Si vous ne spécifiez pas l'option add à la commande dbm-gorm-diff, vous pourrez voir la migration directement dans votre console.

Toujours réviser vos migrations

Une bonne pratique est d'inspecter visuellement et d'exécuter dbm-update après chaque nouvelle migration pour voir si rien n'est brisé. Si vous utilisez d'autres dialectes d'hibernate, il est possible de vous ramasser avec plusieurs index et clé étrangères complètement inutiles.

Réinitialiser les verrous

Il arrive parfois que votre application bloque à cause de quelque chose qui à verrouillé le changelog de la BD et qui n'est plus en cours d'exécution. Votre application bloquera au démarrage car elle ne peut pas acquérir le verrou. Pour réinitialiser le verrou manuellement, entrez la commande SQL suivante :

update DATABASECHANGELOGLOCK set LOCKED=0, LOCKGRANTED=null, LOCKEDBY=null

Ne modifiez jamais une migration, jamais!

Le plugin de migration effectue un checksum pour chaque migration appliquée. Si vous regarder dans votre BD, vous verrez une table appelée DATABASECHANGELOG contenant les migrations antérieures.

Si vous modifiez accidentellement une migration, pendant un refactoring par exemple, ces checksums ne correpsonderont plus aux migrations et le plugin va tout casser!

Pour corriger ce problème, vous devrez réinitialiser les checksums de la BD. Pour se faire, utilisez la commande suivante :

grails dbm-clear-checksums

Vous pouvez utiliser SQL pour assigner des valeurs par défaut au nouvelles colonnes

Une autre commande pratique supporté par le plugin de migration est la commande sql(). Cette commande permet d'exécuter des commandes SQL directement dans votre migration. Si vous ajouter des nouvelles colonnes et que vous souhaitez y insérer une valeur, vous pouvez le faire en modifiant le changeset. Comme ceci

changeSet(author: "tomaslin (generated)", id: "1328139836195-4") {
	addColumn(tableName: "book") {
		column(name: "back_cover", type: "varchar(255)") {
			constraints(nullable: "false")
		}
	}
	sql(''' update book set back_cover = 'Not available' ''' )
}

Vous pouvez utiliser vos propres changesets dans groovy, mais n'utilisez pas vos modèles dedans

Le plugin inclue un type de migration intéressant appellé grailsChange. GrailsChange vous permet d'écrire vos propres changements en groovy. Cependant, sachez qu'utiliser vos modèles dans ces migrations n'est pas sécuritaire du tout car le modèle utilisé dans le temps de l'écriture d'une migration peut être différent que celui à son exécution.

Pour des changements plus complexe, utilisez dbm-diff pour comparer des BD

Si vous souhaitez comparer deux version diffrentes d'une BD, utilisez cette commande :

grails dbm-diff databaseToCompareTo 2012-02-01-database-dif.groovy --add

Seulement 'compile' peut résoudre 'Unable to resolve class errors'

Si vous essayez de générer votre changelog pour la première fois, il se peut que vous rencontrez cette erreur:

| Configuring classpath
| Error Error executing script DbmGenerateGormChangelog: startup failed:
_DatabaseMigrationCommon_groovy: 28: unable to resolve class org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration
  line 28, column 1.

Pour corriger ce problème, exécutez grails compile avant de générer le changelog et ainsi, ensuite, la migration fonctionnera proprement.