Chapitre 3: Migration de Schéma avec Flyway
Introduction
Avez-vous déjà vécu cette situation?
"Attends, tu as exécuté quel script SQL en production?"
"Celui avec les nouvelles colonnes... je crois?"
"Mais il y avait 3 versions! V1, V2 ou V2_final_final?"
💀
Flyway résout ce problème en versionnant votre schéma de base de données comme vous versionnez votre code.
🎯 Objectifs du Chapitre
À la fin de ce chapitre, vous saurez:
- ✅ Pourquoi Flyway est essentiel pour la production
- ✅ Installer et configurer Flyway avec Spring Boot
- ✅ Créer des scripts de migration
- ✅ Comprendre la convention de nommage
- ✅ Gérer les migrations répétables
- ✅ Résoudre les problèmes courants
1. Pourquoi Versionner le Schéma?
Le Problème
Problèmes:
- On ne sait pas quel script a été exécuté
- Pas de trace de l'historique
- Risque d'oublier une migration
- Impossible de reproduire l'environnement
La Solution: Flyway
Avantages:
- ✅ Historique complet des migrations
- ✅ Exécution automatique au démarrage
- ✅ Identique sur tous les environnements
- ✅ Impossible d'oublier une migration
2. Installation
Dépendance Maven
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<!-- Pour PostgreSQL, ajoutez aussi: -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-postgresql</artifactId>
</dependency>
Avec Spring Boot
C'est tout! Spring Boot auto-configure Flyway automatiquement.
# application.properties
spring.flyway.enabled=true # Par défaut: true si Flyway est dans le classpath
3. Structure des Fichiers
Emplacement par Défaut
src/
└── main/
└── resources/
└── db/
└── migration/
├── V1__create_users_table.sql
├── V2__add_email_column.sql
├── V3__create_orders_table.sql
└── V4__add_index_on_email.sql
Convention de Nommage
V{version}__{description}.sql
│ │ │
│ │ └── Description (underscores = espaces)
│ └── Numéro de version (1, 2, 2.1, etc.)
└── Préfixe "V" (Versioned migration)
Exemples valides:
V1__create_users.sql
V2__add_email_to_users.sql
V2.1__add_phone_number.sql
V10__create_products.sql
V100__final_cleanup.sql
Exemples invalides:
V1_create_users.sql ❌ Un seul underscore
v1__create_users.sql ❌ Minuscule
1__create_users.sql ❌ Pas de préfixe V
V1__create users.sql ❌ Espaces dans le nom
4. Créer des Migrations
Migration 1: Créer une Table
-- V1__create_users_table.sql
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Migration 2: Ajouter une Colonne
-- V2__add_email_to_users.sql
ALTER TABLE users
ADD COLUMN email VARCHAR(100) UNIQUE;
Migration 3: Créer une Autre Table avec FK
-- V3__create_orders_table.sql
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id),
total_amount DECIMAL(10, 2) NOT NULL,
status VARCHAR(20) DEFAULT 'PENDING',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_orders_user_id ON orders(user_id);
Migration 4: Insérer des Données
-- V4__insert_initial_data.sql
INSERT INTO users (username, password, email) VALUES
('admin', '$2a$10$hashedpassword', 'admin@example.com'),
('user1', '$2a$10$hashedpassword', 'user1@example.com');
5. Comment Flyway Fonctionne
La Table flyway_schema_history
Flyway crée automatiquement une table pour tracker les migrations:
SELECT * FROM flyway_schema_history;
| installed_rank | version | description | type | script | checksum | installed_on | execution_time | success |
|---|---|---|---|---|---|---|---|---|
| 1 | 1 | create users table | SQL | V1__create_users_table.sql | -123456 | 2025-01-15 10:00 | 45 | true |
| 2 | 2 | add email to users | SQL | V2__add_email_to_users.sql | 789012 | 2025-01-15 10:01 | 12 | true |
| 3 | 3 | create orders table | SQL | V3__create_orders_table.sql | 345678 | 2025-01-16 09:00 | 67 | true |
Flux d'Exécution
6. Configuration Spring Boot
Configuration de Base
# application.properties
# Activer Flyway
spring.flyway.enabled=true
# Emplacement des scripts (par défaut: classpath:db/migration)
spring.flyway.locations=classpath:db/migration
# Baseline (pour les BDD existantes)
spring.flyway.baseline-on-migrate=false
spring.flyway.baseline-version=1
Configuration Avancée
# Schéma cible
spring.flyway.schemas=public
# Nettoyage (DANGEREUX en prod!)
spring.flyway.clean-disabled=true
# Validation
spring.flyway.validate-on-migrate=true
# Placeholder substitution
spring.flyway.placeholder-replacement=true
spring.flyway.placeholders.table_prefix=app_
Configuration YAML
spring:
flyway:
enabled: true
locations: classpath:db/migration
baseline-on-migrate: false
clean-disabled: true
validate-on-migrate: true
7. Migrations Répétables
Cas d'Utilisation
Certains objets doivent être recréés à chaque changement:
- Vues (CREATE OR REPLACE VIEW)
- Procédures stockées
- Fonctions
- Données de référence
Convention
R__{description}.sql
│
└── Préfixe "R" (Repeatable)
Exemple
-- R__create_active_users_view.sql
CREATE OR REPLACE VIEW active_users AS
SELECT id, username, email
FROM users
WHERE last_login > NOW() - INTERVAL '30 days';
Comportement:
- Flyway calcule un checksum du fichier
- Si le fichier change → réexécuté
- Exécuté APRÈS toutes les migrations versionnées
8. Gestion des Environnements
Structure Multi-Environnement
src/main/resources/
├── db/
│ └── migration/
│ ├── V1__create_tables.sql # Tous les environnements
│ ├── V2__add_columns.sql
│ └── V3__create_indexes.sql
│
├── db-dev/
│ └── migration/
│ └── V1000__dev_test_data.sql # Données de test (dev seulement)
│
└── db-prod/
└── migration/
└── V1001__prod_config.sql # Config production
Configuration par Profil
# application-dev.properties
spring.flyway.locations=classpath:db/migration,classpath:db-dev/migration
# application-prod.properties
spring.flyway.locations=classpath:db/migration,classpath:db-prod/migration
9. Intégration avec JPA/Hibernate
Ordre d'Exécution
# IMPORTANT: Flyway s'exécute AVANT Hibernate
spring.jpa.hibernate.ddl-auto=validate # Ne pas générer de schéma
# Ou pour le dev:
spring.jpa.hibernate.ddl-auto=none # Flyway gère tout
Workflow Recommandé
En développement: Utilisez ddl-auto=validate pour vérifier que vos entités correspondent au schéma.
En production: Utilisez ddl-auto=none et laissez Flyway gérer tout.
10. Commandes Flyway
Via Maven Plugin
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<configuration>
<url>jdbc:postgresql://localhost:5432/mydb</url>
<user>postgres</user>
<password>secret</password>
</configuration>
</plugin>
# Voir les migrations appliquées
mvn flyway:info
# Appliquer les migrations
mvn flyway:migrate
# Valider les migrations
mvn flyway:validate
# Réparer l'historique (attention!)
mvn flyway:repair
# Nettoyer la BDD (DANGEREUX!)
mvn flyway:clean
Exemple de Sortie flyway:info
+-----------+---------+----------------------+------+---------------------+---------+
| Category | Version | Description | Type | Installed On | State |
+-----------+---------+----------------------+------+---------------------+---------+
| Versioned | 1 | create users table | SQL | 2025-01-15 10:00:00 | Success |
| Versioned | 2 | add email to users | SQL | 2025-01-15 10:01:00 | Success |
| Versioned | 3 | create orders table | SQL | 2025-01-16 09:00:00 | Success |
| Versioned | 4 | add indexes | SQL | | Pending |
+-----------+---------+----------------------+------+---------------------+---------+
11. Problèmes Courants et Solutions
❌ Checksum Mismatch
FlywayException: Validate failed: Migration checksum mismatch for migration version 2
-> Applied to database : -123456
-> Resolved locally : 789012
Cause: Le fichier de migration a été modifié après son exécution.
Solutions:
- Ne jamais modifier une migration déjà exécutée!
- Si c'est en dev:
mvn flyway:repair - Créer une nouvelle migration pour corriger
❌ Migration Not Found
FlywayException: Detected resolved migration not applied to database: 3
Cause: Une migration existe en BDD mais pas dans les fichiers.
Solution: Restaurer le fichier de migration manquant.
❌ Out of Order Migration
FlywayException: Detected resolved migration not applied to database: 2
(Applied migrations are: 1, 3)
Cause: Migration V2 ajoutée après V3.
Solution:
spring.flyway.out-of-order=true # Autoriser les migrations hors ordre
❌ Failed Migration
FlywayException: Migration V4__add_index.sql failed
Solution:
- Corriger le SQL dans le fichier
- Exécuter
mvn flyway:repair(marque comme résolu) - Exécuter
mvn flyway:migrate
12. Best Practices
✅ Règles d'Or
1. NE JAMAIS modifier une migration déjà exécutée
→ Créer une nouvelle migration pour corriger
2. NE JAMAIS supprimer une migration exécutée
→ L'historique doit rester cohérent
3. TOUJOURS tester les migrations sur un environnement de test
→ Avant de déployer en production
4. TOUJOURS avoir un backup avant migration en production
→ Au cas où
5. Garder les migrations petites et focalisées
→ Une migration = une modification logique
✅ Nommage Clair
-- ✅ BON
V1__create_users_table.sql
V2__add_email_column_to_users.sql
V3__create_orders_with_foreign_keys.sql
-- ❌ MAUVAIS
V1__changes.sql
V2__update.sql
V3__fix.sql
✅ Scripts Idempotents
-- ✅ BON: Vérifie avant de créer
CREATE TABLE IF NOT EXISTS users (...);
-- ✅ BON: Vérifie avant d'ajouter une colonne (PostgreSQL)
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
WHERE table_name = 'users' AND column_name = 'phone') THEN
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
END IF;
END $$;
13. Résumé
Points Clés
| Concept | Description |
|---|---|
| Flyway | Outil de migration de schéma versionné |
| V{n}__ | Préfixe pour migrations versionnées |
| R__ | Préfixe pour migrations répétables |
| flyway_schema_history | Table qui track les migrations |
| checksum | Hash du fichier pour détecter les modifications |
Workflow Typique
1. Créer un fichier V{n}__description.sql
2. Écrire le SQL
3. Démarrer l'application (ou mvn flyway:migrate)
4. Flyway exécute automatiquement
5. Vérifier avec mvn flyway:info
Configuration Minimale
# Spring Boot + Flyway
spring.flyway.enabled=true
spring.flyway.locations=classpath:db/migration
spring.jpa.hibernate.ddl-auto=validate
🎯 Prochaine Étape
Flyway est l'outil le plus populaire, mais Liquibase offre une alternative avec des fonctionnalités différentes. Voyons quand choisir l'un ou l'autre.
➡️ Chapitre 4: Migration avec Liquibase