Library persistensi Room memberikan lapisan abstraksi pada SQLite untuk memungkinkan akses database yang lebih stabil sambil memanfaatkan kemampuan penuh SQLite. Halaman ini berfokus pada penggunaan Room dalam project Kotlin Multiplatform (KMP). Untuk mengetahui informasi selengkapnya tentang cara menggunakan Room, lihat Menyimpan data di database lokal menggunakan Room atau contoh resmi kami.
Menyiapkan dependensi
Untuk menyiapkan Room di project KMP, tambahkan dependensi untuk artefak dalam file
build.gradle.kts
untuk modul KMP Anda.
Tentukan dependensi dalam file libs.versions.toml
:
[versions]
room = "2.7.2"
sqlite = "2.5.2"
ksp = "<kotlinCompatibleKspVersion>"
[libraries]
androidx-sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "sqlite" }
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
# Optional SQLite Wrapper available in version 2.8.0 and higher
androidx-room-sqlite-wrapper = { module = "androidx.room:room-sqlite-wrapper", version.ref = "room" }
[plugins]
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
androidx-room = { id = "androidx.room", version.ref = "room" }
Tambahkan Plugin Gradle Room untuk mengonfigurasi skema Room dan plugin KSP
plugins {
alias(libs.plugins.ksp)
alias(libs.plugins.androidx.room)
}
Tambahkan dependensi runtime Room dan library SQLite yang di-bundle:
commonMain.dependencies {
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.sqlite.bundled)
}
// Optional when using Room SQLite Wrapper
androidMain.dependencies {
implementation(libs.androidx.room.sqlite.wrapper)
}
Tambahkan dependensi KSP ke blok dependencies
root. Perhatikan bahwa Anda
harus menambahkan semua target yang digunakan aplikasi Anda. Untuk mengetahui informasi selengkapnya, lihat KSP dengan
Kotlin Multiplatform.
dependencies {
add("kspAndroid", libs.androidx.room.compiler)
add("kspIosSimulatorArm64", libs.androidx.room.compiler)
add("kspIosX64", libs.androidx.room.compiler)
add("kspIosArm64", libs.androidx.room.compiler)
// Add any other platform target you use in your project, for example kspDesktop
}
Tentukan direktori skema Room. Untuk informasi tambahan, lihat Menetapkan lokasi skema menggunakan Plugin Room Gradle.
room {
schemaDirectory("$projectDir/schemas")
}
Menentukan class database
Anda perlu membuat class database yang dianotasi dengan @Database
bersama dengan DAO dan entity di dalam set sumber umum modul KMP bersama Anda. Menempatkan
class ini di sumber umum akan memungkinkan class tersebut dibagikan di semua platform
target.
// 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("KotlinNoActualForExpect")
expect object AppDatabaseConstructor : RoomDatabaseConstructor<AppDatabase> {
override fun initialize(): AppDatabase
}
Saat Anda mendeklarasikan objek expect
dengan antarmuka
RoomDatabaseConstructor
, compiler Room akan menghasilkan implementasi actual
. Android Studio mungkin mengeluarkan peringatan berikut, yang dapat Anda
nonaktifkan dengan @Suppress("KotlinNoActualForExpect")
:
Expected object 'AppDatabaseConstructor' has no actual declaration in module`
Selanjutnya, tentukan antarmuka DAO baru atau pindahkan antarmuka yang sudah ada ke
commonMain
:
// shared/src/commonMain/kotlin/TodoDao.kt
@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>>
}
Tentukan atau pindahkan entitas Anda ke commonMain
:
// shared/src/commonMain/kotlin/TodoEntity.kt
@Entity
data class TodoEntity(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
val title: String,
val content: String
)
Membuat builder database khusus platform
Anda perlu menentukan pembuat database untuk membuat instance Room di setiap platform. Ini adalah satu-satunya bagian API yang harus ada di set sumber khusus platform karena perbedaan dalam API sistem file.
Android
Di Android, lokasi database biasanya diperoleh melalui API
Context.getDatabasePath()
. Untuk membuat instance database, tentukan
Context
bersama dengan jalur database.
// shared/src/androidMain/kotlin/Database.android.kt
fun getDatabaseBuilder(context: Context): RoomDatabase.Builder<AppDatabase> {
val appContext = context.applicationContext
val dbFile = appContext.getDatabasePath("my_room.db")
return Room.databaseBuilder<AppDatabase>(
context = appContext,
name = dbFile.absolutePath
)
}
iOS
Untuk membuat instance database di iOS, berikan jalur database menggunakan
NSFileManager
, yang biasanya terletak di NSDocumentDirectory
.
// shared/src/iosMain/kotlin/Database.ios.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, berikan jalur database menggunakan API Java atau Kotlin.
// shared/src/jvmMain/kotlin/Database.desktop.kt
fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
val dbFile = File(System.getProperty("java.io.tmpdir"), "my_room.db")
return Room.databaseBuilder<AppDatabase>(
name = dbFile.absolutePath,
)
}
Buat instance database
Setelah mendapatkan RoomDatabase.Builder
dari salah satu konstruktor khusus platform, Anda dapat mengonfigurasi database Room lainnya dalam kode umum bersama dengan instansiasi database sebenarnya.
// shared/src/commonMain/kotlin/Database.kt
fun getRoomDatabase(
builder: RoomDatabase.Builder<AppDatabase>
): AppDatabase {
return builder
.setDriver(BundledSQLiteDriver())
.setQueryCoroutineContext(Dispatchers.IO)
.build()
}
Pilih driver SQLite
Cuplikan kode sebelumnya memanggil fungsi builder setDriver
untuk menentukan driver SQLite yang harus digunakan database Room. Driver ini berbeda-beda berdasarkan platform target. Cuplikan kode sebelumnya menggunakan BundledSQLiteDriver
.
Ini adalah driver yang direkomendasikan yang mencakup SQLite yang dikompilasi dari sumber, yang menyediakan versi SQLite yang paling konsisten dan terbaru di semua platform.
Jika Anda ingin menggunakan SQLite yang disediakan OS, gunakan API setDriver
di set sumber khusus platform yang menentukan driver khusus platform. Lihat
Penerapan driver untuk mengetahui deskripsi penerapan driver yang tersedia. Anda dapat menggunakan salah satu opsi berikut:
AndroidSQLiteDriver
diandroidMain
NativeSQLiteDriver
diiosMain
Untuk menggunakan NativeSQLiteDriver
, Anda harus memberikan opsi linker -lsqlite3
sehingga
aplikasi iOS secara dinamis ditautkan dengan 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")
}
}
}
Menetapkan konteks Coroutine (Opsional)
Objek RoomDatabase
di Android dapat dikonfigurasi secara opsional dengan eksekutor aplikasi bersama menggunakan RoomDatabase.Builder.setQueryExecutor()
untuk melakukan operasi database.
Karena executor tidak kompatibel dengan KMP, API setQueryExecutor()
Room tidak
tersedia di commonMain
. Sebagai gantinya, objek RoomDatabase
harus dikonfigurasi
dengan CoroutineContext
, yang dapat ditetapkan menggunakan
RoomDatabase.Builder.setCoroutineContext()
. Jika tidak ada konteks yang ditetapkan, objek
RoomDatabase
akan menggunakan Dispatchers.IO
secara default.
Minifikasi dan pengaburan
Jika project dikecilkan atau di-obfuscate, Anda harus menyertakan aturan ProGuard berikut agar Room dapat menemukan penerapan yang dihasilkan dari definisi database:
-keep class * extends androidx.room.RoomDatabase { <init>(); }
Bermigrasi ke Multiplatform Kotlin
Room awalnya dikembangkan sebagai library Android dan kemudian dimigrasikan ke KMP dengan fokus pada kompatibilitas API. Versi KMP Room agak berbeda di antara platform dan dari versi khusus Android. Perbedaan tersebut tercantum dan dijelaskan sebagai berikut.
Bermigrasi dari Support SQLite ke Driver SQLite
Penggunaan SupportSQLiteDatabase
dan API lainnya di
androidx.sqlite.db
harus di-refactor dengan SQLite Driver API,
karena API di androidx.sqlite.db
hanya untuk Android (perhatikan
paket yang berbeda dengan paket KMP).
Untuk kompatibilitas mundur, dan selama RoomDatabase
dikonfigurasi dengan SupportSQLiteOpenHelper.Factory
(misalnya, tidak ada SQLiteDriver
yang ditetapkan), Room akan berperilaku dalam 'mode kompatibilitas' di mana API Support SQLite dan SQLite Driver berfungsi seperti yang diharapkan. Hal ini memungkinkan migrasi inkremental sehingga Anda tidak perlu mengonversi semua penggunaan Support SQLite ke Driver SQLite dalam satu perubahan.
Mengonversi Subclass Migrasi
Subkelas migrasi perlu dimigrasikan ke rekanan driver SQLite:
Multiplatform Kotlin
Subkelas migrasi
object Migration_1_2 : Migration(1, 2) {
override fun migrate(connection: SQLiteConnection) {
// …
}
}
Subkelas spesifikasi migrasi otomatis
class AutoMigrationSpec_1_2 : AutoMigrationSpec {
override fun onPostMigrate(connection: SQLiteConnection) {
// …
}
}
Khusus Android
Subkelas migrasi
object Migration_1_2 : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
// …
}
}
Subkelas spesifikasi migrasi otomatis
class AutoMigrationSpec_1_2 : AutoMigrationSpec {
override fun onPostMigrate(db: SupportSQLiteDatabase) {
// …
}
}
Callback database konversi
Callback database perlu dimigrasikan ke rekanan driver SQLite:
Multiplatform Kotlin
object MyRoomCallback : RoomDatabase.Callback() {
override fun onCreate(connection: SQLiteConnection) {
// …
}
override fun onDestructiveMigration(connection: SQLiteConnection) {
// …
}
override fun onOpen(connection: SQLiteConnection) {
// …
}
}
Khusus Android
object MyRoomCallback : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
// …
}
override fun onDestructiveMigration(db: SupportSQLiteDatabase) {
// …
}
override fun onOpen(db: SupportSQLiteDatabase) {
// …
}
}
Mengonversi fungsi DAO @RawQuery
Fungsi yang dianotasi dengan @RawQuery
yang dikompilasi untuk platform non-Android
harus mendeklarasikan parameter jenis RoomRawQuery
, bukan
SupportSQLiteQuery
.
Multiplatform Kotlin
Menentukan kueri mentah
@Dao
interface TodoDao {
@RawQuery
suspend fun getTodos(query: RoomRawQuery): List<TodoEntity>
}
Kemudian, RoomRawQuery
dapat digunakan untuk membuat kueri saat runtime:
suspend fun AppDatabase.getTodosWithLowercaseTitle(title: String): List<TodoEntity> {
val query = RoomRawQuery(
sql = "SELECT * FROM TodoEntity WHERE title = ?",
onBindStatement = {
it.bindText(1, title.lowercase())
}
)
return todoDao().getTodos(query)
}
Khusus Android
Menentukan kueri mentah
@Dao
interface TodoDao {
@RawQuery
suspend fun getTodos(query: SupportSQLiteQuery): List<TodoEntity>
}
Kemudian, SimpleSQLiteQuery
dapat digunakan untuk membuat kueri saat runtime:
suspend fun AndroidOnlyDao.getTodosWithLowercaseTitle(title: String): List<TodoEntity> {
val query = SimpleSQLiteQuery(
query = "SELECT * FROM TodoEntity WHERE title = ?",
bindArgs = arrayOf(title.lowercase())
)
return getTodos(query)
}
Mengonversi fungsi DAO pemblokiran
Room memanfaatkan library kotlinx.coroutines
asinkron yang kaya fitur
yang ditawarkan Kotlin untuk beberapa platform. Untuk fungsi yang optimal, fungsi suspend
diterapkan untuk DAO yang dikompilasi dalam project KMP, dengan pengecualian
DAO yang diterapkan di androidMain
untuk mempertahankan kompatibilitas mundur dengan
kode dasar yang ada. Saat menggunakan Room untuk KMP, semua fungsi DAO yang dikompilasi untuk
platform non-Android harus berupa fungsi suspend
.
Multiplatform Kotlin
Menangguhkan kueri
@Query("SELECT * FROM Todo")
suspend fun getAllTodos(): List<Todo>
Menangguhkan transaksi
@Transaction
suspend fun transaction() { … }
Khusus Android
Memblokir kueri
@Query("SELECT * FROM Todo")
fun getAllTodos(): List<Todo>
Memblokir transaksi
@Transaction
fun blockingTransaction() { … }
Mengonversi jenis reaktif ke Flow
Tidak semua fungsi DAO harus berupa fungsi penangguhan. Fungsi DAO yang menampilkan
jenis reaktif seperti LiveData
atau Flowable
RxJava tidak boleh dikonversi
menjadi fungsi penangguhan. Namun, beberapa jenis, seperti LiveData
, tidak kompatibel dengan KMP. Fungsi DAO dengan jenis nilai yang ditampilkan reaktif harus dimigrasikan ke alur coroutine.
Multiplatform Kotlin
Jenis reaktif Flows
@Query("SELECT * FROM Todo")
fun getTodosFlow(): Flow<List<Todo>>
Khusus Android
Jenis reaktif seperti LiveData
atau Flowable
RxJava
@Query("SELECT * FROM Todo")
fun getTodosLiveData(): LiveData<List<Todo>>
API konversi transaksi
API transaksi database untuk Room KMP dapat membedakan antara transaksi penulisan
(useWriterConnection
) dan pembacaan (useReaderConnection
).
Multiplatform Kotlin
val database: RoomDatabase = …
database.useWriterConnection { transactor ->
transactor.immediateTransaction {
// perform database operations in transaction
}
}
Khusus Android
val database: RoomDatabase = …
database.withTransaction {
// perform database operations in transaction
}
Menulis transaksi
Gunakan transaksi tulis untuk memastikan bahwa beberapa kueri menulis data secara atomik,
sehingga pembaca dapat mengakses data secara konsisten. Anda dapat melakukannya menggunakan
useWriterConnection
dengan salah satu dari tiga jenis transaksi:
immediateTransaction
: Dalam mode Write-Ahead Logging (WAL) (default), jenis transaksi ini mendapatkan kunci saat dimulai, tetapi pembaca dapat terus membaca. Ini adalah pilihan yang lebih disukai untuk sebagian besar kasus.deferredTransaction
: Transaksi tidak akan mendapatkan kunci hingga pernyataan penulisan pertama. Gunakan jenis transaksi ini sebagai pengoptimalan jika Anda tidak yakin apakah operasi tulis akan diperlukan dalam transaksi. Misalnya, jika Anda memulai transaksi untuk menghapus lagu dari playlist hanya dengan nama playlist dan playlist tersebut tidak ada, maka tidak ada operasi penulisan (penghapusan) yang diperlukan.exclusiveTransaction
: Mode ini berperilaku sama denganimmediateTransaction
dalam mode WAL. Dalam mode pencatatan aktivitas lainnya, mode ini mencegah koneksi database lain membaca database saat transaksi sedang berlangsung.
Membaca transaksi
Gunakan transaksi baca untuk membaca dari database beberapa kali secara konsisten. Misalnya, saat Anda memiliki dua kueri terpisah atau lebih dan Anda tidak menggunakan klausa JOIN
. Hanya transaksi yang ditangguhkan yang diizinkan dalam koneksi pembaca. Mencoba
memulai transaksi langsung atau eksklusif dalam koneksi pembaca akan memunculkan
pengecualian, karena ini dianggap sebagai operasi 'tulis'.
val database: RoomDatabase = …
database.useReaderConnection { transactor ->
transactor.deferredTransaction {
// perform database operations in transaction
}
}
Tidak Tersedia di Multiplatform Kotlin
Beberapa API yang tersedia untuk Android tidak tersedia di Kotlin Multiplatform.
Callback Kueri
API berikut untuk mengonfigurasi callback kueri tidak tersedia di umum dan dengan demikian tidak tersedia di platform selain Android.
RoomDatabase.Builder.setQueryCallback
RoomDatabase.QueryCallback
Kami berencana menambahkan dukungan untuk callback kueri di versi Room mendatang.
API untuk mengonfigurasi RoomDatabase
dengan callback kueri
RoomDatabase.Builder.setQueryCallback
beserta antarmuka callback
RoomDatabase.QueryCallback
tidak tersedia di umum dan oleh karena itu tidak tersedia
di platform selain Android.
Menutup Database Otomatis
API untuk mengaktifkan penutupan otomatis setelah waktu tunggu berakhir, RoomDatabase.Builder.setAutoCloseTimeout
, hanya tersedia di Android dan tidak tersedia di platform lain.
Database yang telah dipaketkan
API berikut untuk membuat RoomDatabase
menggunakan database yang ada (yaitu database dalam bentuk paket) tidak tersedia secara umum dan oleh karena itu tidak tersedia di platform selain Android. API tersebut adalah:
RoomDatabase.Builder.createFromAsset
RoomDatabase.Builder.createFromFile
RoomDatabase.Builder.createFromInputStream
RoomDatabase.PrepackagedDatabaseCallback
Kami berencana menambahkan dukungan untuk database dalam bentuk paket di Room versi mendatang.
Pembatalan Multi-Instance
API untuk mengaktifkan pembatalan multi-instance,
RoomDatabase.Builder.enableMultiInstanceInvalidation
hanya tersedia di
Android dan tidak tersedia di platform umum atau lainnya.
Direkomendasikan untuk Anda
- Catatan: teks link ditampilkan saat JavaScript nonaktif
- Codelab Memigrasikan aplikasi yang ada ke Room KMP
- Mulai Menggunakan Codelab KMP
- Menyimpan data di database lokal dengan Room