Biblioteka trwałości pomieszczeń zapewnia warstwę abstrakcji w standardzie SQLite, aby umożliwić który zapewnia wydajniejszy dostęp do bazy danych przy jednoczesnym wykorzystaniu wszystkich możliwości SQLite. Ten dotyczy używania pokoju w projektach wieloplatformowych Kotlin (KMP). Więcej Informacje o korzystaniu z Sali znajdziesz w artykule Zapisywanie danych w lokalnej bazie danych przy użyciu Sal lub naszych oficjalnych sampli.
Konfigurowanie zależności
Obecna wersja sali, która obsługuje platformę KMP, to Wersja 2.7.0-alfa01 lub nowsza.
Aby skonfigurować Pokój w projekcie KMP, dodaj zależności między artefaktami w
build.gradle.kts
plik dla Twojego modułu:
androidx.room:room-gradle-plugin
– wtyczka Gradle do konfigurowania schematów pokojówandroidx.room:room-compiler
– procesor KSP, który generuje kod.androidx.room:room-runtime
– część biblioteki związana ze środowiskiem wykonawczym.androidx.sqlite:sqlite-bundled
– (opcjonalnie) połączona biblioteka SQLite
Musisz też skonfigurować sterownik SQLite pokoju. Sterowniki różnią się na podstawie platformy docelowej. Zobacz Implementacje sterowników .
Dodatkowe informacje o konfiguracji:
- Skonfiguruj lokalizację schematu za pomocą wtyczki Room Gradle.
- Kluczowe cechy produktu z platformą Kotlin wieloplatformową.
- Dodawanie zależności środowiska wykonawczego
Definiowanie klas bazy danych
Musisz utworzyć klasę bazy danych z adnotacjami @Database
oraz DAO
i encje w wspólnym zbiorze źródłowym udostępnionego modułu KMP. Umieszczanie
te klasy we wspólnych źródłach będą mogły być udostępniane wszystkim użytkownikom docelowym
platform.
Gdy zadeklarujesz obiekt expect
w interfejsie
RoomDatabaseConstructor
, kompilator sal generuje actual
implementacji. Android Studio może wyświetlić ostrzeżenie
"Expected object 'AppDatabaseConstructor' has no actual declaration in module";
you can suppress the warning with
@Pomijanie("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
)
Pamiętaj, że możesz użyć deklaracji rzeczywistych / oczekujących.
aby tworzyć implementacje pokoi na określonych platformach. Na przykład możesz dodać
DAO na określonych platformach, zdefiniowany we wspólnym kodzie za pomocą algorytmu expect
, a następnie:
określ definicje actual
z dodatkowymi zapytaniami w kontekście konkretnej platformy.
zbiorów źródłowych.
Tworzę konstruktor baz danych
Aby utworzyć instancję pokoju na każdej platformie, musisz zdefiniować kreator baz danych. Ten
to jedyna część interfejsu API, która musi znajdować się w źródle na poziomie platformy
ze względu na różnice w interfejsach API systemu plików. Na przykład w Androidzie
lokalizacja bazy danych jest zwykle uzyskiwana za pomocą
Context.getDatabasePath()
API, a w systemie iOS lokalizacja bazy danych to
uzyskać za pomocą NSFileManager
.
Android
Aby utworzyć instancję bazy danych, określ kontekst wraz z bazą danych ścieżki konwersji.
// 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
Aby utworzyć instancję bazy danych, podaj ścieżkę do bazy danych za pomocą atrybutu
NSFileManager
, zwykle w regionie 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 (komputery)
Aby utworzyć instancję bazy danych, podaj ścieżkę bazy danych za pomocą Javy lub 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,
)
}
Tworzenie instancji bazy danych
Po uzyskaniu RoomDatabase.Builder
od jednego z użytkowników platformy
konstruktorami, możesz skonfigurować resztę bazy danych sal we wspólnym kodzie
wraz z rzeczywistym wystąpieniem bazy danych.
// shared/src/commonMain/kotlin/Database.kt
fun getRoomDatabase(
builder: RoomDatabase.Builder<AppDatabase>
): AppDatabase {
return builder
.addMigrations(MIGRATIONS)
.fallbackToDestructiveMigrationOnDowngrade()
.setDriver(BundledSQLiteDriver())
.setQueryCoroutineContext(Dispatchers.IO)
.build()
}
Wybór SQLiteDriver
Poprzednie fragmenty kodu korzystają z tagu BundledSQLiteDriver
. To jest
zalecany sterownik zawierający SQLite skompilowany ze źródła, który udostępnia
najbardziej spójną i aktualną wersję SQLite na wszystkich platformach. Jeśli
Jeśli chcesz korzystać z SQLite dostarczonego przez system operacyjny, użyj interfejsu API setDriver
na odpowiedniej platformie
zbiorów źródłowych, które określają sterownik dla danej platformy. W przypadku Androida możesz użyć
AndroidSQLiteDriver
, a iOS – NativeSQLiteDriver
. Do
używasz NativeSQLiteDriver
, musisz udostępnić opcję tagu łączącego, tak aby system iOS
aplikacja dynamicznie łączy się z systemową bazą 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")
}
}
}
Różnice
Pokój został pierwotnie utworzony jako biblioteka na Androida, a później został przeniesiony do KMP z naciskiem na zgodność z interfejsami API. Wersja pokoju w KMP różni się nieco między platformami i od wersji na Androida. Różnice te są następujące który został opisany poniżej.
Blokowanie funkcji DAO
W przypadku korzystania z usługi Room dla KMP wszystkie funkcje DAO są kompilowane na platformy inne niż Android.
muszą być funkcjami suspend
z wyjątkiem reaktywnych typów zwrotów, takich jak
jako 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() { // … }
}
Dostępne w pokojach funkcje z bogatą w funkcje asynchroniczną biblioteką kotlinx.coroutines
które Kotlin oferuje
na wiele platform. Aby zapewnić optymalne działanie, suspend
funkcje są egzekwowane w przypadku jednostek DAO skompilowanych w projekcie KMP, z wyjątkiem
DAO dla Androida, by zachować zgodność wsteczną z istniejącymi
bazy kodu.
Różnice funkcjonalne z KMP
Z tej sekcji dowiesz się, czym różnią się funkcje KMP i platformy Androida wersji pokoju.
Funkcje DAO @RawQuery
Funkcje z adnotacjami @RawQuery
, które są kompilowane na platformy inne niż Android
będzie musiał zadeklarować parametr typu RoomRawQuery
, a nie
SupportSQLiteQuery
@Dao
interface TodoDao {
@RawQuery
suspend fun getTodos(query RoomRawQuery): List<TodoEntity>
}
Za pomocą RoomRawQuery
można następnie utworzyć zapytanie w czasie działania:
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)
}
Zapytanie o wywołanie zwrotne
Poniższe interfejsy API do konfigurowania wywołań zwrotnych zapytań nie są dostępne wspólnie i dlatego są niedostępne na platformach innych niż Android.
RoomDatabase.Builder.setQueryCallback
RoomDatabase.QueryCallback
W przyszłej wersji pokoju planujemy dodać obsługę wywołań zwrotnych zapytań.
Interfejs API do konfigurowania RoomDatabase
z wywołaniem zwrotnym zapytania
RoomDatabase.Builder.setQueryCallback
wraz z interfejsem wywołania zwrotnego
Listy RoomDatabase.QueryCallback
nie są wspólne, więc są niedostępne
na platformach innych niż Android.
Automatyczne zamykanie bazy danych
interfejs API do włączania automatycznego zamykania po upływie limitu czasu,
Aplikacja RoomDatabase.Builder.setAutoCloseTimeout
jest dostępna tylko na urządzeniach z Androidem i jest
niedostępne na innych platformach.
Baza danych przed pakietami
Poniższe interfejsy API pozwalają utworzyć RoomDatabase
przy użyciu istniejącej bazy danych (np.
gotowa baza danych) nie są wspólne i dlatego nie są dostępne
na platformach innych niż Android. Te interfejsy API to:
RoomDatabase.Builder.createFromAsset
RoomDatabase.Builder.createFromFile
RoomDatabase.Builder.createFromInputStream
RoomDatabase.PrepackagedDatabaseCallback
W przyszłości planujemy dodać obsługę gotowych baz danych. Pokój.
Unieważnienie wielu instancji
interfejs API do włączania unieważniania wielu instancji,
Pole RoomDatabase.Builder.enableMultiInstanceInvalidation
jest dostępne tylko w tych krajach:
Androida i nie jest dostępna na wspólnych platformach.