Room: ما هي، مكتبة ORM والعمل مع SQLite

المؤلف: IT Sectr نُشر: 2026-03-12 وقت القراءة: 10 دق

Room هي مكتبة ORM من Android Jetpack توفر طبقة تجريد فوق SQLite للعمل مع قواعد البيانات المحلية على Android. وفقاً للوثائق الرسمية في Android Developers, 2025، يقوم Room تلقائياً بإنشاء تطبيقات DAO بناءً على التعليقات التوضيحية أثناء وقت الترجمة، مما يلغي حوالي 70% من الكود المتكرر مقارنة بالاستخدام المباشر لـ SQLiteOpenHelper. تقوم المكتبة بالتحقق من استعلامات SQL في وقت الترجمة، مما يسمح باكتشاف الأخطاء النحوية قبل تشغيل التطبيق على الجهاز.

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

  • Room هي مكتبة ORM من Android Jetpack توفر طبقة تجريد فوق SQLite للتخزين المحلي للبيانات في تطبيقات Android.
  • ثلاثة مكونات رئيسية: Entity (تعريف الجدول)، DAO (عمليات البيانات) و Database (نقطة الدخول إلى قاعدة البيانات).
  • التحقق من استعلامات SQL في وقت الترجمة — ميزة رئيسية تسمح باكتشاف الأخطاء قبل تثبيت التطبيق.
  • دعم مدمج لـ Flow و LiveData و RxJava للمراقبة التفاعلية للتغييرات في قاعدة البيانات.
  • آلية الترحيل تسمح بتحديث مخطط قاعدة البيانات دون فقدان بيانات المستخدم المحفوظة بالفعل.

ما هي مكتبة Room ORM؟

Room هي مكتبة ORM من Android Jetpack أنشأتها Google لتبسيط العمل مع قواعد بيانات SQLite المحلية على منصة Android. توفر تعليقات توضيحية لوصف مخطط البيانات وتنشئ تلقائياً تطبيقات واجهات DAO في وقت الترجمة. على عكس الاستخدام المباشر لـ SQLiteOpenHelper، يريح Room المطور من كتابة كمية كبيرة من الكود المتكرر لإنشاء وفتح وإدارة الاتصال بقاعدة البيانات.

تم تقديم المكتبة في Google I/O 2017 كجزء من مكونات بنية Android. منذ ذلك الحين، أصبح Room المعيار الفعلي للتخزين المحلي للبيانات، متجاوزاً في الشعبية حلولاً مثل GreenDAO و Realm لنظام Android. وفقاً لـ Google، تُستخدم المكتبة في أكثر من 60% من التطبيقات المنشورة على Google Play التي تعمل مع البيانات المحلية على الجهاز.

الميزة الرئيسية هي التحقق من استعلامات SQL في وقت الترجمة باستخدام معالج التعليقات التوضيحية. إذا ارتكب المطور خطأً في أمر SQL، على سبيل المثال، تحديد اسم عمود غير موجود، سيفشل البناء مع خطأ قبل تثبيت التطبيق. هذا يختلف جوهرياً عن نهج SQLiteOpenHelper، حيث يتم اكتشاف هذه الأخطاء فقط أثناء وقت التشغيل، غالباً في الإنتاج.

TypeConverters للأنواع غير القياسية

يدعم SQLite خمسة أنواع فقط من البيانات: TEXT و INTEGER و REAL و BLOB و NULL. ومع ذلك، تستخدم Java و Kotlin أنواعاً معقدة: Date و List و Enum وكائنات مخصصة. لتخزينها، يوفر Room آلية TypeConverters — طرق ثابتة تحول النوع المعقد إلى نوع بدائي يفهمه SQLite. على سبيل المثال، يتم تحويل كائن Date إلى Long (طابع زمني)، و List<String> إلى سلسلة JSON عبر Gson أو Moshi.

kotlin
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

val db = Room
    .databaseBuilder(context, AppDatabase::class.java, "app-db")
    .build()

لإعلان محول، يكفي إضافة التعليق التوضيحي @TypeConverter إلى طريقة ثابتة وتحديد فئة المحول في التعليق التوضيحي @TypeConverters على مستوى قاعدة البيانات. يطبق Room المحول تلقائياً عند قراءة وكتابة النوع المقابل في كل استعلام SQL دون الحاجة إلى استدعاء طرق التحويل يدوياً.

هندسة Room: ثلاثة مكونات رئيسية

يتكون Room من ثلاثة مكونات رئيسية: Entity و DAO و Database. يؤدي كل منها دوراً محدداً بدقة ويتم التعليق عليه بالتعليق التوضيحي المناسب. معاً، يشكلون طبقة وصول كاملة للبيانات تعزل منطق الأعمال للتطبيق عن تفاصيل تنفيذ SQLite.

