La libreria di persistenza della stanza fornisce un livello di astrazione rispetto a SQLite per consentire per un accesso più affidabile al database sfruttando al contempo tutta la potenza di SQLite. Questo è incentrata sull'utilizzo di Room nei progetti Kotlin Multiplatform (KMP). Per ulteriori informazioni informazioni sull'utilizzo della stanza, consulta Salvare i dati in un database locale utilizzando Room o i nostri campioni ufficiali.
Configurazione delle dipendenze
La versione attuale di Room che supporta KMP è 2.7.0-alpha01 o superiore.
Per configurare la stanza nel tuo progetto KMP, aggiungi le dipendenze per gli artefatti nel
build.gradle.kts
file per il modulo:
androidx.room:room-gradle-plugin
- Il plug-in Gradle per configurare gli schemi delle stanzeandroidx.room:room-compiler
: il processore KSP che genera il codiceandroidx.room:room-runtime
: la parte relativa al runtime della libreriaandroidx.sqlite:sqlite-bundled
: (facoltativo) la libreria SQLite in bundle
Inoltre, devi configurare il driver SQLite di Room. Questi fattori sono diversi in base alla piattaforma di destinazione. Consulta Implementazioni dei driver per le descrizioni delle implementazioni disponibili dei driver.
Per ulteriori informazioni sulla configurazione, consulta le seguenti risorse:
- Imposta la posizione dello schema utilizzando il plug-in Room Gradle.
- Punti di forza principali con Kotlin Multiplatform.
- Aggiunta delle dipendenze di runtime.
definisci le classi di database
Devi creare una classe di database annotata con @Database
insieme ai DAO
ed entità all'interno dell'insieme di origine comune del modulo KMP condiviso. In fase di posizionamento
queste classi nelle origini comuni ne consentiranno la condivisione tra tutti i target
piattaforme di terze parti.
Quando dichiari un oggetto expect
tramite l'interfaccia
RoomDatabaseConstructor
, il compilatore delle stanze genera il actual
implementazioni. Android Studio potrebbe mostrare un avviso
"Expected object 'AppDatabaseConstructor' has no actual declaration in
module"
; puoi eliminare l'avviso con @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>
@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
)
Tieni presente che, se vuoi, puoi utilizzare le dichiarazioni effettive / previste.
per creare implementazioni di stanze specifiche per la piattaforma. Ad esempio, puoi aggiungere
DAO specifico della piattaforma definito nel codice comune utilizzando expect
e quindi
specificare le definizioni di actual
con query aggiuntive in specifiche della piattaforma
set di origini dati.
Creazione del generatore di database
Devi definire un generatore di database per creare un'istanza di Room su ogni piattaforma. Questo
è l'unica parte dell'API che deve trovarsi nell'origine specifica della piattaforma
set di dati a causa delle differenze nelle API del file system. Ad esempio, in Android,
la posizione del database è di solito ottenuta
API Context.getDatabasePath()
, mentre per iOS la posizione del database è
ottenerlo utilizzando NSFileManager
.
Android
Per creare l'istanza del database, specifica un Contesto insieme al database del tuo percorso di apprendimento.
// 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
Per creare l'istanza di database, fornisci un percorso del database utilizzando
NSFileManager
, in genere si trova in 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)
Per creare l'istanza di database, fornisci un percorso del database utilizzando Java o Kotlin su quelle di livello inferiore.
// 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,
)
}
Creazione di un'istanza del database
Dopo aver ottenuto RoomDatabase.Builder
da uno dei provider di servizi
puoi configurare il resto del database della stanza con un codice
insieme all'istanza effettiva del database.
// shared/src/commonMain/kotlin/Database.kt
fun getRoomDatabase(
builder: RoomDatabase.Builder<AppDatabase>
): AppDatabase {
return builder
.addMigrations(MIGRATIONS)
.fallbackToDestructiveMigrationOnDowngrade()
.setDriver(BundledSQLiteDriver())
.setQueryCoroutineContext(Dispatchers.IO)
.build()
}
Selezione di un driver SQLite
Gli snippet di codice precedenti utilizzano BundledSQLiteDriver
. Questo è il
driver consigliato che include SQLite compilato dall'origine, che fornisce
la versione più coerente e aggiornata di SQLite su tutte le piattaforme. Se
Se vuoi usare SQLite fornito dal sistema operativo, usa l'API setDriver
nelle applicazioni
set di origini dati che specificano
un driver specifico per la piattaforma. Per Android, puoi utilizzare
AndroidSQLiteDriver
, mentre per iOS puoi usare NativeSQLiteDriver
. A
usa NativeSQLiteDriver
, devi fornire un'opzione di collegamento in modo che iOS
che si collega in modo dinamico al sistema 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")
}
}
}
Differenze
Room è stata originariamente sviluppata come libreria Android, per poi essere migrata in KMP con particolare attenzione alla compatibilità delle API. La versione KMP di Room è leggermente diversa tra piattaforme e dalla versione specifica per Android. Queste differenze sono elencate e descritte come segue.
Blocco delle funzioni DAO
Quando utilizzi Room per KMP, tutte le funzioni DAO compilate per piattaforme non Android
devono essere funzioni suspend
con l'eccezione dei tipi restituiti reattivi, come
come 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() { // … }
}
La stanza sfrutta i vantaggi della libreria kotlinx.coroutines
asincrona ricca di funzionalità
offerti da Kotlin su più piattaforme. Per una funzionalità ottimale, suspend
vengono applicate per i DAO compilati in un progetto KMP, ad eccezione delle
DAO specifici di Android per mantenere la compatibilità con le versioni precedenti
codebase.
Differenze di funzionalità in KMP
Questa sezione descrive le differenze tra le funzionalità di KMP e la piattaforma Android diverse versioni di Room.
Funzioni DAO @RawQuery
Funzioni annotate con @RawQuery
compilate per piattaforme non Android
è necessario dichiarare un parametro di tipo RoomRawQuery
anziché
SupportSQLiteQuery
.
@Dao
interface TodoDao {
@RawQuery
suspend fun getTodos(query RoomRawQuery): List<TodoEntity>
}
È quindi possibile utilizzare un RoomRawQuery
per creare una query in fase di runtime:
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)
}
Callback della query
Le seguenti API per la configurazione dei callback delle query non sono disponibili in comune e non sono quindi disponibili su piattaforme diverse da Android.
RoomDatabase.Builder.setQueryCallback
RoomDatabase.QueryCallback
Abbiamo intenzione di aggiungere il supporto per il callback delle query in una versione futura di Room.
L'API per configurare un RoomDatabase
con un callback di query
RoomDatabase.Builder.setQueryCallback
insieme all'interfaccia di callback
RoomDatabase.QueryCallback
non sono disponibili in comune e pertanto non sono disponibili
su altre piattaforme diverse da Android.
Database con chiusura automatica
L'API per abilitare la chiusura automatica dopo un timeout,
RoomDatabase.Builder.setAutoCloseTimeout
è disponibile solo su Android ed è
non disponibile in altre piattaforme.
Database predefinito
Le seguenti API per creare un RoomDatabase
utilizzando un database esistente (ad es.
predefiniti) non sono disponibili in comune e pertanto non lo sono
su altre piattaforme diverse da Android. Queste API sono:
RoomDatabase.Builder.createFromAsset
RoomDatabase.Builder.createFromFile
RoomDatabase.Builder.createFromInputStream
RoomDatabase.PrepackagedDatabaseCallback
Abbiamo intenzione di aggiungere il supporto per i database predefiniti in una versione futura di Stanza.
Annullamento della convalida di più istanze
L'API per abilitare l'annullamento della convalida di più istanze,
RoomDatabase.Builder.enableMultiInstanceInvalidation
è disponibile solo su
Android e non è disponibile né nelle piattaforme standard né in altre.