انتقل إلى المحتوى الرئيسي

الفصل 3: ترحيل المخطط باستخدام Flyway

مقدمة​

هل سبق لك أن عشت هذا الموقف؟

"انتظر، أي نص SQL قمت بتشغيله في الإنتاج؟"
"الذي يحتوي على الأعمدة الجديدة... أعتقد؟"
"لكن كان هناك 3 إصدارات! V1، V2 أو V2_final_final؟"
💀

Flyway يحل هذه المشكلة عن طريق إصدار مخطط قاعدة البيانات الخاصة بك كما تقوم بإصدار الكود الخاص بك.


🎯 أهداف الفصل​

في نهاية هذا الفصل، ستعرف:

  1. ✅ لماذا Flyway ضروري للإنتاج
  2. ✅ تثبيت وتكوين Flyway مع Spring Boot
  3. ✅ إنشاء نصوص ترحيل
  4. ✅ فهم اتفاقية التسمية
  5. ✅ إدارة الترحيلات المتكررة
  6. ✅ حل المشاكل الشائعة

1. لماذا إصدار المخطط؟​

المشكلة​

مشاكل:

  • لا نعرف أي نص تم تشغيله
  • لا يوجد سجل للتاريخ
  • خطر نسيان ترحيل
  • من المستحيل إعادة إنتاج البيئة

الحل: Flyway​

فوائد:

  • ✅ سجل كامل للترحيلات
  • ✅ تنفيذ تلقائي عند بدء التشغيل
  • ✅ متطابق في جميع البيئات
  • ✅ من المستحيل نسيان ترحيل

2. التثبيت​

تبعية Maven​

<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>

<!-- لـ PostgreSQL، أضف أيضًا: -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-postgresql</artifactId>
</dependency>

مع Spring Boot​

هذا كل شيء! يقوم Spring Boot بتكوين Flyway تلقائيًا.

# application.properties
spring.flyway.enabled=true # الافتراضي: true إذا كان Flyway في classpath

3. هيكل الملفات​

الموقع الافتراضي​

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

اتفاقية التسمية​

V{version}__{description}.sql
│ │ │
│ │ └── الوصف (underscores = مسافات)
│ └── رقم الإصدار (1، 2، 2.1، إلخ.)
└── البادئة "V" (ترحيل بالإصدار)

أمثلة صحيحة:

V1__create_users.sql
V2__add_email_to_users.sql
V2.1__add_phone_number.sql
V10__create_products.sql
V100__final_cleanup.sql

أمثلة غير صحيحة:

V1_create_users.sql       ❌ underscore واحد فقط
v1__create_users.sql ❌ حرف صغير
1__create_users.sql ❌ لا توجد بادئة V
V1__create users.sql ❌ مسافات في الاسم

4. إنشاء الترحيلات​

الترحيل 1: إنشاء جدول​

-- 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
);

الترحيل 2: إضافة عمود​

-- V2__add_email_to_users.sql

ALTER TABLE users
ADD COLUMN email VARCHAR(100) UNIQUE;

الترحيل 3: إنشاء جدول آخر مع 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);

الترحيل 4: إدخال بيانات​

-- 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. كيف يعمل Flyway​

جدول flyway_schema_history​

يقوم Flyway بإنشاء جدول تلقائيًا لتتبع الترحيلات:

SELECT * FROM flyway_schema_history;
installed_rankversiondescriptiontypescriptchecksuminstalled_onexecution_timesuccess
11create users tableSQLV1__create_users_table.sql-1234562025-01-15 10:0045true
22add email to usersSQLV2__add_email_to_users.sql7890122025-01-15 10:0112true
33create orders tableSQLV3__create_orders_table.sql3456782025-01-16 09:0067true

تدفق التنفيذ​


6. تكوين Spring Boot​

التكوين الأساسي​

# application.properties

# تفعيل Flyway
spring.flyway.enabled=true

# موقع النصوص (الافتراضي: classpath:db/migration)
spring.flyway.locations=classpath:db/migration

# Baseline (لـ BDD الموجودة)
spring.flyway.baseline-on-migrate=false
spring.flyway.baseline-version=1

التكوين المتقدم​

# المخطط المستهدف
spring.flyway.schemas=public

# التنظيف (خطر في الإنتاج!)
spring.flyway.clean-disabled=true

# التحقق
spring.flyway.validate-on-migrate=true

# استبدال العناصر النائبة
spring.flyway.placeholder-replacement=true
spring.flyway.placeholders.table_prefix=app_

تكوين YAML​

spring:
flyway:
enabled: true
locations: classpath:db/migration
baseline-on-migrate: false
clean-disabled: true
validate-on-migrate: true

7. الترحيلات المتكررة​

حالات الاستخدام​

بعض الكائنات يجب أن تكون معاد إنشاؤها عند كل تغيير:

  • العروض (CREATE OR REPLACE VIEW)
  • الإجراءات المخزنة
  • الوظائف
  • بيانات مرجعية

الاتفاقية​

R__{description}.sql
│
└── البادئة "R" (متكرر)

مثال​

-- 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';

السلوك:

  • يقوم Flyway بحساب checksum للملف
  • إذا تغير الملف → يعاد تنفيذه
  • ينفذ بعد جميع الترحيلات بالإصدار