Entity — جدول قاعدة البيانات

Entity هي فئة بيانات تصف بنية جدول واحد في قاعدة البيانات. كل حقل في الفئة يتوافق مع عمود في الجدول، وكل صف في قاعدة البيانات يتوافق مع مثيل واحد من الفئة. يخبر التعليق التوضيحي @Entity Room بأن الفئة هي جدول. الحقل مع التعليق التوضيحي @PrimaryKey يحدد المفتاح الأساسي، والذي يمكن أن يكون تلقائي الزيادة أو مركباً. يُستخدم @ForeignKey للعلاقات بين الجداول، مما يضمن سلامة البيانات على مستوى قاعدة البيانات.

kotlin
@Entity(tableName = "users")
data class User(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    @ColumnInfo(name = "full_name")
    val name: String,
    val age: Int,
    val email: String
)

DAO — عمليات البيانات

DAO (Data Access Object) هي واجهة أو فئة مجردة تعلن عن عمليات العمل مع البيانات: الإدراج والقراءة والتحديث والحذف. يتم التعليق على كل عملية بـ @Insert أو @Query أو @Update أو @Delete. يقوم Room تلقائياً بإنشاء تطبيق لهذه الواجهة في وقت الترجمة. التعليق التوضيحي @Query ذو قيمة خاصة — فهو يقبل استعلام SQL كسلسلة ويتحقق من صحته في وقت البناء.

kotlin
@Dao
interface UserDao {
    @Insert
    suspend fun insert(user: User): Long

    @Query("SELECT * FROM users WHERE id = :userId")
    suspend fun getUserById(userId: Int): User?

    @Query("SELECT * FROM users")
    fun getAllUsers(): Flow<List<User>>

    @Delete
    suspend fun delete(user: User)
}

Database — نقطة الدخول

Database هي فئة مجردة تمد RoomDatabase وتعمل كنقطة دخول إلى قاعدة البيانات. تحتوي على قائمة بجميع الكيانات وتوفر طرقاً مجردة للحصول على DAOs. يتم التعليق على الفئة بـ @Database، التي تحدد إصدار المخطط وقائمة الكيانات. يتم إنشاء مثيل قاعدة البيانات عبر Room.databaseBuilder مع سياق التطبيق واسم الملف وفئة Database.

كيف يعمل Room مع SQLite تحت الغطاء

Room لا يستبدل SQLite، بل يعمل فوقه كطبقة تجريد. تشمل البنية الداخلية معالج التعليقات التوضيحية ومولد الكود ومجمع الاتصالات. في وقت الترجمة، يحلل معالج التعليقات التوضيحية فئات Entity و DAO و Database، ثم ينشئ فئات تنفيذ باللاحقة _Impl. يتم وضع جميع الفئات المنشأة في حزمة البناء ولا تكون مرئية مباشرة للمطور.

توليد الكود في وقت الترجمة هو الآلية المركزية لـ Room. لكل واجهة DAO، يتم إنشاء فئة مع التنفيذ الكامل لجميع الطرق المُعلَّق عليها. يتم التحقق من استعلامات SQL من التعليق التوضيحي @Query للتأكد من صحتها: يطابق المعالج أسماء الأعمدة مع حقول Entity ويتحقق من بناء جملة SQL. إذا تم العثور على خطأ، يتوقف البناء برسالة واضحة. هذا مستحيل عند استخدام SQLiteOpenHelper الخام، حيث تظهر الأخطاء فقط في وقت التشغيل.

توليد الكود في وقت الترجمة

تتضمن عملية التوليد ثلاث مراحل. الأولى — التحقق من صحة المخطط: يتحقق المعالج من أن جميع الفئات المدرجة في @Database هي Entities صالحة. الثانية — توليد نص DAO: لكل طريقة، يتم إنشاء تنفيذ باستخدام الكائن الداخلي RoomSQLiteQuery الذي ينفذ الاستعلامات المعدة. الثالثة — توليد فئة Database_Impl، التي تتعامل مع إنشاء وفتح قاعدة البيانات، وكذلك تهيئة جميع كائنات DAO.

kotlin
class UserDao_Impl(private val __db: RoomDatabase) : UserDao {
    private val __insertionAdapter = __db
        .createInsertionAdapter(User::class, 0)

    override suspend fun insert(user: User): Long {
        __db.assertNotSuspendingTransaction()
        return __db.runInTransaction {
            __insertionAdapter.insertAndReturnId(user)
        }
    }
}

