Oda (Kotlin Multiplatform)

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 eklentisi
  • androidx.room:room-compiler: Kod oluşturan KSP işlemcisi
  • androidx.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:

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.