Library persistensi Room menyediakan lapisan abstraksi atas SQLite untuk memungkinkan untuk akses database yang lebih tangguh sambil memanfaatkan kemampuan penuh SQLite. Ini berfokus pada penggunaan Room di project Kotlin Multiplatform (KMP). Untuk selengkapnya informasi tentang cara menggunakan Room, lihat Menyimpan data di database lokal menggunakan Room atau contoh resmi kami.
Menyiapkan dependensi
Versi Room saat ini yang mendukung KMP adalah 2.7.0-alpha01 atau yang lebih tinggi.
Untuk menyiapkan Room di project KMP Anda, tambahkan dependensi untuk artefak di
File build.gradle.kts
untuk modul Anda:
androidx.room:room-gradle-plugin
- Plugin Gradle untuk mengonfigurasi skema Roomandroidx.room:room-compiler
- Prosesor KSP yang membuat kodeandroidx.room:room-runtime
- Bagian runtime libraryandroidx.sqlite:sqlite-bundled
- (Opsional) Library SQLite yang dipaketkan
Selain itu, Anda perlu mengonfigurasi driver SQLite Room. Penggerak ini berbeda berdasarkan platform target. Lihat Penerapan driver untuk deskripsi implementasi driver yang tersedia.
Untuk informasi penyiapan tambahan, lihat referensi berikut:
- Menetapkan lokasi skema menggunakan Plugin Room Gradle.
- KSP dengan Multiplatform Kotlin.
- Menambahkan dependensi runtime.
Menentukan class database
Anda perlu membuat class database yang dianotasi dengan @Database
beserta DAO
dan entity di dalam set sumber umum
pada modul KMP bersama. Menempatkan
kelas-kelas dalam sumber umum ini akan memungkinkan kelas-kelas itu dibagikan di semua target
di seluruh platform Google.
Saat Anda mendeklarasikan objek expect
dengan antarmuka
RoomDatabaseConstructor
, compiler Room akan menghasilkan actual
implementasi yang tepat. Android Studio mungkin memberikan peringatan
"Expected object 'AppDatabaseConstructor' has no actual declaration in
module"
; Anda dapat menyembunyikan peringatan dengan @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
)
Perhatikan bahwa Anda dapat secara opsional menggunakan deklarasi sebenarnya / yang diharapkan
untuk membuat implementasi Room khusus platform. Misalnya, Anda dapat menambahkan
DAO khusus platform yang ditentukan dalam kode umum menggunakan expect
, lalu
menentukan definisi actual
dengan kueri tambahan dalam khusus platform
set sumber.
Membuat builder database
Anda perlu menentukan builder database untuk membuat instance Room di setiap platform. Ini
adalah satu-satunya bagian API yang harus ada di sumber khusus platform
karena perbedaan dalam API sistem file. Misalnya, di Android,
lokasi database biasanya
diperoleh melalui
Context.getDatabasePath()
API, sedangkan untuk iOS, lokasi database adalah
diperoleh menggunakan NSFileManager
.
Android
Untuk membuat instance database, tentukan Context bersama dengan database .
// 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
Untuk membuat instance database, sediakan jalur database menggunakan metode
NSFileManager
, biasanya berada di 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)
Untuk membuat instance database, sediakan jalur database menggunakan Java atau Kotlin Google Cloud Platform.
// 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,
)
}
Pembuatan instance database
Setelah Anda mendapatkan RoomDatabase.Builder
dari salah satu platform
Anda, Anda dapat mengonfigurasi sisa database Room dalam kode umum
beserta pembuatan instance database yang sebenarnya.
// shared/src/commonMain/kotlin/Database.kt
fun getRoomDatabase(
builder: RoomDatabase.Builder<AppDatabase>
): AppDatabase {
return builder
.addMigrations(MIGRATIONS)
.fallbackToDestructiveMigrationOnDowngrade()
.setDriver(BundledSQLiteDriver())
.setQueryCoroutineContext(Dispatchers.IO)
.build()
}
Memilih SQLiteDriver
Cuplikan kode sebelumnya menggunakan BundledSQLiteDriver
. Ini adalah
driver yang direkomendasikan yang menyertakan kompilasi SQLite dari sumber, yang memberikan
yang paling konsisten dan terbaru dari SQLite
pada semua platform. Jika Anda
ingin menggunakan SQLite yang disediakan OS, gunakan setDriver
API di platform khusus
set sumber yang menentukan {i>driver<i} khusus platform. Untuk Android, Anda dapat menggunakan
AndroidSQLiteDriver
, sedangkan untuk iOS Anda dapat menggunakan NativeSQLiteDriver
. Kepada
menggunakan NativeSQLiteDriver
, Anda harus menyediakan opsi penaut sehingga sistem
secara dinamis menautkan ke SQLite sistem.
// 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")
}
}
}
Perbedaan
Room awalnya dikembangkan sebagai library Android dan kemudian dimigrasikan ke KMP dengan fokus pada kompatibilitas API. Versi KMP Room agak berbeda antar platform dan dari versi khusus Android. Perbedaan ini adalah tercantum dan dijelaskan sebagai berikut.
Memblokir fungsi DAO
Saat menggunakan Room untuk KMP, semua fungsi DAO dikompilasi untuk platform non-Android
harus berupa fungsi suspend
dengan pengecualian untuk jenis nilai yang ditampilkan reaktif, seperti
sebagai 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 memanfaatkan library kotlinx.coroutines
asinkron yang kaya fitur
yang ditawarkan Kotlin untuk beberapa platform. Untuk fungsionalitas yang optimal, suspend
fungsi diberlakukan untuk DAO yang dikompilasi dalam project KMP, dengan pengecualian
DAO khusus Android untuk mempertahankan kompatibilitas mundur dengan
saat ini.
Perbedaan fitur dengan KMP
Bagian ini menjelaskan perbedaan fitur antara KMP dan platform Android Room.
Fungsi DAO @RawQuery
Fungsi yang dianotasi dengan @RawQuery
yang dikompilasi untuk platform non-Android
harus mendeklarasikan parameter jenis RoomRawQuery
, bukan
SupportSQLiteQuery
.
@Dao
interface TodoDao {
@RawQuery
suspend fun getTodos(query RoomRawQuery): List<TodoEntity>
}
Kemudian, RoomRawQuery
dapat digunakan untuk membuat kueri saat 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 Kueri
API berikut untuk mengonfigurasi callback kueri tidak tersedia dan karenanya tidak tersedia di platform selain Android.
RoomDatabase.Builder.setQueryCallback
RoomDatabase.QueryCallback
Kita bermaksud menambahkan dukungan untuk callback kueri di Room versi mendatang.
API untuk mengonfigurasi RoomDatabase
dengan callback kueri
RoomDatabase.Builder.setQueryCallback
bersama dengan antarmuka callback
RoomDatabase.QueryCallback
tidak tersedia bersama sehingga tidak tersedia
di platform lain selain Android.
Menutup Database Otomatis
API untuk mengaktifkan penutupan otomatis setelah waktu tunggu,
RoomDatabase.Builder.setAutoCloseTimeout
, hanya tersedia di Android dan merupakan
tidak tersedia di platform lain.
Database dalam bentuk paket
API berikut untuk membuat RoomDatabase
menggunakan database yang sudah ada (yaitu
dalam bentuk paket) tidak tersedia bersama dan dengan demikian tidak tersedia di
platform lain selain Android. API tersebut adalah:
RoomDatabase.Builder.createFromAsset
RoomDatabase.Builder.createFromFile
RoomDatabase.Builder.createFromInputStream
RoomDatabase.PrepackagedDatabaseCallback
Kami bermaksud untuk menambahkan dukungan untuk {i>database<i} dalam bentuk paket di versi Ruang.
Pembatalan Multi-Instance
API untuk memungkinkan pembatalan validasi multi-instance,
RoomDatabase.Builder.enableMultiInstanceInvalidation
hanya tersedia di
Android dan tidak tersedia di platform umum atau platform lainnya.