Room هي مكتبة ORM من Android Jetpack توفر طبقة تجريد فوق SQLite للعمل مع قواعد البيانات المحلية على Android. وفقاً للوثائق الرسمية في Android Developers, 2025، يقوم Room تلقائياً بإنشاء تطبيقات DAO بناءً على التعليقات التوضيحية أثناء وقت الترجمة، مما يلغي حوالي 70% من الكود المتكرر مقارنة بالاستخدام المباشر لـ SQLiteOpenHelper. تقوم المكتبة بالتحقق من استعلامات SQL في وقت الترجمة، مما يسمح باكتشاف الأخطاء النحوية قبل تشغيل التطبيق على الجهاز.
النقاط الرئيسية
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، حيث يتم اكتشاف هذه الأخطاء فقط أثناء وقت التشغيل، غالباً في الإنتاج.
يدعم SQLite خمسة أنواع فقط من البيانات: TEXT و INTEGER و REAL و BLOB و NULL. ومع ذلك، تستخدم Java و Kotlin أنواعاً معقدة: Date و List و Enum وكائنات مخصصة. لتخزينها، يوفر Room آلية TypeConverters — طرق ثابتة تحول النوع المعقد إلى نوع بدائي يفهمه SQLite. على سبيل المثال، يتم تحويل كائن Date إلى Long (طابع زمني)، و List<String> إلى سلسلة JSON عبر Gson أو Moshi.
@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 من ثلاثة مكونات رئيسية: Entity و DAO و Database. يؤدي كل منها دوراً محدداً بدقة ويتم التعليق عليه بالتعليق التوضيحي المناسب. معاً، يشكلون طبقة وصول كاملة للبيانات تعزل منطق الأعمال للتطبيق عن تفاصيل تنفيذ SQLite.
Entity هي فئة بيانات تصف بنية جدول واحد في قاعدة البيانات. كل حقل في الفئة يتوافق مع عمود في الجدول، وكل صف في قاعدة البيانات يتوافق مع مثيل واحد من الفئة. يخبر التعليق التوضيحي @Entity Room بأن الفئة هي جدول. الحقل مع التعليق التوضيحي @PrimaryKey يحدد المفتاح الأساسي، والذي يمكن أن يكون تلقائي الزيادة أو مركباً. يُستخدم @ForeignKey للعلاقات بين الجداول، مما يضمن سلامة البيانات على مستوى قاعدة البيانات.
@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 (Data Access Object) هي واجهة أو فئة مجردة تعلن عن عمليات العمل مع البيانات: الإدراج والقراءة والتحديث والحذف. يتم التعليق على كل عملية بـ @Insert أو @Query أو @Update أو @Delete. يقوم Room تلقائياً بإنشاء تطبيق لهذه الواجهة في وقت الترجمة. التعليق التوضيحي @Query ذو قيمة خاصة — فهو يقبل استعلام SQL كسلسلة ويتحقق من صحته في وقت البناء.
@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 هي فئة مجردة تمد RoomDatabase وتعمل كنقطة دخول إلى قاعدة البيانات. تحتوي على قائمة بجميع الكيانات وتوفر طرقاً مجردة للحصول على DAOs. يتم التعليق على الفئة بـ @Database، التي تحدد إصدار المخطط وقائمة الكيانات. يتم إنشاء مثيل قاعدة البيانات عبر Room.databaseBuilder مع سياق التطبيق واسم الملف وفئة Database.
Room لا يستبدل SQLite، بل يعمل فوقه كطبقة تجريد. تشمل البنية الداخلية معالج التعليقات التوضيحية ومولد الكود ومجمع الاتصالات. في وقت الترجمة، يحلل معالج التعليقات التوضيحية فئات Entity و DAO و Database، ثم ينشئ فئات تنفيذ باللاحقة _Impl. يتم وضع جميع الفئات المنشأة في حزمة البناء ولا تكون مرئية مباشرة للمطور.
توليد الكود في وقت الترجمة هو الآلية المركزية لـ Room. لكل واجهة DAO، يتم إنشاء فئة مع التنفيذ الكامل لجميع الطرق المُعلَّق عليها. يتم التحقق من استعلامات SQL من التعليق التوضيحي @Query للتأكد من صحتها: يطابق المعالج أسماء الأعمدة مع حقول Entity ويتحقق من بناء جملة SQL. إذا تم العثور على خطأ، يتوقف البناء برسالة واضحة. هذا مستحيل عند استخدام SQLiteOpenHelper الخام، حيث تظهر الأخطاء فقط في وقت التشغيل.
تتضمن عملية التوليد ثلاث مراحل. الأولى — التحقق من صحة المخطط: يتحقق المعالج من أن جميع الفئات المدرجة في @Database هي Entities صالحة. الثانية — توليد نص DAO: لكل طريقة، يتم إنشاء تنفيذ باستخدام الكائن الداخلي RoomSQLiteQuery الذي ينفذ الاستعلامات المعدة. الثالثة — توليد فئة Database_Impl، التي تتعامل مع إنشاء وفتح قاعدة البيانات، وكذلك تهيئة جميع كائنات DAO.
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. يحتوي التطبيق على جدول Note واحد مع الحقول id و title و content و timestamp. يمكن للمستخدمين إضافة وعرض وحذف الملاحظات. يتم استخدام coroutines للعمليات غير المتزامنة.
لدمج Room في مشروع Android، أضف التبعيات إلى ملف build.gradle على مستوى الوحدة. يتطلب Room ثلاثة مكونات: مكتبة وقت التشغيل ومعالج التعليقات التوضيحية kapt والدعم الاختياري لـ coroutines. يتم تحديد إصدار المكتبة في متغير room_version لسهولة التحديث. بدءاً من Room 2.4.0، يتم دعم KSP كبديل لـ kapt بسرعات بناء أسرع.
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.
@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 هي آلية لتغيير مخطط قاعدة البيانات عند تحديث تطبيق دون فقدان البيانات الموجودة. عندما يقوم المستخدم بتثبيت إصدار جديد مع Entities معدلة، يكتشف Room عدم تطابق الإصدار وينفذ خطوات الترحيل المحددة. بدون ترحيل، سيتم حذف قاعدة البيانات وإنشاؤها من جديد، مما يؤدي إلى فقدان جميع البيانات المحفوظة من قبل المستخدم.
يتم وصف الترحيل بواسطة فئة Migration، التي تأخذ إصدار قاعدة البيانات الأولي والنهائي. داخل طريقة migrate، يتم تنفيذ استعلام SQL ALTER TABLE أو CREATE TABLE لتغيير المخطط. Room لا يستطيع اكتشاف تغييرات المخطط تلقائياً — يجب على المطور كتابة ترحيل يدوياً لكل تغيير في Entity. بدءاً من Room 2.4.0، تتوفر ميزة autoMigrations التجريبية للتوليد التلقائي للترحيلات.
تتيح ميزة autoMigrations لـ Room توليد الترحيلات تلقائياً بناءً على الاختلافات بين إصدارات Entity. لاستخدامها، يكفي إضافة التعليق التوضيحي @AutoMigration إلى @Database وتمكين تصدير المخطط إلى JSON. يقارن Room مخططات الإصدارات المتجاورة وينشئ استعلامات ALTER اللازمة. ومع ذلك، تدعم autoMigrations فقط التغييرات المتوافقة مع الإصدارات السابقة: إضافة أعمدة وإنشاء فهارس وتغيير الأنواع مع تحويلات متوافقة.
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 يوفر تجريد ORM مع تعليقات توضيحية والتحقق من SQL في وقت الترجمة، بينما يتطلب SQLiteOpenHelper كتابة جميع الاستعلامات يدوياً وإدارة الاتصالات. يقوم Room تلقائياً بإنشاء كود لعمليات CRUD ويتكامل مع مكونات بنية Android، بما في ذلك LiveData و Flow.
Room يدعم جميع الأنواع البدائية في Java: Int و Long و Boolean و Float و Double، بالإضافة إلى String و ByteArray و Date. للأنواع المعقدة مثل List أو Enum، يتم استخدام TypeConverters — طرق تحويل ثابتة تحول الأنواع غير القياسية إلى تنسيقات متوافقة مع SQLite.
نعم، Room يدعم الاستدعاءات المتزامنة بدون coroutines، لكنها تحجب الخيط الذي تعمل عليه. للعمل غير المتزامن، يمكنك استخدام LiveData أو RxJava بدلاً من coroutines. توصي Google باستخدام coroutines كطريقة رئيسية للوصول غير المتزامن إلى البيانات في المشاريع الجديدة.
إذا اكتشف Room عدم تطابق في إصدار قاعدة البيانات ولم يجد ترحيلاً مناسباً، فإنه افتراضياً يرمي IllegalStateException مع وصف للخطأ. يمكن للمطور تجاوز هذا السلوك بطريقة fallbackToDestructiveMigration، التي ستحذف قاعدة البيانات الموجودة وتنشئ قاعدة جديدة، مع فقدان جميع البيانات.
Room يدعم العلاقات من خلال الكائنات المتداخلة مع التعليق التوضيحي @Embedded ومن خلال فئات العلاقة مع التعليق التوضيحي @Relation. للاستعلامات المعقدة التي تتضمن ضم الجداول، يتم استخدام فئات POJO مخصصة، يتم ملء حقولها من نتائج @Query مع عبارات SQL JOIN.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا