Room(Kotlin マルチプラットフォーム)

Room 永続ライブラリは、SQLite に抽象化レイヤを提供することで、 では、SQLite を最大限に活用しながら、より堅牢なデータベース アクセスを実現できます。この このページでは、Kotlin マルチプラットフォーム(KMP)プロジェクトで Room を使用する方法に焦点を当てます。詳細 Room の使用について詳しくは、Room を使用してローカル データベースにデータを保存するをご覧ください。 または公式サンプルをご覧ください。

依存関係をセットアップする

KMP をサポートしている Room の現在のバージョンは次のとおりです。 2.7.0-alpha01 以降

KMP プロジェクトで Room を設定するには、 モジュールの build.gradle.kts ファイルに追加します。

  • androidx.room:room-gradle-plugin - Room スキーマを構成するための Gradle プラグイン
  • androidx.room:room-compiler - コードを生成する KSP プロセッサ
  • androidx.room:room-runtime - ライブラリのランタイム部分
  • androidx.sqlite:sqlite-bundled - (省略可)バンドルされた SQLite ライブラリ

また、Room の SQLite ドライバを構成する必要があります。これらの要因は ターゲットプラットフォームに応じて 自動的に作成されます詳しくは、 ドライバの実装 を参照してください。

その他の設定情報については、以下をご覧ください。

で確認できます。

データベース クラスの定義

DAO とともに @Database アノテーション付きのデータベース クラスを作成する必要があります 共有 KMP モジュールの共通ソースセット内のエンティティを確認できます。配置 共通ソース内でこれらのクラスを使用すると、ターゲット 説明します。

インターフェースを使用して expect オブジェクトを宣言する場合 RoomDatabaseConstructor: Room コンパイラが actual を生成します あります。Android Studio が警告を発することがある "Expected object 'AppDatabaseConstructor' has no actual declaration in module"@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
)

なお、実際の宣言 / 期待される宣言を必要に応じて使用できます。 を使用してプラットフォーム固有の Room 実装を作成します。たとえば、 共通のコードで定義されるプラットフォーム固有の DAO。expect を使用して プラットフォーム固有のクエリを追加して actual 定義を指定する ソースセットです

データベース ビルダーの作成

各プラットフォームで Room をインスタンス化するために、データベース ビルダーを定義する必要があります。この プラットフォーム固有のソースに配置する必要がある API の唯一の部分 異なるためです。たとえば Android では データベースの場所は通常、 Context.getDatabasePath() API を使用します。iOS の場合、データベースのロケーションは NSFileManager を使用して取得できます。

Android

データベース インスタンスを作成するには、データベースとともに [Context] を指定します。 あります。

// 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

データベース インスタンスを作成するには、 NSFileManager(通常は 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(デスクトップ)

データベース インスタンスを作成するには、Java または 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,
    )
}

データベースのインスタンス化

プラットフォーム固有の API から RoomDatabase.Builder を取得したら、 Room データベースの残りの部分は共通のコードで構成できます。 実際のデータベースのインスタンス化も 一緒に行います

// 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 の選択

上記のコード スニペットでは BundledSQLiteDriver を使用しています。これが 推奨ドライバには、ソースからコンパイルされた SQLite が含まれます。 すべてのプラットフォームで最も整合性の高い最新バージョンの SQLite を提供すること。もし OS 提供の SQLite を使用する場合は、プラットフォーム固有の setDriver API を使用します。 プラットフォーム固有のドライバを指定するソースセットです。Android の場合は、次のコマンドを使用します。 AndroidSQLiteDriver。iOS の場合は NativeSQLiteDriver を使用できます。宛先 NativeSQLiteDriver を使用するには、リンカー オプションを提供して、iOS アプリが アプリをシステム 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")
        }
    }
}

相違点

Room はもともと Android ライブラリとして開発され、その後 API の互換性に重点を置いた KMP。Room の KMP バージョンは多少異なる プラットフォーム間の違いや Android 固有のバージョンからの違いですこれらの違いは、 以下で説明します。

ブロック DAO 関数

Room for KMP を使用する場合、Android 以外のプラットフォーム用にコンパイルされたすべての DAO 関数が リアクティブの戻り値の型を除き、suspend 関数にする必要があります。 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() { // … }
}

豊富な機能を備えた非同期 kotlinx.coroutines ライブラリが Room で利用できる 複数のプラットフォームに 利用できます最適な機能を得るには、suspend 関数は、KMP プロジェクトでコンパイルされた DAO に適用されます。ただし、例外として 既存の DAO との下位互換性を維持するための Android 固有の DAO 学びました。

KMP との機能の違い

このセクションでは、KMP と Android プラットフォームの機能の違いについて説明します。 できません。

@RawQuery DAO 関数

Android 以外のプラットフォーム用にコンパイルされた、@RawQuery アノテーション付きの関数 代わりに RoomRawQuery 型のパラメータを宣言し、 SupportSQLiteQuery

@Dao
interface TodoDao {
  @RawQuery
  suspend fun getTodos(query RoomRawQuery): List<TodoEntity>
}

RoomRawQuery を使用すると、実行時にクエリを作成できます。

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)
}

クエリ コールバック

クエリ コールバックを構成するための次の API は、一般的には使用できない Android 以外のプラットフォームでは使用できません。

  • RoomDatabase.Builder.setQueryCallback
  • RoomDatabase.QueryCallback

Room の今後のバージョンで、クエリ コールバックのサポートを追加する予定です。

クエリ コールバックを使用して RoomDatabase を構成する API RoomDatabase.Builder.setQueryCallback とコールバック インターフェース RoomDatabase.QueryCallback は共通で使用できないため、使用できません Android 以外のプラットフォームでも利用できます

データベースの自動終了

タイムアウト後の自動終了を有効にする API RoomDatabase.Builder.setAutoCloseTimeout は、Android でのみご利用いただけます。 他のプラットフォームでは利用できません。

パッケージ化前のデータベース

次の API を使用して、RoomDatabase既存のデータベース( 事前パッケージ化済みデータベースなど)は、一般的には利用できないため、 Android 以外のプラットフォームで利用できますこれらの API は次のとおりです。

  • RoomDatabase.Builder.createFromAsset
  • RoomDatabase.Builder.createFromFile
  • RoomDatabase.Builder.createFromInputStream
  • RoomDatabase.PrepackagedDatabaseCallback

次のバージョンで、事前パッケージ化済みデータベースのサポートを追加する予定です。 会議室。

マルチインスタンスの無効化

複数インスタンスの無効化を有効にする API RoomDatabase.Builder.enableMultiInstanceInvalidation は以下でのみご利用いただけます: 一般的なプラットフォームや他のプラットフォームでは利用できません。