8. إدارة البيئات​

هيكل متعدد البيئات​

src/main/resources/
├── db/
│ └── migration/
│ ├── V1__create_tables.sql # جميع البيئات
│ ├── V2__add_columns.sql
│ └── V3__create_indexes.sql
│
├── db-dev/
│ └── migration/
│ └── V1000__dev_test_data.sql # بيانات اختبار (dev فقط)
│
└── db-prod/
└── migration/
└── V1001__prod_config.sql # تكوين الإنتاج

التكوين حسب ملف التعريف​

# 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. التكامل مع JPA/Hibernate​

ترتيب التنفيذ​

# مهم: Flyway ينفذ قبل Hibernate
spring.jpa.hibernate.ddl-auto=validate # لا توليد مخطط

# أو لـ dev:
spring.jpa.hibernate.ddl-auto=none # Flyway يدير كل شيء

سير العمل الموصى به​

تلميح

في التطوير: استخدم ddl-auto=validate للتحقق من أن الكيانات الخاصة بك تتطابق مع المخطط.

في الإنتاج: استخدم ddl-auto=none ودع Flyway يدير كل شيء.


10. أوامر Flyway​

عبر 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>
# عرض الترحيلات المطبقة
mvn flyway:info

# تطبيق الترحيلات
mvn flyway:migrate

# التحقق من الترحيلات
mvn flyway:validate

# إصلاح السجل (احذر!)
mvn flyway:repair

# تنظيف قاعدة البيانات (خطر!)
mvn flyway:clean

مثال على مخرجات 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. المشاكل الشائعة والحلول​

❌ عدم تطابق Checksum​

FlywayException: Validate failed: Migration checksum mismatch for migration version 2
-> Applied to database : -123456
-> Resolved locally : 789012

السبب: تم تعديل ملف الترحيل بعد تنفيذه.

الحلول:

  1. لا تقم أبدًا بتعديل ترحيل تم تنفيذه بالفعل!
  2. إذا كان في dev: mvn flyway:repair
  3. إنشاء ترحيل جديد للتصحيح

❌ لم يتم العثور على الترحيل​

FlywayException: Detected resolved migration not applied to database: 3

السبب: يوجد ترحيل في قاعدة البيانات ولكن ليس في الملفات.

الحل: استعادة ملف الترحيل المفقود.

❌ ترحيل خارج الترتيب​

FlywayException: Detected resolved migration not applied to database: 2
(Applied migrations are: 1, 3)

السبب: تم إضافة ترحيل V2 بعد V3.

الحل:

spring.flyway.out-of-order=true  # السماح بالترحيلات خارج الترتيب

❌ ترحيل فاشل​

FlywayException: Migration V4__add_index.sql failed

الحل:

  1. تصحيح SQL في الملف
  2. تنفيذ mvn flyway:repair (يضع علامة كحل)
  3. تنفيذ mvn flyway:migrate

12. أفضل الممارسات​

✅ القواعد الذهبية​

1. لا تقم أبدًا بتعديل ترحيل تم تنفيذه بالفعل
→ إنشاء ترحيل جديد للتصحيح

2. لا تقم أبدًا بحذف ترحيل تم تنفيذه
→ يجب أن يبقى السجل متسقًا

3. دائمًا اختبر الترحيلات على بيئة اختبار
→ قبل النشر في الإنتاج

4. دائمًا احتفظ بنسخة احتياطية قبل الترحيل في الإنتاج
→ في حالة الطوارئ

5. اجعل الترحيلات صغيرة ومركزة
→ ترحيل واحد = تعديل منطقي واحد

✅ تسمية واضحة​

-- ✅ جيد
V1__create_users_table.sql
V2__add_email_column_to_users.sql
V3__create_orders_with_foreign_keys.sql

-- ❌ سيء
V1__changes.sql
V2__update.sql
V3__fix.sql

✅ نصوص قابلة للتكرار​

-- ✅ جيد: تحقق قبل الإنشاء
CREATE TABLE IF NOT EXISTS users (...);

-- ✅ جيد: تحقق قبل إضافة عمود (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. ملخص​

النقاط الرئيسية​

المفهومالوصف
Flywayأداة ترحيل المخطط بالإصدار
V{n}__بادئة للترحيلات بالإصدار
R__بادئة للترحيلات المتكررة
flyway_schema_historyجدول يتتبع الترحيلات
checksumتجزئة الملف لاكتشاف التعديلات

سير العمل النموذجي​

1. إنشاء ملف V{n}__description.sql
2. كتابة SQL
3. بدء التطبيق (أو mvn flyway:migrate)
4. Flyway ينفذ تلقائيًا
5. التحقق باستخدام mvn flyway:info

التكوين الأدنى​

# Spring Boot + Flyway
spring.flyway.enabled=true
spring.flyway.locations=classpath:db/migration
spring.jpa.hibernate.ddl-auto=validate

🎯 الخطوة التالية​

Flyway هو الأداة الأكثر شعبية، ولكن Liquibase يقدم بديلاً بميزات مختلفة. دعونا نرى متى نختار أحدهما أو الآخر.

➡️ الفصل 4: الترحيل باستخدام Liquibase


⬅️ العودة إلى الوحدة 7