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 驱动程序。这些推动因素有所不同 基于目标平台。请参阅 驱动程序实现 了解可用驱动程序实现的说明。
如需了解更多设置信息,请参阅以下内容:
。定义数据库类
您需要创建一个带有 @Database
注解的数据库类以及 DAO
共享 KMP 模块的公共源代码集中内的实体和实体。放置
这些常用来源中的类将允许在所有目标之间共享
平台。
当您使用接口声明 expect
对象时,
RoomDatabaseConstructor
,Room 编译器会生成 actual
实现。Android Studio 可能会发出警告
"Expected object 'AppDatabaseConstructor' has no actual declaration in
module"
;您可以使用 @Suppress("NO_ACTUAL_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> {
override fun initialize(): 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
)
请注意,您可以选择使用实际 / 预期声明
来创建针对具体平台的 Room 实现。例如,您可以添加
特定于平台的 DAO,使用 expect
在通用代码中定义,然后
使用平台专用的其他查询指定 actual
定义
源代码集。
创建数据库构建器
您需要定义一个数据库构建器,以便在每个平台上实例化 Room。本次
是 API 中唯一需要包含在平台专用源代码中的部分
因为文件系统 API 存在差异。例如,在 Android 中
数据库位置通常通过
Context.getDatabasePath()
API,但对于 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,请使用特定于平台的 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 库开发的,后来迁移到了 侧重于 API 兼容性的 KMP。KMP 版本的 Room 有所不同 以及从 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() { // … }
}
Room 受益于功能丰富的异步 kotlinx.coroutines
库
Kotlin 为多个平台提供的各种功能为实现最佳功能,suspend
函数会强制执行在 KMP 项目中编译的 DAO,但
Android 专用 DAO,旨在保持与现有 DAO 的向后兼容性
代码库。
与 KMP 的功能差异
本部分介绍了 KMP 和 Android 平台在功能上有何不同 版本。
@RawQuery DAO 函数
针对非 Android 平台编译且带有 @RawQuery
注解的函数
需要声明一个类型为 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
我们打算在未来的 Room 版本中添加对查询回调的支持。
用于通过查询回调配置 RoomDatabase
的 API
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
我们打算在 Room。
多实例失效
用于启用多实例失效的 API
RoomDatabase.Builder.enableMultiInstanceInvalidation
仅适用于
Android 平台,不适用于通用平台或其他平台。