Room لا ينشئ مجموعة خيوط منفصلة لعمليات قاعدة البيانات. افتراضياً، يتم تنفيذ الاستعلامات في الخيط المستدعي مع قيد واحد: القراءة والكتابة تحجبان الخيط. للعمل غير المتزامن، يتكامل Room مع coroutines في Kotlin عبر وظائف suspend، ومع LiveData عبر قيم الإرجاع، ومع Flow عبر أغلفة تفاعلية. هذا يمنح المطور المرونة في اختيار الحل المعماري لمهمة محددة.

مثال على استخدام Room في تطبيق Android

لنلق نظرة على مثال عملي لإنشاء تطبيق لتدوين الملاحظات باستخدام Room. يحتوي التطبيق على جدول Note واحد مع الحقول id و title و content و timestamp. يمكن للمستخدمين إضافة وعرض وحذف الملاحظات. يتم استخدام coroutines للعمليات غير المتزامنة.

إعداد تبعيات Gradle

لدمج Room في مشروع Android، أضف التبعيات إلى ملف build.gradle على مستوى الوحدة. يتطلب Room ثلاثة مكونات: مكتبة وقت التشغيل ومعالج التعليقات التوضيحية kapt والدعم الاختياري لـ coroutines. يتم تحديد إصدار المكتبة في متغير room_version لسهولة التحديث. بدءاً من Room 2.4.0، يتم دعم KSP كبديل لـ kapt بسرعات بناء أسرع.

groovy
dependencies {
    def room_version = "2.6.1"
    implementation "androidx.room:room-runtime:$room_version"
    kapt "androidx.room:room-compiler:$room_version"
    implementation "androidx.room:room-ktx:$room_version"
    // اختياري: اختبار
    testImplementation "androidx.room:room-testing:$room_version"
}

بعد إعداد التبعيات، قم بإنشاء ثلاثة ملفات: Entity Note وواجهة NoteDao وفئة AppDatabase. Entity Note تحتوي على حقول مع التعليقات التوضيحية @PrimaryKey و @ColumnInfo. يوفر DAO طرقاً للإدراج والحصول على القائمة والحذف. تربط Database بين Entity و DAO من خلال التعليق التوضيحي @Database.

kotlin
@Entity(tableName = "notes")
data class Note(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    val title: String,
    val content: String,
    @ColumnInfo(name = "created_at")
    val timestamp: Long = System.currentTimeMillis()
)

@Dao
interface NoteDao {
    @Insert
    suspend fun insert(note: Note)

    @Query("SELECT * FROM notes ORDER BY created_at DESC")
    fun getAllNotes(): Flow<List<Note>>

    @Delete
    suspend fun delete(note: Note)
}

يتم إعلان ملف AppDatabase كفئة مجردة تمد RoomDatabase. يحدد التعليق التوضيحي @Database جميع Entities للإصدار الحالي ورقم إصدار المخطط. للحصول على مثيل، يتم استخدام نمط singleton عبر طريقة build من Room.databaseBuilder مع سياق التطبيق. يمنع التخزين المؤقت لمثيل قاعدة البيانات الإنشاءات المتعددة التي قد تؤدي إلى تسرب الذاكرة.

ترحيلات قاعدة البيانات في Room

الترحيلات في Room هي آلية لتغيير مخطط قاعدة البيانات عند تحديث تطبيق دون فقدان البيانات الموجودة. عندما يقوم المستخدم بتثبيت إصدار جديد مع Entities معدلة، يكتشف Room عدم تطابق الإصدار وينفذ خطوات الترحيل المحددة. بدون ترحيل، سيتم حذف قاعدة البيانات وإنشاؤها من جديد، مما يؤدي إلى فقدان جميع البيانات المحفوظة من قبل المستخدم.

يتم وصف الترحيل بواسطة فئة Migration، التي تأخذ إصدار قاعدة البيانات الأولي والنهائي. داخل طريقة migrate، يتم تنفيذ استعلام SQL ALTER TABLE أو CREATE TABLE لتغيير المخطط. Room لا يستطيع اكتشاف تغييرات المخطط تلقائياً — يجب على المطور كتابة ترحيل يدوياً لكل تغيير في Entity. بدءاً من Room 2.4.0، تتوفر ميزة autoMigrations التجريبية للتوليد التلقائي للترحيلات.

الترحيلات التلقائية مع autoMigrations

