【正社員】還元率83%【フリーランス】マージン一律5万円で案件をご紹介させていただきます。 詳細はこちら

【Android】<Kotlin>Roomのマイグレーションとは?仕組みと安全な対策方法を解説

【Android】<Kotlin>Roomのマイグレーションとは?仕組みと安全な対策方法を解説
すだ

みなさまこんにちは〜!
メモリアインクのすだです。

本日は、Roomで内部データベースを実装した際にマイグレーション処理を行う方法について、わかりやすく解説していきます!

この記事を読んでわかること…
・Roomのマイグレーションとは
・Roomのマイグレーション例
・Roomのマイグレーション時の注意点

目次

環境

  • Kotlin (ver 1.9.0)
  • Android Studio (Giraffe | 2022.3.1 Patch 3)

Roomのマイグレーションとは?

マイグレーション(migration)とは、
Roomで一度作ったデータベースの構造(テーブルやカラム)を変更する際に必要な「バージョン移行処理」のことです。

Roomは、構造が変わったまま実行されると「このデータベースは前と違う」と判断し、アプリをクラッシュさせる仕様になっています。
それを避けるために、「どうやって変えたか」を明示的に伝える必要があるのです。

Roomでマイグレーションが必要になる場面

以下のような場合には、マイグレーション対応が必須です:

  • カラム(列)を追加・削除した
  • テーブルを追加・削除した
  • データ型や初期値を変えた
  • 主キーやインデックスを変更した

Roomはデータベースの整合性を守るため、
バージョンが変わったのにマイグレーション処理が指定されていないとクラッシュします。

java.lang.IllegalStateException:
A migration from version X to Y was required but not found.

というようなエラーが発生し、アプリが起動しなくなってしまいますので、注意してください。

Roomのマイグレーション方法

基本的には、addMigrations() で手動マイグレーションを定義する形となります。

旧バージョンのEntityを以下として、今回はカラムを新しく追加するというマイグレーションを実行していきます。

旧バージョン▼

@Entity
data class User(
    @PrimaryKey val id: Int,
    val name: String
)

Step1. カラムを追加

@Entity
data class User(
    @PrimaryKey val id: Int,
    val name: String,
    val age: Int // ← 追加されたカラム
)

Step2. Database定義のversionを更新

@Database(entities = [User::class], version = 2)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

ここで必ず、versionを+1してください。

Step3. マイグレーション処理を追加&DB初期化処理も修正

val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(database: SupportSQLiteDatabase) {
        // 新しいカラム「age」を追加
        database.execSQL("ALTER TABLE User ADD COLUMN age INTEGER DEFAULT 0 NOT NULL")
    }
}

val MIGRATION_1_2 = object : Migration(1, 2) {ここで、マイグレーション処理をオブジェクトとして定義します。
Migration クラスを継承し、「バージョン1 → バージョン2」への移行処理をこの中に記述します。

database.execSQL(...) の部分では、SQL文を直接実行します。
今回の例では、ALTER TABLE 文を使って、User テーブルに age カラムを追加しています。

(重要:SQLiteではカラム追加時に 必ず初期値(DEFAULT)とNOT NULL制約をセットで指定する必要があります。でないと、既存のデータが入らないためエラーになります。)

val db = Room.databaseBuilder(
    context,
    AppDatabase::class.java,
    "user-db"
).addMigrations(MIGRATION_1_2)
 .build()

そして、ビルダーに.addMigrations(MIGRATION_1_2)を追加して定義したマイグレーション処理を適用します。

これにより、既存データを維持したまま新しいカラムが使えるようになります。

Roomは、データベースの現在のバージョンから最新版までのマイグレーション手順をすべて連続して適用する仕組みです。

そのため、過去のマイグレーションの履歴を全て残しておく必要があります。

例)バージョン1 → 4 にアップグレードされた場合

// v1 → v2: ageカラムを追加
val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("ALTER TABLE User ADD COLUMN age INTEGER DEFAULT 0 NOT NULL")
    }
}

// v2 → v3: createdAtカラムを追加
val MIGRATION_2_3 = object : Migration(2, 3) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("ALTER TABLE User ADD COLUMN createdAt TEXT DEFAULT '' NOT NULL")
    }
}

// v3 → v4: Addressテーブルを新規作成
val MIGRATION_3_4 = object : Migration(3, 4) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("""
            CREATE TABLE IF NOT EXISTS Address (
                id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
                userId INTEGER NOT NULL,
                address TEXT NOT NULL,
                FOREIGN KEY(userId) REFERENCES User(id)
            )
        """.trimIndent())
    }
}

初期化時に全ての履歴を指定してあげてください。

val db = Room.databaseBuilder(
    context,
    AppDatabase::class.java,
    "user-db"
)
.addMigrations(
    MIGRATION_1_2,
    MIGRATION_2_3,
    MIGRATION_3_4
)
.build()

fallbackToDestructiveMigrationを使う場合

fallbackToDestructiveMigrationはマイグレーション処理をスキップして、データベースを再作成してくれるメソッドです。
保存していたデータは消えるので、本番環境にはおすすめしませんが、主に開発・デバッグ中の一時的な手段としては有効です。

val db = Room.databaseBuilder(
    context,
    AppDatabase::class.java,
    "user-db"
).fallbackToDestructiveMigration()
 .build()

まとめ

おつかれさまでした。
いかがでしたでしょうか!

Roomを使用している上で、DB構造が変わったときには、
必ずこのマイグレーション処理を実装してください!

すだ

技術者としてのキャリアパスを次のレベルへと進めたい皆様、
未経験からIT・Webエンジニアを目指すなら【ユニゾンキャリア】
を通じて、
自分の市場価値をさらに向上させてみませんか?

それではまた次回の記事でお会いしましょう!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

弊社テックブログをご愛読いただきありがとうございます。
当テックブログを運用している株式会社メモリアインクは、
【正社員】還元率83%
【フリーランス】マージン一律5万円で案件のご紹介
と、エンジニアの皆様に分かりやすい形で稼げる仕組みを構築し提供させていただいております。

コメント

コメントする

目次