Die Room-Persistenzbibliothek bietet eine Abstraktionsschicht über SQLite, für einen stabileren Datenbankzugriff bei gleichzeitiger Nutzung der vollen Leistung von SQLite. Dieses konzentriert sich auf die Verwendung von Room in KMP-Projekten (Kotlin Multiplatform). Weitere Informationen Informationen zur Verwendung von Room finden Sie unter Daten in einer lokalen Datenbank mit Room speichern. oder unsere offiziellen Beispiele.
Abhängigkeiten einrichten
Die aktuelle Version von Room, die KMP unterstützt, ist 2.7.0-alpha01 oder höher.
Fügen Sie die Abhängigkeiten für die Artefakte in das Feld
build.gradle.kts
-Datei für Ihr Modul:
androidx.room:room-gradle-plugin
: Das Gradle-Plug-in zum Konfigurieren von Raumschemasandroidx.room:room-compiler
: Der KSP-Prozessor, der den Code generiertandroidx.room:room-runtime
– Laufzeitteil der Bibliothekandroidx.sqlite:sqlite-bundled
(optional): die gebündelte SQLite-Bibliothek
Außerdem müssen Sie den SQLite-Treiber von Room konfigurieren. Diese Faktoren unterscheiden sich basierend auf der Zielplattform. Weitere Informationen finden Sie unter Treiberimplementierungen finden Sie Beschreibungen der verfügbaren Treiberimplementierungen.
Weitere Informationen zur Einrichtung finden Sie hier:
- Schemastandort mit Room-Gradle-Plug-in festlegen
- KSP mit Kotlin Multiplatform
- Laufzeitabhängigkeiten hinzufügen
Datenbankklassen definieren
Sie müssen eine Datenbankklasse erstellen, die mit @Database
und DAOs annotiert ist
Entitäten innerhalb des gemeinsamen Quellsatzes
des freigegebenen KMP-Moduls. Wird platziert
Wenn diese Klassen in gemeinsamen Quellen verwendet werden, können sie in allen Zielen
Plattformen.
Wenn Sie ein expect
-Objekt mit der -Schnittstelle deklarieren
RoomDatabaseConstructor
, generiert der Room-Compiler den actual
.
Implementierungen. Android Studio gibt möglicherweise eine Warnung aus
"Expected object 'AppDatabaseConstructor' has no actual declaration in
module"
; können Sie die Warnung mit @Suppress("NO_ACTUAL_FOR_EXPECT")
unterdrücken.
// 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
)
Optional können Sie tatsächliche / erwartete Deklarationen verwenden.
um plattformspezifische Raumimplementierungen zu erstellen. Sie können beispielsweise ein
plattformspezifischen DAO, der im gemeinsamen Code mit expect
definiert ist, und dann
die actual
-Definitionen mit zusätzlichen Abfragen in plattformspezifischen
Quellsätzen.
Datenbank-Builder erstellen
Sie müssen einen Datenbank-Builder definieren, um Room auf jeder Plattform zu instanziieren. Dieses
ist der einzige Teil der API, der in einer plattformspezifischen Quelle vorhanden sein muss
aufgrund der Unterschiede zwischen Dateisystem-APIs festgelegt. In Android können Sie z. B.
Datenbankstandort wird normalerweise über die
Context.getDatabasePath()
-API. Für iOS ist der Datenbankspeicherort
können mithilfe von NSFileManager
abgerufen werden.
Android
Geben Sie zum Erstellen der Datenbankinstanz einen Context zusammen mit der Datenbank an. Pfad.
// 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
Geben Sie zum Erstellen der Datenbankinstanz mithilfe der Methode
NSFileManager
, befindet sich normalerweise in der 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 (Desktop)
Geben Sie zum Erstellen der Datenbankinstanz einen Datenbankpfad mit Java oder Kotlin an APIs
// 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,
)
}
Datenbankinstanziierung
Wenn Sie die RoomDatabase.Builder
von einem der plattformspezifischen
Konstruktoren können Sie den Rest der Raumdatenbank in gemeinsamem Code konfigurieren.
sowie die eigentliche Datenbankinstanziierung.
// 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 auswählen
In den vorherigen Code-Snippets wird BundledSQLiteDriver
verwendet. Dies ist die
empfohlenen Treiber mit SQLite, der aus der Quelle kompiliert wurde und den
die konsistenteste und aktuellste Version von SQLite auf allen Plattformen. Wenn Sie
das vom Betriebssystem bereitgestellte SQLite nutzen möchten, verwenden Sie die setDriver
API in plattformspezifischen
Quellsätze, die einen plattformspezifischen Treiber angeben. Für Android können Sie
AndroidSQLiteDriver
und für iOS können Sie die NativeSQLiteDriver
verwenden. Bis
NativeSQLiteDriver
verwenden, müssen Sie eine Verknüpfungsoption angeben,
die App dynamisch mit dem SQLite-System verknüpft.
// 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")
}
}
}
Unterschiede
Room wurde ursprünglich als Android-Bibliothek entwickelt und später zu KMP mit Schwerpunkt auf API-Kompatibilität Die KMP-Version von Room unterscheidet sich etwas zwischen den Plattformen und der Android-spezifischen Version. Diese Unterschiede sind aufgeführt und wie folgt beschrieben.
DAO-Funktionen blockieren
Bei Verwendung von Room for KMP alle DAO-Funktionen, die für Nicht-Android-Plattformen kompiliert wurden
müssen suspend
-Funktionen sein, mit Ausnahme von reaktiven Rückgabetypen wie
als 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() { // … }
}
Raumvorteile der asynchronen kotlinx.coroutines
-Bibliothek mit vielen Funktionen
die Kotlin für
mehrere Plattformen bietet. Für eine optimale Funktionalität sollten Sie suspend
werden für DAOs erzwungen, die in einem KMP-Projekt zusammengestellt wurden, mit Ausnahme von
Android-spezifische DAOs zur Aufrechterhaltung der Abwärtskompatibilität mit den
Codebasis.
Funktionsunterschiede zu KMP
In diesem Abschnitt wird beschrieben, wie sich die Funktionen der KMP- und der Android-Plattform unterscheiden. von Room.
@RawQuery-DAO-Funktionen
Mit @RawQuery
annotierte Funktionen, die für Nicht-Android-Plattformen kompiliert wurden
muss ein Parameter vom Typ RoomRawQuery
und nicht
SupportSQLiteQuery
@Dao
interface TodoDao {
@RawQuery
suspend fun getTodos(query RoomRawQuery): List<TodoEntity>
}
Mit einem RoomRawQuery
kann dann zur Laufzeit eine Abfrage erstellt werden:
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)
}
Abfragerückruf
Die folgenden APIs zum Konfigurieren von Abfragerückrufen sind gemeinsam nicht verfügbar und sind daher auf anderen Plattformen als Android nicht verfügbar.
RoomDatabase.Builder.setQueryCallback
RoomDatabase.QueryCallback
Wir planen, in einer zukünftigen Version von Room Unterstützung für den Rückruf von Anfragen hinzuzufügen.
Die API zum Konfigurieren eines RoomDatabase
mit einem Abfrage-Callback
RoomDatabase.Builder.setQueryCallback
und die Callback-Oberfläche
RoomDatabase.QueryCallback
sind nicht gemeinsam und daher nicht verfügbar
auf anderen Plattformen als Android nutzen.
Datenbank automatisch schließen
Die API zum automatischen Schließen nach einer Zeitüberschreitung,
RoomDatabase.Builder.setAutoCloseTimeout
ist nur auf Android-Geräten verfügbar und
die auf anderen Plattformen
nicht verfügbar sind.
Pre-Package Database
Mit den folgenden APIs erstellen Sie ein RoomDatabase
-Objekt mithilfe einer vorhandenen Datenbank (z.B.
vorkonfigurierte Datenbank) sind nicht gemeinsam und daher auch nicht
anderen Plattformen als Android. Diese APIs sind:
RoomDatabase.Builder.createFromAsset
RoomDatabase.Builder.createFromFile
RoomDatabase.Builder.createFromInputStream
RoomDatabase.PrepackagedDatabaseCallback
Wir beabsichtigen, in einer zukünftigen Version von Raum.
Entwertung mehrerer Instanzen
Die API zum Aktivieren der Entwertung mehrerer Instanzen,
RoomDatabase.Builder.enableMultiInstanceInvalidation
ist nur verfügbar auf
Android und ist weder auf gängigen noch auf anderen Plattformen verfügbar.