تتيح ميزة autoMigrations لـ Room توليد الترحيلات تلقائياً بناءً على الاختلافات بين إصدارات Entity. لاستخدامها، يكفي إضافة التعليق التوضيحي @AutoMigration إلى @Database وتمكين تصدير المخطط إلى JSON. يقارن Room مخططات الإصدارات المتجاورة وينشئ استعلامات ALTER اللازمة. ومع ذلك، تدعم autoMigrations فقط التغييرات المتوافقة مع الإصدارات السابقة: إضافة أعمدة وإنشاء فهارس وتغيير الأنواع مع تحويلات متوافقة.

kotlin
val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(db: SupportSQLiteDatabase) {
        db.execSQL(
            "ALTER TABLE users ADD COLUMN phone TEXT"
        )
    }
}

val db = Room
    .databaseBuilder(context, AppDatabase::class.java, "app-db")
    .addMigrations(MIGRATION_1_2)
    .build()

للتغييرات المعقدة، مثل إعادة تسمية الأعمدة أو دمج الجداول، يلزم ترحيل يدوي باستخدام جداول وسيطة. السيناريو النموذجي: إنشاء جدول مؤقت بالمخطط القديم، نسخ البيانات من الجدول القديم إلى الجديد مع التحويلات، حذف الجدول القديم وإعادة تسمية المؤقت. يضمن Room تنفيذ جميع الترحيلات في معاملة واحدة، وفي حالة حدوث خطأ، يتم التراجع عن التغييرات بالكامل.

الأسئلة الشائعة

ما الفرق بين Room و SQLiteOpenHelper؟

Room يوفر تجريد ORM مع تعليقات توضيحية والتحقق من SQL في وقت الترجمة، بينما يتطلب SQLiteOpenHelper كتابة جميع الاستعلامات يدوياً وإدارة الاتصالات. يقوم Room تلقائياً بإنشاء كود لعمليات CRUD ويتكامل مع مكونات بنية Android، بما في ذلك LiveData و Flow.

ما أنواع البيانات التي يدعمها Room؟

Room يدعم جميع الأنواع البدائية في Java: Int و Long و Boolean و Float و Double، بالإضافة إلى String و ByteArray و Date. للأنواع المعقدة مثل List أو Enum، يتم استخدام TypeConverters — طرق تحويل ثابتة تحول الأنواع غير القياسية إلى تنسيقات متوافقة مع SQLite.

هل يمكن استخدام Room بدون coroutines؟

نعم، Room يدعم الاستدعاءات المتزامنة بدون coroutines، لكنها تحجب الخيط الذي تعمل عليه. للعمل غير المتزامن، يمكنك استخدام LiveData أو RxJava بدلاً من coroutines. توصي Google باستخدام coroutines كطريقة رئيسية للوصول غير المتزامن إلى البيانات في المشاريع الجديدة.

ماذا يحدث في حالة عدم وجود ترحيل؟

إذا اكتشف Room عدم تطابق في إصدار قاعدة البيانات ولم يجد ترحيلاً مناسباً، فإنه افتراضياً يرمي IllegalStateException مع وصف للخطأ. يمكن للمطور تجاوز هذا السلوك بطريقة fallbackToDestructiveMigration، التي ستحذف قاعدة البيانات الموجودة وتنشئ قاعدة جديدة، مع فقدان جميع البيانات.

كيف يتعامل Room مع العلاقات بين الجداول؟

Room يدعم العلاقات من خلال الكائنات المتداخلة مع التعليق التوضيحي @Embedded ومن خلال فئات العلاقة مع التعليق التوضيحي @Relation. للاستعلامات المعقدة التي تتضمن ضم الجداول، يتم استخدام فئات POJO مخصصة، يتم ملء حقولها من نتائج @Query مع عبارات SQL JOIN.

الخلاصة

  • Room هي مكتبة ORM من Android Jetpack تنشئ طبقة تجريد فوق SQLite للتخزين المريح للبيانات على الجهاز.
  • تعتمد البنية على ثلاثة مكونات: Entity (مخطط الجدول)، DAO (العمليات) و Database (نقطة الدخول).
  • التحقق من استعلامات SQL في وقت الترجمة هو الميزة الرئيسية، مما يلغي أخطاء وقت التشغيل في الاستعلامات.
  • الدعم المدمج لـ Flow و LiveData و RxJava يتيح بناء بنى تفاعلية مع تحديثات تلقائية لواجهة المستخدم عند تغيير البيانات.
  • الترحيلات في Room تضمن تحديثات سلسة لمخطط قاعدة البيانات دون فقدان معلومات المستخدم المحفوظة.
  • تتكامل المكتبة مع coroutines في Kotlin عبر وظائف suspend، مما يبسط العمل غير المتزامن مع البيانات.
  • للمشاريع الجديدة، Room هو الحل الموصى به رسمياً من Google للتخزين المحلي للبيانات على Android.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا