ספריית מתמידים של חדר מספקת שכבת הפשטה מעל SQLite שמאפשרת לגישה חזקה יותר למסדי נתונים תוך ניצול כל העוצמה של SQLite. הזה להתמקד בשימוש ב'חדר' בפרויקטים של Kotlin Multiplatform (KMP). לקבלת מידע נוסף מידע על השימוש ב'חדר', ראו שמירת נתונים במסד נתונים מקומי באמצעות Room או הדוגמאות הרשמיות שלנו.
הגדרת יחסי תלות
הגרסה הנוכחית של Room שתומכת ב-KMP היא גרסה 2.7.0-alpha01 ואילך.
כדי להגדיר חדר בפרויקט KMP, צריך להוסיף את יחסי התלות של פריטי המידע שנוצרו בתהליך הפיתוח (Artifact)
קובץ build.gradle.kts
למודול שלך:
androidx.room:room-gradle-plugin
– הפלאגין של Gradle להגדרת סכימות של חדריםandroidx.room:room-compiler
- מעבד ה-KSP שיוצר קודandroidx.room:room-runtime
– החלק של סביבת זמן הריצה בספרייהandroidx.sqlite:sqlite-bundled
– (אופציונלי) ספריית SQLite בחבילה
בנוסף, צריך להגדיר את מנהל התקן SQLite של Room. מנהלי ההתקנים האלה שונים בהתאם לפלטפורמת היעד. צפייה הטמעות של מנהלי ההתקנים לתיאורים של ההטמעות הזמינות של מנהלי התקנים.
למידע נוסף על ההגדרות, תוכלו להיעזר במאמרים הבאים:
- הגדרת מיקום הסכימה באמצעות הפלאגין Room Gradle
- KSP עם Kotlin Multiplatform.
- הוספת יחסי תלות של סביבת זמן ריצה
הגדרת המחלקות של מסד הנתונים
עליך ליצור מחלקה של מסד נתונים עם הערות עם @Database
יחד עם DAOs
וישויות בתוך קבוצת המקור המשותף של מודול ה-KMP המשותף. מיקום
המחלקות האלה במקורות משותפים יאפשרו שיתוף שלהן בין כל סוגי הטירגוט
פלטפורמות שונות.
כשמצהירים על אובייקט expect
עם הממשק
RoomDatabaseConstructor
, מהדר החדרים יוצר את actual
בפועל. יכול להיות שתוצג אזהרה ב-Android Studio
"Expected object 'AppDatabaseConstructor' has no actual declaration in module";
you can suppress the warning with
@Suppress("NO_CODE_FOR_EXPECT")`.
// shared/src/commonMain/kotlin/Database.kt
@Database(entities = [TodoEntity::class], version = 1)
@ConstructedBy(AppDatabaseConstructor::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun getDao(): TodoDao
}
// The Room compiler generates the `actual` implementations.
@Suppress("NO_ACTUAL_FOR_EXPECT")
expect object AppDatabaseConstructor : RoomDatabaseConstructor<AppDatabase>
@Dao
interface TodoDao {
@Insert
suspend fun insert(item: TodoEntity)
@Query("SELECT count(*) FROM TodoEntity")
suspend fun count(): Int
@Query("SELECT * FROM TodoEntity")
fun getAllAsFlow(): Flow<List<TodoEntity>>
}
@Entity
data class TodoEntity(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
val title: String,
val content: String
)
לתשומת ליבכם: אפשר להשתמש בהצהרות בפועל והצהרות צפויות
כדי ליצור הטמעות של חדרים ספציפיות לפלטפורמה. לדוגמה, אפשר להוסיף
DAO ספציפי לפלטפורמה, שמוגדר בקוד משותף באמצעות expect
ואז
לציין את ההגדרות של actual
בשאילתות נוספות ספציפיות לפלטפורמה
בקבוצות המקור.
יצירת הכלי לבניית מסדי נתונים
צריך להגדיר כלי לבניית מסדי נתונים כדי ליצור מופע של חדר בכל פלטפורמה. הזה
הוא החלק היחיד ב-API שנדרש להיות במקור ספציפי לפלטפורמה
בגלל ההבדלים בממשקי ה-API של מערכת הקבצים. לדוגמה, ב-Android,
בדרך כלל המיקום של מסד הנתונים מתקבל
ב-API של Context.getDatabasePath()
. ב-iOS, המיקום של מסד הנתונים הוא
באמצעות NSFileManager
.
Android
כדי ליצור את מכונת מסד הנתונים, צריך לציין Context יחד עם מסד הנתונים נתיב.
// shared/src/androidMain/kotlin/Database.kt
fun getDatabaseBuilder(ctx: Context): RoomDatabase.Builder<AppDatabase> {
val appContext = ctx.applicationContext
val dbFile = appContext.getDatabasePath("my_room.db")
return Room.databaseBuilder<AppDatabase>(
context = appContext,
name = dbFile.absolutePath
)
}
iOS
כדי ליצור את מכונת מסד הנתונים, צריך לציין נתיב למסד הנתונים באמצעות הפונקציה
NSFileManager
, בדרך כלל נמצא בNSDocumentDirectory
.
// shared/src/iosMain/kotlin/Database.kt
fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
val dbFilePath = documentDirectory() + "/my_room.db"
return Room.databaseBuilder<AppDatabase>(
name = dbFilePath,
)
}
private fun documentDirectory(): String {
val documentDirectory = NSFileManager.defaultManager.URLForDirectory(
directory = NSDocumentDirectory,
inDomain = NSUserDomainMask,
appropriateForURL = null,
create = false,
error = null,
)
return requireNotNull(documentDirectory?.path)
}
JVM (מחשב)
כדי ליצור את מכונת מסד הנתונים, צריך לספק נתיב למסד נתונים באמצעות Java או Kotlin ממשקי API.
// shared/src/jvmMain/kotlin/Database.kt
fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
val dbFile = File(System.getProperty("java.io.tmpdir"), "my_room.db")
return Room.databaseBuilder<AppDatabase>(
name = dbFile.absolutePath,
)
}
יצירת מודלים של מסד נתונים
לאחר קבלת RoomDatabase.Builder
מאחת מהפלטפורמות הספציפיות
אפשר להגדיר את שאר מסד הנתונים Room בקוד משותף
וגם את יצירת המופע של מסד הנתונים.
// shared/src/commonMain/kotlin/Database.kt
fun getRoomDatabase(
builder: RoomDatabase.Builder<AppDatabase>
): AppDatabase {
return builder
.addMigrations(MIGRATIONS)
.fallbackToDestructiveMigrationOnDowngrade()
.setDriver(BundledSQLiteDriver())
.setQueryCoroutineContext(Dispatchers.IO)
.build()
}
בחירת SQLiteDriver
קטעי הקוד הקודמים משתמשים בBundledSQLiteDriver
. כאן
של מנהל התקן המומלץ שכולל את SQLite שהורכב ממקור, שמספק את
את הגרסה העקבית והעדכנית ביותר של SQLite בכל הפלטפורמות. אם
שרוצים להשתמש ב-SQLite שסופק על ידי מערכת ההפעלה, צריך להשתמש ב-API של setDriver
קבוצות מקור שמציינות מנהל התקן ספציפי לפלטפורמה. ב-Android, אפשר להשתמש ב
AndroidSQLiteDriver
. במכשירי iOS אפשר להשתמש בNativeSQLiteDriver
. שפת תרגום
להשתמש ב-NativeSQLiteDriver
, עליכם לספק אפשרות לקישור כדי ש-iOS
מקשר באופן דינמי למערכת SQLite.
// shared/build.gradle.kts
kotlin {
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "TodoApp"
isStatic = true
// Required when using NativeSQLiteDriver
linkerOpts.add("-lsqlite3")
}
}
}
הבדלים
החדר פותח במקור כספריית Android, ומאוחר יותר עבר אל KMP תוך התמקדות בתאימות ל-API. גרסת ה-KMP של Room שונה במידה מסוימת בין פלטפורמות ומהגרסה הספציפית ל-Android. ההבדלים האלה ומפורטים בהמשך.
חסימת פונקציות DAO
כשמשתמשים ב- Room for KMP, כל פונקציות DAO עברו הידור לפלטפורמות שהן לא Android
צריכות להיות פונקציות suspend
, למעט סוגי החזרה תגובתיים,
בתור Flow
.
// shared/src/commonMain/kotlin/MultiplatformDao.kt
@Dao
interface MultiplatformDao {
// ERROR: Blocking function not valid for non-Android targets
@Query("SELECT * FROM Entity")
fun blockingQuery(): List<Entity>
// OK
@Query("SELECT * FROM Entity")
suspend fun query(): List<Entity>
// OK
@Query("SELECT * FROM Entity")
fun queryFlow(): Flow<List<Entity>>
// ERROR: Blocking function not valid for non-Android targets
@Transaction
fun blockingTransaction() { // … }
// OK
@Transaction
suspend fun transaction() { // … }
}
ההטבות של החדר הן ספריית kotlinx.coroutines
האסינכרונית עם מגוון התכונות והאפשרויות
ש-Kotlin מציעה לכמה פלטפורמות. לפונקציונליות אופטימלית, suspend
נאכפות עבור DAOs שנאספו בפרויקט KMP, מלבד
DAOs ספציפיים ל-Android לשמירה על תאימות לאחור עם המודלים הקיימים
ב-codebase.
הבדלים בתכונות ב-KMP
בקטע הזה מתוארים ההבדלים בתכונות בין KMP לבין פלטפורמת Android של Room.
פונקציות DAO של @RawQuery
פונקציות עם הערות עם @RawQuery
שעברו הידור לפלטפורמות שאינן Android
תצטרכו להצהיר על פרמטר מסוג RoomRawQuery
במקום
SupportSQLiteQuery
.
@Dao
interface TodoDao {
@RawQuery
suspend fun getTodos(query RoomRawQuery): List<TodoEntity>
}
לאחר מכן אפשר להשתמש ב-RoomRawQuery
כדי ליצור שאילתה בזמן הריצה:
suspend fun getTodosWithLowercaseTitle(title: String): List<TodoEntity> {
val query = RoomRawQuery(
sql = "SELECT * FROM TodoEntity WHERE title = ?"
onBindStatement = {
it.bindText(1, title.lowercase())
}
)
return todosDao.getTodos(query)
}
קריאה חוזרת לשאילתה
ממשקי ה-API הבאים להגדרת קריאה חוזרת של שאילתות אינם זמינים במשותף ולכן הן לא זמינות בפלטפורמות אחרות מלבד Android.
RoomDatabase.Builder.setQueryCallback
RoomDatabase.QueryCallback
אנחנו מתכוונים להוסיף תמיכה בקריאה חוזרת (callback) של שאילתה בגרסה עתידית של Room.
ה-API להגדרה של RoomDatabase
עם קריאה חוזרת (callback) של שאילתה
RoomDatabase.Builder.setQueryCallback
וגם ממשק הקריאה החוזרת
RoomDatabase.QueryCallback
אינם זמינים במשותף ולכן אינם זמינים
בפלטפורמות אחרות מלבד Android.
סגירה אוטומטית של מסד הנתונים
ה-API שמאפשר סגירה אוטומטית אחרי שתם הזמן הקצוב לתפוגה.
RoomDatabase.Builder.setAutoCloseTimeout
, זמין רק ב-Android ו
לא זמין בפלטפורמות אחרות.
מסד נתונים לפני האריזה
ממשקי ה-API הבאים ליצירת RoomDatabase
באמצעות מסד נתונים קיים (כלומר
מסד נתונים מראש ארוז מראש) אינם זמינים במשותף ולכן אינם זמינים
בפלטפורמות אחרות מלבד Android. אלו הם ממשקי ה-API:
RoomDatabase.Builder.createFromAsset
RoomDatabase.Builder.createFromFile
RoomDatabase.Builder.createFromInputStream
RoomDatabase.PrepackagedDatabaseCallback
אנחנו מתכוונים להוסיף תמיכה במסדי נתונים ארוזים מראש בגרסה עתידית של חדר.
ביטול תוקף של מכונות מרובות
את ה-API להפעלת ביטול תוקף של מספר מכונות,
הניסוי RoomDatabase.Builder.enableMultiInstanceInvalidation
זמין רק ב-YouTube
Android ולא זמין בפלטפורמות משותפות או בפלטפורמות אחרות.