Room 持續性程式庫透過 SQLite 提供抽象層,可提升資料庫存取的穩固性,同時充分發揮 SQLite 的效用。本頁面著重於在 Kotlin Multiplatform (KMP) 專案中使用 Room。如要進一步瞭解如何使用 Room,請參閱「使用 Room 將資料儲存在本機資料庫」或我們的官方範例。
設定依附元件
目前支援 KMP 的 Room 目前版本為 2.7.0-alpha01 以上版本。
如要在 KMP 專案中設定 Room,請在模組的 build.gradle.kts
檔案中新增構件的依附元件:
androidx.room:room-gradle-plugin
:用於設定 Room 結構定義的 Gradle 外掛程式androidx.room:room-compiler
- 產生程式碼的 KSP 處理器androidx.room:room-runtime
- 程式庫的執行階段部分androidx.sqlite:sqlite-bundled
- (選用) 隨附的 SQLite 程式庫
此外,您還需要設定 Room 的 SQLite 驅動程式。這些驅動程式會因目標平台而異。如需可用的驅動程式實作說明,請參閱驅動程式實作。
如需其他設定資訊,請參閱下列資源:
定義資料庫類別
您需要在共用 KMP 模組的共同來源集中,建立加上 @Database
註解的資料庫類別,以及 DAO 和實體。將這些類別放入通用來源後,所有目標平台都能共用這些類別。
// shared/src/commonMain/kotlin/Database.kt
@Database(entities = [TodoEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun getDao(): TodoDao
}
@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
)
請注意,您可以使用實際 / 預期宣告來建立平台專屬的 Room 實作項目。舉例來說,您可以使用 expect
新增在通用程式碼中定義的平台專用 DAO,然後在平台專屬來源集中指定 actual
定義搭配其他查詢。
建立資料庫建構工具
您必須定義資料庫建構工具,才能在每個平台上將 Room 執行個體化。由於檔案系統 API 有所不同,因此在 API 中,這是唯一必要屬於平台專屬的來源集的一部分。例如,在 Android 中,資料庫位置通常是透過 Context.getDatabasePath()
API 取得;如果是 iOS,則可使用 NSHomeDirectory
取得資料庫位置。
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
如要建立資料庫執行個體,請提供資料庫工廠以及資料庫路徑。資料庫工廠是 lambda 函式,會叫用系統產生的擴充功能函式,該函式名稱為 instantiateImpl
,接收器類型為 KClass<T>
,其中 T
是 @Database
註解類別的類型。
// shared/src/iosMain/kotlin/Database.kt
fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
val dbFilePath = NSHomeDirectory() + "/my_room.db"
return Room.databaseBuilder<AppDatabase>(
name = dbFilePath,
factory = { AppDatabase::class.instantiateImpl() }
)
}
JVM (電腦)
如要建立資料庫執行個體,請僅指定資料庫路徑。您不需要提供資料庫工廠。
// shared/src/commonMain/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 版本。如果您想使用 OS 提供的 SQLite,請在指定平台專用驅動程式的平台專屬來源集中使用 setDriver
API。針對 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")
}
}
}
不同之處
Room 最初是以 Android 程式庫的形式開發,後來遷移至 KMP 並著重 API 相容性。Room 的 KMP 版本在平台和 Android 專用版本之間略有不同。以下將列出及說明這些差異。
封鎖 DAO 函式
使用 Room for KMP 時,針對非 Android 平台編譯的所有 DAO 函式都必須為 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() { // … }
}
借助 Kotlin 為多個平台提供的功能豐富的非同步 kotlinx.coroutines
程式庫,Room 可帶來許多好處。為達到最佳功能,系統會針對在 KMP 專案中編譯的 DAO 強制執行 suspend
函式,但 Android 專屬 DAO 除外,以便維持與現有程式碼集的回溯相容性。
KMP 的功能差異
本節說明 KMP 和 Android 平台版本的 Room 功能差異。
@RawQuery DAO 函式
加註 @RawQuery
的函式若針對非 Android 平台進行編譯,就會產生錯誤。我們會在日後的 Room 版本中新增對 @RawQuery
的支援。
查詢回呼
下列用來設定查詢回呼的 API 並不常見,因此不適用於 Android 以外的平台。
RoomDatabase.Builder.setQueryCallback
RoomDatabase.QueryCallback
我們會在日後的 Room 版本中新增查詢回呼的支援。
使用查詢回呼 RoomDatabase.Builder.setQueryCallback
以及回呼介面 RoomDatabase.QueryCallback
的 API 並不常見,因此不適用於 Android 以外的平台。RoomDatabase
自動關閉資料庫
用來在逾時後啟用自動關閉的 API RoomDatabase.Builder.setAutoCloseTimeout
僅適用於 Android,不適用於其他平台。
預先封裝資料庫
下列 API 無法使用現有資料庫 (例如預先封裝的資料庫) 建立 RoomDatabase
,因此不可用於 Android 以外的其他平台。這些 API 如下:
RoomDatabase.Builder.createFromAsset
RoomDatabase.Builder.createFromFile
RoomDatabase.Builder.createFromInputStream
RoomDatabase.PrepackagedDatabaseCallback
我們會在日後的 Room 版本中新增對預先封裝資料庫的支援。
多執行個體撤銷
用於啟用多執行個體撤銷功能的 API,RoomDatabase.Builder.enableMultiInstanceInvalidation
僅適用於 Android,不可用於通用平台或其他平台。