Room (Kotlin Multiplatform)

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 Raumschemas
  • androidx.room:room-compiler: Der KSP-Prozessor, der den Code generiert
  • androidx.room:room-runtime – Laufzeitteil der Bibliothek
  • androidx.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:

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.