حفظ البيانات في قاعدة بيانات محلية باستخدام Room   جزء من Android Jetpack.

يمكن للتطبيقات التي تتعامل مع كميات كبيرة من البيانات المنظَّمة الاستفادة بشكل كبير من تخزين هذه البيانات على الجهاز. وأكثر حالات الاستخدام شيوعًا هي تخزين مقتطفات البيانات ذات الصلة مؤقتًا لكي يظل بإمكان المستخدم تصفّح هذا المحتوى بلا إنترنت عندما يتعذّر على الجهاز الوصول إلى الشبكة.

توفّر مكتبة Room للحفاظ على البيانات طبقة تجريدية فوق SQLite للسماح بالوصول إلى قاعدة البيانات بسلاسة مع الاستفادة من الإمكانات الكاملة لـ SQLite. على وجه الخصوص، يقدّم Room المزايا التالية:

  • التحقّق من طلبات لغة الاستعلامات البنيوية (SQL) في وقت الترجمة
  • التعليقات التوضيحية الملائمة التي تقلّل من النصوص النموذجية المتكرّرة والمعرضة للأخطاء
  • مسارات نقل قاعدة البيانات المبسّطة

وبناءً على هذه الاعتبارات، ننصحك بشدة باستخدام Room بدلاً من استخدام واجهات برمجة تطبيقات SQLite مباشرةً.

ضبط إعدادات الجهاز

لاستخدام Room في تطبيقك، أضِف الملحقات التالية إلى ملف build.gradle في تطبيقك.

Kotlin

dependencies {
    val room_version = "2.6.1"

    implementation("androidx.room:room-runtime:$room_version")

    // If this project uses any Kotlin source, use Kotlin Symbol Processing (KSP)
    // See Add the KSP plugin to your project
    ksp("androidx.room:room-compiler:$room_version")

    // If this project only uses Java source, use the Java annotationProcessor
    // No additional plugins are necessary
    annotationProcessor("androidx.room:room-compiler:$room_version")

    // optional - Kotlin Extensions and Coroutines support for Room
    implementation("androidx.room:room-ktx:$room_version")

    // optional - RxJava2 support for Room
    implementation("androidx.room:room-rxjava2:$room_version")

    // optional - RxJava3 support for Room
    implementation("androidx.room:room-rxjava3:$room_version")

    // optional - Guava support for Room, including Optional and ListenableFuture
    implementation("androidx.room:room-guava:$room_version")

    // optional - Test helpers
    testImplementation("androidx.room:room-testing:$room_version")

    // optional - Paging 3 Integration
    implementation("androidx.room:room-paging:$room_version")
}

Groovy

dependencies {
    def room_version = "2.6.1"

    implementation "androidx.room:room-runtime:$room_version"

    // If this project uses any Kotlin source, use Kotlin Symbol Processing (KSP)
    // See KSP Quickstart to add KSP to your build
    ksp "androidx.room:room-compiler:$room_version"

    // If this project only uses Java source, use the Java annotationProcessor
    // No additional plugins are necessary
    annotationProcessor "androidx.room:room-compiler:$room_version"

    // optional - RxJava2 support for Room
    implementation "androidx.room:room-rxjava2:$room_version"

    // optional - RxJava3 support for Room
    implementation "androidx.room:room-rxjava3:$room_version"

    // optional - Guava support for Room, including Optional and ListenableFuture
    implementation "androidx.room:room-guava:$room_version"

    // optional - Test helpers
    testImplementation "androidx.room:room-testing:$room_version"

    // optional - Paging 3 Integration
    implementation "androidx.room:room-paging:$room_version"
}

المكونات الأساسية

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

توفّر فئة قاعدة البيانات لتطبيقك مثيلات لـ DAO المرتبطة بقاعدة البيانات هذه. في المقابل، يمكن للتطبيق استخدام واجهة DAO لاسترداد البيانات من قاعدة بيانات كمثيلات لكائنات عناصر البيانات المرتبطة. يمكن للتطبيق أيضًا استخدام كيانات البيانات المحدّدة لتعديل الصفوف من الجداول المقابلة، أو لإنشاء صفوف جديدة لإدراجها. يوضّح الشكل 1 العلاقة بين المكوّنات المختلفة لـ Room.

الشكل 1. مخطّط بياني لبنية مكتبة Room

مثال على التنفيذ

يقدّم هذا القسم مثالاً على تنفيذ قاعدة بيانات Room باستخدام كينان data واحد وDAO واحد.

عنصر البيانات

تحدِّد التعليمة البرمجية التالية عنصر بيانات User. يمثّل كل مثيل من User صفًا في جدول user في قاعدة بيانات التطبيق.

Kotlin

