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 ドライバを構成する必要があります。これらのドライバは、ターゲット プラットフォームによって異なります。使用可能なドライバの実装については、ドライバの実装をご覧ください。
追加の設定情報については、以下をご覧ください。
データベース クラスの定義
共有 KMP モジュールの共通ソースセット内に、DAO とエンティティとともに、@Database
アノテーションを付けたデータベース クラスを作成する必要があります。これらのクラスを共通のソースに配置すると、すべてのターゲット プラットフォームで共有できるようになります。
// shared/src/commonMain/kotlin/Database.kt
@Database(entities = [TodoEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun getDao(): TodoDao
}
@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 実装を作成できます。たとえば、expect
を使用して共通コードで定義されているプラットフォーム固有の DAO を追加し、プラットフォーム固有のソースセットで追加のクエリを使用して actual
定義を指定できます。
データベース ビルダーの作成
各プラットフォームで Room をインスタンス化するためのデータベース ビルダーを定義する必要があります。ファイル システム API の違いにより、これは API でプラットフォーム固有のソースセットに含める必要がある唯一の部分です。たとえば、Android では通常、データベースの場所は Context.getDatabasePath()
API によって取得されますが、iOS の場合、データベースの場所は NSHomeDirectory
を使用して取得されます。
Android
データベース インスタンスを作成するには、データベース パスとともにコンテキストを指定します。データベース ファクトリを指定する必要はありません。
// 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
データベース インスタンスを作成するには、データベース ファクトリとデータベース パスを指定します。データベース ファクトリは、KClass<T>
型のレシーバを使用して、生成された名前が instantiateImpl
の拡張関数を呼び出すラムダ関数です。ここで、T
は @Database
アノテーションを付けたクラスの型です。
// shared/src/iosMain/kotlin/Database.kt
fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
val dbFilePath = NSHomeDirectory() + "/my_room.db"
return Room.databaseBuilder<AppDatabase>(
name = dbFilePath,
factory = { AppDatabase::class.instantiateImpl() }
)
}
JVM(デスクトップ)
データベース インスタンスを作成するには、データベース パスのみを指定します。データベース ファクトリを指定する必要はありません。
// shared/src/commonMain/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,
)
}
データベースのインスタンス化
プラットフォーム固有のコンストラクタのいずれかから 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 関数は、Flow
などのリアクティブな戻り値の型を除き、suspend
関数にする必要があります。
// 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 が複数のプラットフォーム向けに提供している、機能豊富な非同期 kotlinx.coroutines
ライブラリの利点があります。機能を最適化するために、KMP プロジェクトでコンパイルされた DAO には suspend
関数が適用されます。ただし、既存のコードベースとの下位互換性を維持するため、Android 固有の DAO は除きます。
KMP との機能の違い
このセクションでは、KMP と Android プラットフォーム バージョンの Room の機能の違いについて説明します。
@RawQuery DAO 関数
Android 以外のプラットフォーム用にコンパイルされた @RawQuery
アノテーション付きの関数は、エラーが発生します。Room の今後のバージョンで @RawQuery
のサポートを追加する予定です。
クエリ コールバック
クエリ コールバックを設定するための次の API は、一般的には利用できないため、Android 以外のプラットフォームでは使用できません。
RoomDatabase.Builder.setQueryCallback
RoomDatabase.QueryCallback
Room の今後のバージョンで、クエリ コールバックのサポートを追加する予定です。
クエリ コールバック RoomDatabase.Builder.setQueryCallback
とコールバック インターフェース RoomDatabase.QueryCallback
を使用して RoomDatabase
を構成する API は、一般的には利用できないため、Android 以外のプラットフォームでは使用できません。
データベースの自動クローズ
タイムアウト後の自動終了を有効にする API RoomDatabase.Builder.setAutoCloseTimeout
は Android でのみ使用でき、他のプラットフォームでは使用できません。
プレパッケージ データベース
既存のデータベース(あらかじめパッケージ化されたデータベース)を使用して RoomDatabase
を作成する次の API は、一般的には利用できないため、Android 以外のプラットフォームでは使用できません。これらの API は次のとおりです。
RoomDatabase.Builder.createFromAsset
RoomDatabase.Builder.createFromFile
RoomDatabase.Builder.createFromInputStream
RoomDatabase.PrepackagedDatabaseCallback
Room の今後のバージョンでは、事前にパッケージ化されたデータベースのサポートを追加する予定です。
複数インスタンスの無効化
マルチインスタンスの無効化を有効にする API RoomDatabase.Builder.enableMultiInstanceInvalidation
は、Android でのみ使用でき、一般的なプラットフォームや他のプラットフォームでは使用できません。