Room kalıcılık kitaplığı, SQLite'in tüm gücünden yararlanırken daha sağlam veritabanı erişimi sağlamak için SQLite üzerinde bir soyutlama katmanı sağlar. Bu sayfada, Room'un Kotlin Multiplatform (KMP) projelerinde kullanımına odaklanılmaktadır. Room'u kullanma hakkında daha fazla bilgi için Room'u kullanarak verileri yerel bir veritabanına kaydetme başlıklı makaleyi veya resmi örneklerimizi inceleyin.
Bağımlılıkları ayarlama
Room'un KMP'yi destekleyen mevcut sürümü 2.7.0-alpha01 veya daha yenidir.
KMP projenizde Room'u ayarlamak için modülünüzün build.gradle.kts
dosyasına yapıların bağımlılıklarını ekleyin:
androidx.room:room-gradle-plugin
: Room şemalarını yapılandırmak için Gradle eklentisiandroidx.room:room-compiler
: Kod oluşturan KSP işlemcisiandroidx.room:room-runtime
: Kitaplığın çalışma zamanı bölümüandroidx.sqlite:sqlite-bundled
: (İsteğe bağlı) Paketlenmiş SQLite kitaplığı
Ayrıca Room'un SQLite sürücüsünü yapılandırmanız gerekir. Bu sürücüler, hedef platforma göre farklılık gösterir. Mevcut sürücü uygulamalarının açıklamaları için Sürücü uygulamaları bölümüne bakın.
Kurulumla ilgili daha fazla bilgi için aşağıdakilere bakın:
- Room Gradle eklentisini kullanarak şema konumunu ayarlayın.
- Kotlin Multiplatform ile KSP.
- Çalışma zamanı bağımlılıkları ekleme.
Veritabanı sınıflarını tanımlama
Paylaşılan KMP modülünüzün ortak kaynak kümesinde DAO'lar ve varlıklarla birlikte @Database
ile ek açıklamalı bir veritabanı sınıfı oluşturmanız gerekir. Bu sınıfları ortak kaynaklara yerleştirmek, tüm hedef platformlarda paylaşılmasına olanak tanır.
RoomDatabaseConstructor
arayüzünü kullanarak bir expect
nesnesi tanımladığınızda Room derleyicisi actual
uygulamalarını oluşturur. Android Studio bir uyarı "Expected object 'AppDatabaseConstructor' has no actual declaration in
module"
yayınlayabilir. Uyarıyı @Suppress("NO_ACTUAL_FOR_EXPECT")
ile bastırabilirsiniz.
// 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
)
Platforma özel Room uygulamaları oluşturmak için isteğe bağlı olarak actual / expect bildirimlerini kullanabileceğinizi unutmayın. Örneğin, expect
kullanılarak ortak kodda tanımlanan platforma özgü bir DAO ekleyebilir ve ardından platforma özgü kaynak kümelerinde ek sorgularla actual
tanımlarını belirtebilirsiniz.
Veritabanı oluşturucuyu oluşturma
Room sınıfını her platformda örneklemek için bir veritabanı oluşturucu tanımlamanız gerekir. Bu, dosya sistemi API'lerindeki farklılıklar nedeniyle API'nin platforma özel kaynak kümelerinde olması gereken tek parçasıdır. Örneğin, Android'de veritabanı konumu genellikle Context.getDatabasePath()
API aracılığıyla elde edilirken iOS'te veritabanı konumu NSFileManager
kullanılarak elde edilir.
Android
Veritabanı örneğini oluşturmak için veritabanı yolu ile birlikte bir bağlam belirtin.
// 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
Veritabanı örneğini oluşturmak için genellikle NSDocumentDirectory
içinde bulunan NSFileManager
kullanarak bir veritabanı yolu sağlayın.
// 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 (Masaüstü)
Veritabanı örneğini oluşturmak için Java veya Kotlin API'lerini kullanarak bir veritabanı yolu sağlayın.
// 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,
)
}
Kodu küçültme ve karartma
Proje küçültülmüş veya karartılmışsa Room'un veritabanı tanımının oluşturulmuş uygulamasını bulabilmesi için aşağıdaki ProGuard kuralı eklenmelidir:
-keep class * extends androidx.room.RoomDatabase { <init>(); }
Veritabanı oluşturma
Platforma özel kuruculardan birinden RoomDatabase.Builder
'yi aldıktan sonra, Room veritabanının geri kalanını gerçek veritabanı örneğiyle birlikte ortak kodda yapılandırabilirsiniz.
// 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 seçme
Önceki kod snippet'lerinde BundledSQLiteDriver
kullanılır. Bu, kaynaktan derlenmiş SQLite içeren ve tüm platformlarda SQLite'in en tutarlı ve güncel sürümünü sağlayan önerilen sürücüdür. OS tarafından sağlanan SQLite'i kullanmak istiyorsanız platforma özgü bir sürücü belirten platforma özgü kaynak kümelerinde setDriver
API'yi kullanın. Android için AndroidSQLiteDriver
, iOS için ise NativeSQLiteDriver
simgesini kullanabilirsiniz. NativeSQLiteDriver
kullanmak için iOS uygulamasının sistem SQLite ile dinamik olarak bağlantı kurması için bir bağlayıcı seçeneği sağlamanız gerekir.
// 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")
}
}
}
Farklılıklar
Room başlangıçta bir Android kitaplığı olarak geliştirildi ve daha sonra API uyumluluğuna odaklanarak KMP'ye taşındı. Room'un KMP sürümü, platformlar arasında ve Android'e özel sürümden biraz farklıdır. Bu farklılıklar aşağıda listelenmiş ve açıklanmıştır.
DAO işlevlerini engelleme
KMP için Room kullanılırken Android dışı platformlar için derlenen tüm DAO işlevlerinin, Flow
gibi reaktif dönüş türleri hariç suspend
işlevleri olması gerekir.
// 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, Kotlin'in birden fazla platform için sunduğu zengin özelliklere sahip asenkron kotlinx.coroutines
kitaplığından yararlanır. Optimum işlevsellik için suspend
işlevleri, mevcut kod tabanıyla geriye dönük uyumluluğu korumak amacıyla Android'e özgü DAO'lar hariç olmak üzere KMP projesinde derlenen DAO'lar için zorunlu kılınmıştır.
KMP ile özellik farklılıkları
Bu bölümde, Room'un KMP ve Android platform sürümleri arasındaki özellik farklılıkları açıklanmaktadır.
@RawQuery DAO işlevleri
Android dışındaki platformlar için derlenen ve @RawQuery
ile ek açıklamaya sahip işlevler için SupportSQLiteQuery
yerine RoomRawQuery
türündeki bir parametre beyan edilmesi gerekir.
@Dao
interface TodoDao {
@RawQuery
suspend fun getTodos(query RoomRawQuery): List<TodoEntity>
}
Ardından, çalışma zamanında sorgu oluşturmak için bir RoomRawQuery
kullanılabilir:
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)
}
Sorgu Geri Çağırma
Sorgu geri çağırma işlevlerini yapılandırmayla ilgili aşağıdaki API'ler yaygın olarak kullanılamaz ve bu nedenle Android dışındaki platformlarda kullanılamaz.
RoomDatabase.Builder.setQueryCallback
RoomDatabase.QueryCallback
Room'un gelecekteki bir sürümünde sorgu geri çağırma desteğini eklemeyi planlıyoruz.
Sorgu geri çağırma RoomDatabase.Builder.setQueryCallback
ile RoomDatabase
yapılandırmaya yönelik API ve geri çağırma arayüzü RoomDatabase.QueryCallback
ortak olarak kullanılamaz ve bu nedenle Android dışındaki diğer platformlarda kullanılamaz.
Otomatik Kapanış Veritabanı
Zaman aşımı sonrasında otomatik kapatmayı etkinleştirmek için kullanılan API (RoomDatabase.Builder.setAutoCloseTimeout
) yalnızca Android'de kullanılabilir ve diğer platformlarda kullanılamaz.
Önceden Paketlenmiş Veritabanı
Mevcut bir veritabanını (ör. önceden paketlenmiş veritabanı) kullanarak RoomDatabase
oluşturmak için aşağıdaki API'ler ortak olarak kullanılamaz ve bu nedenle Android dışındaki platformlarda kullanılamaz. Bu API'ler şunlardır:
RoomDatabase.Builder.createFromAsset
RoomDatabase.Builder.createFromFile
RoomDatabase.Builder.createFromInputStream
RoomDatabase.PrepackagedDatabaseCallback
Room'un gelecekteki bir sürümünde önceden paketlenmiş veritabanları için destek sunmayı planlıyoruz.
Çoklu Örnek Geçersiz Kılma
Çok örnekli geçersiz kılma özelliğini etkinleştiren API (RoomDatabase.Builder.enableMultiInstanceInvalidation
) yalnızca Android'de kullanılabilir ve yaygın veya diğer platformlarda kullanılamaz.