الفصل 4: الترحيل باستخدام Liquibase
مقدمة
Liquibase هو بديل شائع لـ Flyway. بينما يستخدم Flyway بشكل رئيسي SQL الخام، يقدم Liquibase تنسيقًا محايدًا لقاعدة البيانات (XML، YAML، JSON) يمكن تحويله إلى SQL لعدة قواعد بيانات.
🎯 أهداف الفصل
في نهاية هذا الفصل، ستعرف:
- ✅ فهم الفروقات بين Flyway وLiquibase
- ✅ إعداد Liquibase مع Spring Boot
- ✅ إنشاء سجلات التغيير في XML، YAML وSQL
- ✅ استخدام التراجعات التلقائية
- ✅ الاختيار بين Flyway وLiquibase
1. Flyway مقابل Liquibase
مقارنة سريعة
| الجانب | Flyway | Liquibase |
|---|---|---|
| التنسيق الرئيسي | SQL الأصلي | XML، YAML، JSON، SQL |
| منحنى التعلم | سهل جدًا | متوسط |
| قابلية النقل بين قواعد البيانات | منخفضة (SQL محدد) | عالية |
| التراجع | يدوي | تلقائي لبعض العمليات |
| المجتمع | واسع جدًا | واسع |
| Spring Boot | تكامل ممتاز | تكامل ممتاز |
متى تختار Flyway؟
✅ استخدم Flyway إذا:
- كنت تستخدم قاعدة بيانات واحدة
- تفضل SQL الأصلي
- تريد البساطة القصوى
- فريقك يعرف SQL جيدًا
متى تختار Liquibase؟
✅ استخدم Liquibase إذا:
- تحتاج لدعم عدة قواعد بيانات (PostgreSQL، MySQL، Oracle...)
- تريد تراجعات تلقائية
- تفضل التنسيقات التصريحية (YAML، XML)
- لديك تغييرات معقدة لإدارتها
2. التثبيت
تبعية Maven
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
حذر
تنبيه: لا تستخدم Flyway وLiquibase معًا في نفس المشروع!
3. هيكل الملفات
الهيكل الموصى به
src/
└── main/
└── resources/
└── db/
└── changelog/
├── db.changelog-master.xml # الملف الرئيسي
├── changes/
│ ├── 001-create-users.xml
│ ├── 002-add-email.xml
│ └── 003-create-orders.xml
└── data/
└── initial-data.xml # البيانات الأولية
الملف الرئيسي (نقطة الدخول)
<!-- 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">
<!-- تضمين ملفات التغييرات -->
<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"/>
<!-- أو تضمين مجلد كامل -->
<includeAll path="db/changelog/data/"/>
</databaseChangeLog>
4. تنسيق XML
إنشاء جدول
<!-- 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>إنشاء جدول المستخدمين</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>
<!-- تراجع تلقائي: DROP TABLE users -->
</changeSet>
</databaseChangeLog>
إضافة عمود
<!-- 002-add-email.xml -->
<databaseChangeLog ...>
<changeSet id="2" author="dev">
<comment>إضافة عمود البريد الإلكتروني</comment>
<addColumn tableName="users">
<column name="email" type="VARCHAR(100)">
<constraints unique="true"/>
</column>
</addColumn>
<!-- تراجع تلقائي: DROP COLUMN email -->
</changeSet>
</databaseChangeLog>
إنشاء فهرس
<changeSet id="3" author="dev">
<createIndex tableName="users" indexName="idx_users_email">
<column name="email"/>
</createIndex>
</changeSet>
إدخال بيانات
<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>
DELETE FROM users WHERE username = 'admin';
</rollback>
</changeSet>
5. تنسيق YAML
أكثر قابلية للقراءة من XML!
إنشاء جدول
# 001-create-users.yaml
databaseChangeLog:
- changeSet:
id: 1
author: dev
comment: إنشاء جدول المستخدمين
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
إضافة عمود
# 002-add-email.yaml
databaseChangeLog:
- changeSet:
id: 2
author: dev
comment: إضافة البريد الإلكتروني إلى المستخدمين
changes:
- addColumn:
tableName: users
columns:
- column:
name: email
type: VARCHAR(100)
constraints:
unique: true
الملف الرئيسي في 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. تنسيق SQL (مثل Flyway)
يمكنك أيضًا استخدام SQL الخام:
-- 001-create-users.sql
-- liquibase formatted sql
-- changeset dev:1
-- comment: إنشاء جدول المستخدمين
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. إعداد Spring Boot
الإعداد الأدنى
# application.properties
spring.liquibase.enabled=true
spring.liquibase.change-log=classpath:db/changelog/db.changelog-master.xml
الإعداد الكامل
# تفعيل Liquibase
spring.liquibase.enabled=true
# ملف سجل التغيير الرئيسي
spring.liquibase.change-log=classpath:db/changelog/db.changelog-master.yaml
# المخططات
spring.liquibase.default-schema=public
spring.liquibase.liquibase-schema=public
# السياقات (dev، test، prod)
spring.liquibase.contexts=dev
# التسميات
spring.liquibase.labels=
# التراجع في حالة الفشل
spring.liquibase.rollback-file=rollback.sql
تنسيق YAML
spring:
liquibase:
enabled: true
change-log: classpath:db/changelog/db.changelog-master.yaml
default-schema: public
contexts: dev
8. التراجعات
التراجع التلقائي
Liquibase يولد التراجعات تلقائيًا لـ:
| العملية | التراجع التلقائي |
|---|---|
| createTable | DROP TABLE |
| addColumn | DROP COLUMN |
| createIndex | DROP INDEX |
| addForeignKey | DROP FOREIGN KEY |
| insert | ❌ (يدوي) |
| update | ❌ (يدوي) |
| delete | ❌ (يدوي) |
| sql | ❌ (يدوي) |
التراجع اليدوي
<changeSet id="5" author="dev">
<update tableName="users">
<column name="status" value="ACTIVE"/>
<where>status IS NULL</where>
</update>
<!-- تراجع يدوي إلزامي -->
<rollback>
<update tableName="users">
<column name="status" valueComputed="NULL"/>
<where>status = 'ACTIVE'</where>
</update>
</rollback>
</changeSet>
تنفيذ تراجع
# تراجع عن آخر مجموعة تغييرات
mvn liquibase:rollback -Dliquibase.rollbackCount=1
# تراجع حتى علامة
mvn liquibase:rollback -Dliquibase.rollbackTag=v1.0
# تراجع حتى تاريخ
mvn liquibase:rollback -Dliquibase.rollbackDate=2025-01-01
9. السياقات والتسميات
السياقات
تنفيذ بعض مجموعات التغييرات فقط في بيئات معينة:
<!-- فقط في dev -->
<changeSet id="10" author="dev" context="dev">
<insert tableName="users">
<column name="username" value="testuser"/>
</insert>
</changeSet>
<!-- فقط في 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
التسميات
تجميع مجموعات التغييرات حسب الميزة:
<changeSet id="20" author="dev" labels="user-feature">
<!-- تغييرات متعلقة بالمستخدمين -->
</changeSet>
<changeSet id="21" author="dev" labels="order-feature">
<!-- تغييرات متعلقة بالطلبات -->
</changeSet>
10. جدول DATABASECHANGELOG
Liquibase يتتبع التغييرات في جدول:
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. أوامر Maven
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<configuration>
<propertyFile>src/main/resources/liquibase.properties</propertyFile>
</configuration>
</plugin>
# عرض الحالة
mvn liquibase:status
# تطبيق التغييرات
mvn liquibase:update
# توليد SQL بدون تنفيذه
mvn liquibase:updateSQL
# التراجع
mvn liquibase:rollback -Dliquibase.rollbackCount=1
# إنشاء علامة
mvn liquibase:tag -Dliquibase.tag=v1.0
# توليد سجل التغيير من قاعدة بيانات موجودة
mvn liquibase:generateChangeLog
12. ملخص
النقاط الرئيسية
| المفهوم | الوصف |
|---|---|
| ChangeLog | الملف الرئيسي الذي يسرد التغييرات |
| ChangeSet | تغيير ذري (معرف + مؤلف فريد) |
| السياقات | تصفية مجموعات التغييرات حسب البيئة |
| التسميات | تجميع مجموعات التغييرات حسب الميزة |
| التراجع | إلغاء التغييرات (تلقائي أو يدوي) |
متى تستخدم أي أداة؟
الإعداد الأدنى
# application.yml مع Liquibase
spring:
liquibase:
enabled: true
change-log: classpath:db/changelog/db.changelog-master.yaml
ملف رئيسي بسيط
# db.changelog-master.yaml
databaseChangeLog:
- includeAll:
path: db/changelog/changes/
🎯 الخطوة التالية
لديك الآن كل النظرية! حان الوقت للتطبيق مع الأعمال العملية.
➡️ TP7.1: إعداد Datasource وPool