@Entity
data class User(
    @PrimaryKey val uid: Int,
    @ColumnInfo(name = "first_name") val firstName: String?,
    @ColumnInfo(name = "last_name") val lastName: String?
)

Java

@Entity
public class User {
    @PrimaryKey
    public int uid;

    @ColumnInfo(name = "first_name")
    public String firstName;

    @ColumnInfo(name = "last_name")
    public String lastName;
}

لمزيد من المعلومات عن عناصر البيانات في Room، اطّلِع على مقالة تحديد البيانات باستخدام عناصر Room.

كائن الوصول إلى البيانات (DAO)

تحدِّد التعليمة البرمجية التالية DAO باسم UserDao. يوفّر UserDao methods التي تستخدمها بقية أجزاء التطبيق للتفاعل مع البيانات في جدول user.

Kotlin

@Dao
interface UserDao {
    @Query("SELECT * FROM user")
    fun getAll(): List<User>

    @Query("SELECT * FROM user WHERE uid IN (:userIds)")
    fun loadAllByIds(userIds: IntArray): List<User>

    @Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
           "last_name LIKE :last LIMIT 1")
    fun findByName(first: String, last: String): User

    @Insert
    fun insertAll(vararg users: User)

    @Delete
    fun delete(user: User)
}

Java

@Dao
public interface UserDao {
    @Query("SELECT * FROM user")
    List<User> getAll();

    @Query("SELECT * FROM user WHERE uid IN (:userIds)")
    List<User> loadAllByIds(int[] userIds);

    @Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
           "last_name LIKE :last LIMIT 1")
    User findByName(String first, String last);

    @Insert
    void insertAll(User... users);

    @Delete
    void delete(User user);
}

لمزيد من المعلومات عن DAO، اطّلِع على مقالة الوصول إلى البيانات باستخدام DAO Room.

قاعدة البيانات

تحدِّد التعليمة البرمجية التالية فئة AppDatabase لتخزين قاعدة البيانات. تحدِّد AppDatabase إعدادات قاعدة البيانات وتُستخدَم كنقطة وصول التطبيق الرئيسية إلى البيانات الثابتة. يجب أن تستوفي فئة قاعدة البيانات الشروط التالية:

  • يجب أن يكون الصفّ مُعلَقًا بتعليق توضيحي @Database يحتوي على مصفوفة entities تسرد جميع كيانات البيانات المرتبطة بقاعدة البيانات.
  • يجب أن تكون الفئة فئة مجردة تمديدًا لمحاولة RoomDatabase.
  • بالنسبة إلى كل فئة DAO مرتبطة بقاعدة البيانات، يجب أن تحدِّد فئة قاعدة البيانات طريقة مجردة لا تحتوي على أيّ وسيطات وتُعيد مثيلًا لفئة DAO.

Kotlin

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

Java

@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract UserDao userDao();
}

ملاحظة: إذا كان تطبيقك يعمل في عملية واحدة، عليك اتّباع نمط التصميم للعنصر الفردي عند إنشاء مثيل لعنصر AppDatabase. إنّ كلّ نسخة من RoomDatabase باهظة التكلفة إلى حدٍّ ما، وقلّما تحتاج إلى الوصول إلى نُسخ متعدّدة ضمن عملية واحدة.

إذا كان تطبيقك يعمل في عمليات متعددة، أدرِج enableMultiInstanceInvalidation() في استدعاء enableMultiInstanceInvalidation() لإنشاء قاعدة البيانات. بهذه الطريقة، عندما يكون لديك مثيل من AppDatabase في كل عملية، يمكنك إلغاء صلاحية ملف قاعدة البيانات المشترَكة في عملية واحدة، ويتم نشر هذا الإلغاء تلقائيًا إلى نُسخ AppDatabase في العمليات الأخرى.

الاستخدام

بعد تحديد عنصر البيانات وDAO وعنصر قاعدة البيانات، يمكنك استخدام الرمز البرمجي التالي لإنشاء مثيل لقاعدة البيانات:

Kotlin

val db = Room.databaseBuilder(
            applicationContext,
            AppDatabase::class.java, "database-name"
        ).build()

Java

AppDatabase db = Room.databaseBuilder(getApplicationContext(),
        AppDatabase.class, "database-name").build();

يمكنك بعد ذلك استخدام الطرق المجردة من AppDatabase للحصول على مثيل لـ DAO. بدورك، يمكنك استخدام الطرق من مثيل DAO للتفاعل مع قاعدة البيانات:

Kotlin

val userDao = db.userDao()
val users: List<User> = userDao.getAll()

Java

UserDao userDao = db.userDao();
List<User> users = userDao.getAll();

مصادر إضافية

لمزيد من المعلومات عن Room، يُرجى الاطّلاع على المراجع الإضافية التالية:

نماذج

الدروس التطبيقية حول الترميز

المدوّنات