1. ホーム
  2. java

[解決済み] 新しいテーブルが追加されただけで、ルームデータベースが移行される

2022-06-15 02:15:36

質問

簡単なRoomデータベースがあるとします。

@Database(entities = {User.class}, version = 1)
abstract class AppDatabase extends RoomDatabase {
    public abstract Dao getDao();
}

さて、新しいエンティティを追加します。 Pet を追加し、バージョンを 2 に上げています。

@Database(entities = {User.class, Pet.class}, version = 2)
abstract class AppDatabase extends RoomDatabase {
    public abstract Dao getDao();
}

もちろん、Room は例外を投げます。 java.lang.IllegalStateException: A migration from 1 to 2 is necessary.

仮に変更していないとすると User クラスを変更していないと仮定すると (したがって、すべてのデータは安全です)、私は単に新しいテーブルを作成する移行を提供する必要があります。そこで、私は Room によって生成されたクラスを調べ、新しいテーブルを作成するために生成されたクエリを検索し、それをコピーしてマイグレーションに貼り付けます。

final Migration MIGRATION_1_2 =
        new Migration(1, 2) {
            @Override
            public void migrate(@NonNull final SupportSQLiteDatabase database) {
                database.execSQL("CREATE TABLE IF NOT EXISTS `Pet` (`name` TEXT NOT NULL, PRIMARY KEY(`name`))");
            }
        };

しかし、それを手動で行うのは不便だと思います。 ルームに伝える方法はありますか。 既存のテーブルを一切触らないので、データは安全です。私のために移行を作成してください。

どのように解決するのですか?

部屋 ではない は良いマイグレーションシステムを持っていません。 2.1.0-alpha03 .

そのため、より良いマイグレーションシステムができるまでは、ルームで簡単にマイグレーションを行うためのいくつかの回避策があります。

のようなメソッドは存在しないので @Database(createNewTables = true) または MigrationSystem.createTable(User::class) のどちらかであるべきで、唯一の可能な方法は、実行することです。

CREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))

の中に migrate メソッドの中で

val MIGRATION_1_2 = object : Migration(1, 2){
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("CREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))")
    }
}

上記のように SQL スクリプトを取得するために、4つの方法があります。

1. 自分で書く

基本的には、Room が生成するスクリプトと一致するような上記のスクリプトを書く必要があります。この方法は可能ですが、実現は不可能です。(50個のフィールドがあると仮定して)

2. スキーマをエクスポートする

もしあなたが exportSchema = true の中に @Database というアノテーションを付けると、Room はプロジェクトフォルダの /schemas にデータベーススキーマを生成します。使用法は次のとおりです。

@Database(entities = [User::class], version = 2, exportSchema = true)
abstract class AppDatabase : RoomDatabase {
   //...
}

の中に、以下の行が含まれていることを確認してください。 build.grade に記述していることを確認してください。

kapt {
    arguments {
        arg("room.schemaLocation", "$projectDir/schemas".toString())
    }
} 

プロジェクトを実行またはビルドすると、JSONファイルが生成されます。 2.json を取得します。このファイルには、Room データベース内のすべてのクエリが含まれています。

  "formatVersion": 1,
  "database": {
    "version": 2,
    "identityHash": "325bd539353db508c5248423a1c88c03",
    "entities": [
      {
        "tableName": "User",
        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, PRIMARY KEY(`id`))",
        "fields": [
          {
            "fieldPath": "id",
            "columnName": "id",
            "affinity": "INTEGER",
            "notNull": true
          },

つまり、上記のような createSql の中に migrate メソッドを使用します。

3. AppDatabase_Implからクエリを取得する

スキーマをエクスポートしたくない場合でも、プロジェクトを実行またはビルドすることでクエリを取得することができ、その結果 AppDatabase_Impl.java ファイルを生成し、指定されたファイル内でクエリを実行することができます。

@Override
public void createAllTables(SupportSQLiteDatabase _db) {
  _db.execSQL("CREATE TABLE IF NOT EXISTS `User` (`id` INTEGER, PRIMARY KEY(`id`))");

createAllTables メソッド内に、すべてのエンティティの作成スクリプトがあります。これを取得して migrate メソッドに含めることができます。

4. アノテーションの処理。

ご推察の通り、Room では、上記のようなすべての schema を生成し、さらに AppDatabase_Impl といったファイルをコンパイル時に追加するアノテーション処理と

kapt "androidx.room:room-compiler:$room_version"

つまり、同じようにして、必要なcreate queryを全て生成してくれるアノテーション処理ライブラリを自作することもできるのです。

のルームアノテーションのためのアノテーション処理ライブラリを作るということです。 @Entity@Database . でアノテーションされたクラスを例にとると @Entity を例とします。以下は、その手順です。

  1. 新しい StringBuilder を作成し、"CREATE TABLE IF NOT EXISTS "を追加します。
  2. テーブル名は class.simplename によって、あるいは tableName のフィールド @Entity . それをあなたの StringBuilder
  3. 次に、クラスの各フィールドについて、SQLのカラムを作成します。フィールドの名前、型、nullability をフィールド自身か @ColumnInfo アノテーションを使用します。 すべてのフィールドに対して id INTEGER NOT NULL のスタイルで、カラムの StringBuilder .
  4. による主キーの追加 @PrimaryKey
  5. 追加 ForeignKeyIndices が存在する場合
  6. 終了後、文字列に変換し、使用したい新しいクラスに保存します。例えば、以下のように保存します。
public final class UserSqlUtils {
  public String createTable = "CREATE TABLE IF NOT EXISTS User (id INTEGER, PRIMARY KEY(id))";
}

次に,これを

val MIGRATION_1_2 = object : Migration(1, 2){
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL(UserSqlUtils().createTable)
    }
}

私は自分用にこのようなライブラリを作りましたので、それをチェックアウトし、あなたのプロジェクトで使用することもできます。なお、私が作ったライブラリは完全なものではなく、テーブル作成に関する私の必要条件を満たすだけのものです。

RoomExtensionによる移行性の向上

RoomExtension を利用するアプリケーション

お役に立ったでしょうか。

最新情報

この回答を書いている時点では、ルームバージョンは 2.1.0-alpha03 で、開発者にメールしたところ、次のような回答がありました。

でのマイグレーションシステムがより良くなることが期待されます。 2.2.0

残念ながら、私たちはより良いマイグレーション・システムをまだ持っていません。