Chapitre 4: Migration avec Liquibase
Introduction
Liquibase est une alternative populaire à Flyway. Alors que Flyway utilise principalement du SQL brut, Liquibase propose un format agnostique de base de données (XML, YAML, JSON) qui peut être converti en SQL pour différentes BDD.
🎯 Objectifs du Chapitre
À la fin de ce chapitre, vous saurez:
- ✅ Comprendre les différences entre Flyway et Liquibase
- ✅ Configurer Liquibase avec Spring Boot
- ✅ Créer des changelogs en XML, YAML et SQL
- ✅ Utiliser les rollbacks automatiques
- ✅ Choisir entre Flyway et Liquibase
1. Flyway vs Liquibase
Comparaison Rapide
| Aspect | Flyway | Liquibase |
|---|---|---|
| Format principal | SQL natif | XML, YAML, JSON, SQL |
| Courbe d'apprentissage | Très facile | Moyenne |
| Portabilité multi-BDD | Faible (SQL spécifique) | Élevée |
| Rollback | Manuel | Automatique pour certaines opérations |
| Communauté | Très large | Large |
| Spring Boot | Excellente intégration | Excellente intégration |
Quand Choisir Flyway?
✅ Utilisez Flyway si:
- Vous utilisez une seule base de données
- Vous préférez le SQL natif
- Vous voulez la simplicité maximale
- Votre équipe connaît bien SQL
Quand Choisir Liquibase?
✅ Utilisez Liquibase si:
- Vous devez supporter plusieurs BDD (PostgreSQL, MySQL, Oracle...)
- Vous voulez des rollbacks automatiques
- Vous préférez les formats déclaratifs (YAML, XML)
- Vous avez des changements complexes à gérer
2. Installation
Dépendance Maven
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
Attention: N'utilisez pas Flyway et Liquibase ensemble dans le même projet!
3. Structure des Fichiers
Structure Recommandée
src/
└── main/
└── resources/
└── db/
└── changelog/
├── db.changelog-master.xml # Fichier principal
├── changes/
│ ├── 001-create-users.xml
│ ├── 002-add-email.xml
│ └── 003-create-orders.xml
└── data/
└── initial-data.xml # Données initiales
Fichier Master (Point d'Entrée)
<!-- db.changelog-master.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<!-- Inclure les fichiers de changements -->
<include file="db/changelog/changes/001-create-users.xml"/>
<include file="db/changelog/changes/002-add-email.xml"/>
<include file="db/changelog/changes/003-create-orders.xml"/>
<!-- Ou inclure tout un dossier -->
<includeAll path="db/changelog/data/"/>
</databaseChangeLog>
4. Format XML
Créer une Table
<!-- 001-create-users.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet id="1" author="dev">
<comment>Créer la table users</comment>
<createTable tableName="users">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="username" type="VARCHAR(50)">
<constraints nullable="false" unique="true"/>
</column>
<column name="password" type="VARCHAR(100)">
<constraints nullable="false"/>
</column>
<column name="created_at" type="TIMESTAMP" defaultValueComputed="CURRENT_TIMESTAMP"/>
</createTable>
<!-- Rollback automatique: DROP TABLE users -->
</changeSet>
</databaseChangeLog>
Ajouter une Colonne
<!-- 002-add-email.xml -->
<databaseChangeLog ...>
<changeSet id="2" author="dev">
<comment>Ajouter la colonne email</comment>
<addColumn tableName="users">
<column name="email" type="VARCHAR(100)">
<constraints unique="true"/>
</column>
</addColumn>
<!-- Rollback automatique: DROP COLUMN email -->
</changeSet>
</databaseChangeLog>
Créer un Index
<changeSet id="3" author="dev">
<createIndex tableName="users" indexName="idx_users_email">
<column name="email"/>
</createIndex>
</changeSet>
Insérer des Données
<changeSet id="4" author="dev">
<insert tableName="users">
<column name="username" value="admin"/>
<column name="password" value="$2a$10$hashedpassword"/>
<column name="email" value="admin@example.com"/>
</insert>
<!-- Rollback manuel nécessaire -->
<rollback>
DELETE FROM users WHERE username = 'admin';
</rollback>
</changeSet>
5. Format YAML
Beaucoup plus lisible que XML!
Créer une Table
# 001-create-users.yaml
databaseChangeLog:
- changeSet:
id: 1
author: dev
comment: Créer la table users
changes:
- createTable:
tableName: users
columns:
- column:
name: id
type: BIGINT
autoIncrement: true
constraints:
primaryKey: true
nullable: false
- column:
name: username
type: VARCHAR(50)
constraints:
nullable: false
unique: true
- column:
name: password
type: VARCHAR(100)
constraints:
nullable: false
- column:
name: created_at
type: TIMESTAMP
defaultValueComputed: CURRENT_TIMESTAMP
Ajouter une Colonne
# 002-add-email.yaml
databaseChangeLog:
- changeSet:
id: 2
author: dev
comment: Ajouter email à users
changes:
- addColumn:
tableName: users
columns:
- column:
name: email
type: VARCHAR(100)
constraints:
unique: true
Master File en YAML
# db.changelog-master.yaml
databaseChangeLog:
- include:
file: db/changelog/changes/001-create-users.yaml
- include:
file: db/changelog/changes/002-add-email.yaml
- includeAll:
path: db/changelog/data/
6. Format SQL (Comme Flyway)
Vous pouvez aussi utiliser du SQL brut:
-- 001-create-users.sql
-- liquibase formatted sql
-- changeset dev:1
-- comment: Créer la table users
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- rollback DROP TABLE users;
-- 002-add-email.sql
-- liquibase formatted sql
-- changeset dev:2
ALTER TABLE users ADD COLUMN email VARCHAR(100) UNIQUE;
-- rollback ALTER TABLE users DROP COLUMN email;
7. Configuration Spring Boot
Configuration Minimale
# application.properties
spring.liquibase.enabled=true
spring.liquibase.change-log=classpath:db/changelog/db.changelog-master.xml
Configuration Complète
# Activer Liquibase
spring.liquibase.enabled=true
# Fichier de changelog principal
spring.liquibase.change-log=classpath:db/changelog/db.changelog-master.yaml
# Schémas
spring.liquibase.default-schema=public
spring.liquibase.liquibase-schema=public
# Contextes (dev, test, prod)
spring.liquibase.contexts=dev
# Labels
spring.liquibase.labels=
# Rollback si échec
spring.liquibase.rollback-file=rollback.sql
Format YAML
spring:
liquibase:
enabled: true
change-log: classpath:db/changelog/db.changelog-master.yaml
default-schema: public
contexts: dev
8. Rollbacks
Rollback Automatique
Liquibase génère automatiquement les rollbacks pour:
| Opération | Rollback Automatique |
|---|---|
| createTable | DROP TABLE |
| addColumn | DROP COLUMN |
| createIndex | DROP INDEX |
| addForeignKey | DROP FOREIGN KEY |
| insert | ❌ (manuel) |
| update | ❌ (manuel) |
| delete | ❌ (manuel) |
| sql | ❌ (manuel) |
Rollback Manuel
<changeSet id="5" author="dev">
<update tableName="users">
<column name="status" value="ACTIVE"/>
<where>status IS NULL</where>
</update>
<!-- Rollback manuel obligatoire -->
<rollback>
<update tableName="users">
<column name="status" valueComputed="NULL"/>
<where>status = 'ACTIVE'</where>
</update>
</rollback>
</changeSet>
Exécuter un Rollback
# Rollback du dernier changeset
mvn liquibase:rollback -Dliquibase.rollbackCount=1
# Rollback jusqu'à un tag
mvn liquibase:rollback -Dliquibase.rollbackTag=v1.0
# Rollback jusqu'à une date
mvn liquibase:rollback -Dliquibase.rollbackDate=2025-01-01
9. Contextes et Labels
Contextes
Exécuter certains changesets seulement dans certains environnements:
<!-- Seulement en dev -->
<changeSet id="10" author="dev" context="dev">
<insert tableName="users">
<column name="username" value="testuser"/>
</insert>
</changeSet>
<!-- Seulement en prod -->
<changeSet id="11" author="dev" context="prod">
<insert tableName="config">
<column name="key" value="feature.flag"/>
<column name="value" value="true"/>
</insert>
</changeSet>
# application-dev.properties
spring.liquibase.contexts=dev
# application-prod.properties
spring.liquibase.contexts=prod
Labels
Grouper des changesets par fonctionnalité:
<changeSet id="20" author="dev" labels="user-feature">
<!-- Changements liés aux utilisateurs -->
</changeSet>
<changeSet id="21" author="dev" labels="order-feature">
<!-- Changements liés aux commandes -->
</changeSet>
10. La Table DATABASECHANGELOG
Liquibase track les changements dans une table:
SELECT * FROM DATABASECHANGELOG;
| ID | AUTHOR | FILENAME | DATEEXECUTED | ORDEREXECUTED | EXECTYPE | MD5SUM | DESCRIPTION |
|---|---|---|---|---|---|---|---|
| 1 | dev | 001-create-users.xml | 2025-01-15 | 1 | EXECUTED | abc123 | createTable tableName=users |
| 2 | dev | 002-add-email.xml | 2025-01-15 | 2 | EXECUTED | def456 | addColumn tableName=users |
11. Commandes Maven
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<configuration>
<propertyFile>src/main/resources/liquibase.properties</propertyFile>
</configuration>
</plugin>
# Voir le statut
mvn liquibase:status
# Appliquer les changements
mvn liquibase:update
# Générer le SQL sans l'exécuter
mvn liquibase:updateSQL
# Rollback
mvn liquibase:rollback -Dliquibase.rollbackCount=1
# Créer un tag
mvn liquibase:tag -Dliquibase.tag=v1.0
# Générer un changelog à partir d'une BDD existante
mvn liquibase:generateChangeLog
12. Résumé
Points Clés
| Concept | Description |
|---|---|
| ChangeLog | Fichier principal qui liste les changements |
| ChangeSet | Un changement atomique (id + author unique) |
| Contextes | Filtrer les changesets par environnement |
| Labels | Grouper les changesets par fonctionnalité |
| Rollback | Annuler les changements (auto ou manuel) |
Quand Utiliser Quel Outil?
Configuration Minimale
# application.yml avec Liquibase
spring:
liquibase:
enabled: true
change-log: classpath:db/changelog/db.changelog-master.yaml
Fichier Master Minimal
# db.changelog-master.yaml
databaseChangeLog:
- includeAll:
path: db/changelog/changes/
🎯 Prochaine Étape
Vous avez maintenant toute la théorie! Il est temps de pratiquer avec les travaux pratiques.
➡️ TP7.1: Configuration Datasource et Pool