اختبار: المعاملات والترحيلات
اختبر معرفتك بالمعاملات، Flyway وLiquibase.
30 سؤالًا - انقر على كل سؤال لكشف الإجابة.
الأسئلة 1-10: المعاملات
السؤال 1: ما هي المعاملة؟
الإجابة: المعاملة هي وحدة عمل ذرية تضمن ACID:
- الذرية: الكل أو لا شيء
- التناسق: حالة صالحة قبل/بعد
- العزل: معاملات مستقلة
- الدوام: مستمرة بعد الالتزام
السؤال 2: كيف تعلن عن طريقة معاملية؟
الإجابة:
@Service
public class TransferService {
@Transactional
public void transfer(Long from, Long to, BigDecimal amount) {
accountRepository.debit(from, amount);
accountRepository.credit(to, amount);
}
}
السؤال 3: ماذا يحدث إذا تم رفع استثناء في معاملة؟
الإجابة:
- RuntimeException (غير مفحوصة): تراجع تلقائي
- Exception (مفحوصة): لا تراجع افتراضيًا
@Transactional(rollbackFor = Exception.class) // تراجع أيضًا للمفحوصة
public void doWork() throws IOException { }
السؤال 4: كيف تجبر على التراجع لاستثناء مفحوص؟
الإجابة:
@Transactional(rollbackFor = {IOException.class, CustomException.class})
public void process() throws IOException { }
// أو تراجع للجميع
@Transactional(rollbackFor = Exception.class)
السؤال 5: كيف تمنع التراجع لاستثناء RuntimeException؟
الإجابة:
@Transactional(noRollbackFor = BusinessException.class)
public void process() {
// BusinessException لن يسبب تراجع
}
السؤال 6: ما هي مستويات عزل المعاملات؟
الإجابة:
| المستوى | الوصف |
|---|---|
| READ_UNCOMMITTED | يقرأ البيانات غير الملتزمة |
| READ_COMMITTED | يقرأ فقط الملتزمة (افتراضي) |
| REPEATABLE_READ | قراءات مكررة متطابقة |
| SERIALIZABLE | عزل كامل (أبطأ) |
@Transactional(isolation = Isolation.SERIALIZABLE)
السؤال 7: ما هي انتشار المعاملات؟
الإجابة: كيف تتصرف المعاملة عند استدعائها من أخرى:
@Transactional(propagation = Propagation.REQUIRED) // افتراضي
// يستخدم المعاملة الحالية أو ينشئ واحدة جديدة
@Transactional(propagation = Propagation.REQUIRES_NEW)
// ينشئ دائمًا معاملة جديدة
@Transactional(propagation = Propagation.MANDATORY)
// يجب أن يتم استدعاؤها في معاملة موجودة
السؤال 8: متى تستخدم REQUIRES_NEW؟
الإجابة: عندما يجب الالتزام بعملية بشكل مستقل:
@Transactional
public void processOrder(Order order) {
orderService.save(order);
auditService.logAction(order); // يجب أن تستمر حتى لو فشل الطلب
}
@Service
public class AuditService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logAction(Order order) {
// معاملة جديدة مستقلة
}
}
السؤال 9: لماذا لا يعمل @Transactional على طريقة خاصة؟
الإجابة: يستخدم Spring وكلاء AOP الذين يعترضون فقط المكالمات الخارجية. الطرق الخاصة والمكالمات الداخلية لا تمر عبر الوكيل.
public void methodA() {
methodB(); // مكالمة داخلية - لا وكيل!
}
@Transactional
private void methodB() { } // لا يعمل
السؤال 10: كيف تحل مشكلة المكالمات الداخلية؟
الإجابة:
- استخراج في bean آخر
- استخدام
self-injection - استخدام
TransactionTemplate
// الخيار 1: bean آخر
@Service
public class HelperService {
@Transactional
public void doWork() { }
}
// الخيار 2: TransactionTemplate
transactionTemplate.execute(status -> {
// كود المعاملة
return result;
});
الأسئلة 11-20: Flyway
السؤال 11: ما هو Flyway؟
الإجابة: Flyway هو أداة لترحيل قواعد البيانات:
- يقوم بتحديد إصدار مخطط قاعدة البيانات
- يطبق الترحيلات بالترتيب
- يحتفظ بتاريخ الترحيلات
- يدعم SQL وJava
السؤال 12: كيف تسمي ملفات ترحيل Flyway؟
الإجابة: الاتفاقية: V\{version\}__\{description\}.sql
V1__create_users_table.sql
V2__add_email_column.sql
V3__create_orders_table.sql
V1.1__add_index.sql (تحت الإصدار)
الشرطة السفلية المزدوجة __ إلزامية.
السؤال 13: أين تضع ملفات الترحيل؟
الإجابة: افتراضيًا: src/main/resources/db/migration/
src/main/resources/
└── db/
└── migration/
├── V1__create_users.sql
└── V2__add_orders.sql
السؤال 14: أي جدول يستخدمه Flyway للتتبع؟
الإجابة: flyway_schema_history يحتوي على:
- الإصدار
- الوصف
- المجموع الاختباري
- تاريخ التنفيذ
- النجاح/الفشل
السؤال 15: كيف تقوم بتكوين Flyway في Spring Boot؟
الإجابة:
spring:
flyway:
enabled: true
locations: classpath:db/migration
baseline-on-migrate: true
validate-on-migrate: true
السؤال 16: ما هي الترحيلات المتكررة؟
الإجابة: الترحيل الذي يتم إعادة تنفيذه إذا تغير محتواه:
R__create_views.sql (R بدلاً من V)
يستخدم للمشاهد، الإجراءات المخزنة، بيانات المرجع.
السؤال 17: كيف تلغي ترحيل Flyway؟
الإجابة: Flyway Community لا يدعم التراجع. الحلول:
- إنشاء ترحيل جديد يلغي
- استخدام Flyway Teams (مدفوع)
- استعادة نسخة احتياطية
-- V3__undo_v2.sql
ALTER TABLE users DROP COLUMN email;
السؤال 18: كيف تتجاهل ترحيلًا تم تطبيقه يدويًا؟
الإجابة:
spring:
flyway:
baseline-on-migrate: true
baseline-version: 1
أو أمر CLI: flyway baseline
السؤال 19: كيف تنفذ كود Java في ترحيل؟
الإجابة:
public class V3__ComplexMigration implements JavaMigration {
@Override
public void migrate(Context context) throws Exception {
try (Statement stmt = context.getConnection().createStatement()) {
// ترحيل معقد في Java
}
}
}
السؤال 20: كيف تتحقق من صحة الترحيلات دون تنفيذها؟
الإجابة:
# CLI
flyway validate
# أو في التكوين
spring:
flyway:
validate-on-migrate: true
يتحقق من أن المجموعات الاختبارية تتطابق.
الأسئلة 21-30: Liquibase
السؤال 21: ما الفرق بين Flyway وLiquibase؟
الإجابة:
| Flyway | Liquibase |
|---|---|
| SQL أو Java | XML، YAML، JSON، SQL |
| أبسط | أكثر مرونة |
| لا تراجع (Community) | تراجع مدمج |
| تحديد إصدار خطي | مجموعات التغييرات مع معرف |
السؤال 22: كيف تنظم ملف changelog في Liquibase؟
الإجابة:
# db/changelog/db.changelog-master.yaml
databaseChangeLog:
- changeSet:
id: 1
author: dev
changes:
- createTable:
tableName: users
columns:
- column:
name: id
type: bigint
autoIncrement: true
constraints:
primaryKey: true
السؤال 23: كيف يتتبع Liquibase التغييرات المطبقة؟
الإجابة: عبر الجداول:
DATABASECHANGELOG: تاريخ مجموعات التغييراتDATABASECHANGELOGLOCK: قفل لمنع التنفيذ المتوازي
السؤال 24: كيف تحدد تراجعًا في Liquibase؟
الإجابة:
- changeSet:
id: 1
author: dev
changes:
- addColumn:
tableName: users
columns:
- column:
name: phone
type: varchar(20)
rollback:
- dropColumn:
tableName: users
columnName: phone
السؤال 25: كيف تنفذ تراجعًا في Liquibase؟
الإجابة:
# تراجع عن آخر مجموعة تغييرات
liquibase rollbackCount 1
# تراجع حتى علامة
liquibase rollback myTag
# تراجع حتى تاريخ
liquibase rollbackToDate 2024-01-01
السؤال 26: كيف تقوم بتكوين Liquibase في Spring Boot؟
الإجابة:
spring:
liquibase:
enabled: true
change-log: classpath:db/changelog/db.changelog-master.yaml
contexts: dev
السؤال 27: كيف تستخدم السياقات في Liquibase؟
الإجابة: تنفيذ بعض مجموعات التغييرات حسب البيئة:
- changeSet:
id: 1
author: dev
context: dev, test # فقط في dev وtest
changes:
- insert:
tableName: users
columns:
- column: {name: name, value: 'Test User'}
spring:
liquibase:
contexts: dev
السؤال 28: كيف تولد changelog من قاعدة بيانات موجودة؟
الإجابة:
liquibase generateChangeLog \
--changeLogFile=changelog.yaml \
--url=jdbc:postgresql://localhost/mydb \
--username=user \
--password=pass
السؤال 29: كيف تتحقق من صحة changelog دون تنفيذه؟
الإجابة:
liquibase validate
يتحقق من صحة الصياغة والمجموعات الاختبارية.
السؤال 30: كيف تضع علامة على إصدار للتراجع؟
الإجابة:
- changeSet:
id: release-1.0
author: dev
changes:
- tagDatabase:
tag: v1.0.0
# تراجع إلى هذه العلامة
liquibase rollback v1.0.0
النتيجة
| النتيجة | المستوى |
|---|---|
| 27-30 | ممتاز - إتقان المعاملات والترحيلات |
| 21-26 | جيد - بعض المراجعات |
| 15-20 | متوسط - أعد قراءة الفصول |
| أقل من 15 | بحاجة إلى مراجعة |