שמירת נתונים במסד נתונים מקומי באמצעות Room   בארגז הכלים Android Jetpack.

כדאי לנסות בשילוב עם Kotlin Multiplatform
באמצעות Kotlin Multiplatform אפשר לשתף את שכבת מסד הנתונים עם פלטפורמות אחרות. איך מגדירים את Room Database ומשתמשים בו ב-KMP

אפליקציות שמטפלות בכמויות משמעותיות של נתונים מובְנים יכולות להפיק תועלת רבה משמירת הנתונים האלה באופן מקומי. תרחיש השימוש הנפוץ ביותר הוא שמירת חלקים רלוונטיים של נתונים במטמון, כך שאם למכשיר אין גישה לרשת, המשתמש עדיין יכול לעיין בתוכן הזה במצב אופליין.

ספריית Room persistence מספקת שכבת הפשטה מעל SQLite כדי לאפשר גישה שוטפת למסד הנתונים תוך ניצול מלא של היכולות של SQLite. באופן ספציפי, Room מספק את היתרונות הבאים:

  • אימות של שאילתות SQL בזמן ההידור.
  • הערות נוחות שמצמצמות חזרות על קוד סטנדרטי שנוטה לשגיאות.
  • נתיבי העברה יעילים של מסדי נתונים.

בגלל השיקולים האלה, מומלץ מאוד להשתמש ב-Room במקום בממשקי ה-API של SQLite ישירות.

הגדרה

כדי להשתמש ב-Room באפליקציה, מוסיפים את יחסי התלות הבאים לקובץ build.gradle של האפליקציה.

Kotlin

dependencies {
    val room_version = "2.8.4"

    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")
}

מגניב

dependencies {
    def room_version = "2.8.4"

    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 שמשויכים למסד הנתונים הזה. בתמורה, האפליקציה יכולה להשתמש ב-DAO כדי לאחזר נתונים ממסד הנתונים כמופעים של אובייקטים של ישויות נתונים משויכות. האפליקציה יכולה גם להשתמש בישויות הנתונים שהוגדרו כדי לעדכן שורות מהטבלאות המתאימות, או כדי ליצור שורות חדשות להוספה. איור 1 ממחיש את הקשר בין הרכיבים השונים של Room.

איור 1. דיאגרמה של ארכיטקטורת ספריית החדרים.

דוגמה להטמעה

בקטע הזה מוצגת הטמעה לדוגמה של מסד נתונים של Room עם ישות נתונים אחת ו-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 מספק את השיטות שבהן שאר האפליקציה משתמשת כדי לבצע אינטראקציה עם נתונים בטבלה 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();
}

הערה: אם האפליקציה שלכם פועלת בתהליך יחיד, צריך לפעול לפי תבנית העיצוב של singleton כשיוצרים מופע של אובייקט AppDatabase. כל מכונת RoomDatabase יקרה למדי, ובדרך כלל לא צריך גישה לכמה מכונות בתהליך אחד.

אם האפליקציה פועלת בכמה תהליכים, צריך לכלול את ‫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?

דוגמאות

Codelabs

בלוגים