web-dev-qa-db-ja.com

ルームデータベースの移行がALTER TABLEの移行を適切に処理しない

Java.lang.IllegalStateException

移行はユーザー(therealandroid.github.com.roomcore.Java.User)を適切に処理しませんでした。

期待される:

TableInfo {name = 'user'、columns = {name = Column {name = 'name'、type = 'TEXT'、notNull = false、primaryKeyPosition = 0}、age = Column {name = 'age'、type = 'INTEGER '、notNull = true、primaryKeyPosition = 0}、id = Column {name =' id '、type =' INTEGER '、notNull = true、primaryKeyPosition = 1}}、foreignKeys = []}が見つかりました:

見つかった

TableInfo {name = 'user'、columns = {name = Column {name = 'name'、type = 'TEXT'、notNull = false、primaryKeyPosition = 0}、id = Column {name = 'id'、type = 'INTEGER '、notNull = true、primaryKeyPosition = 1}、age = Column {name =' age '、type =' INTEGER '、notNull = false、primaryKeyPosition = 0}}、foreignKeys = []}

単純な移行を実行しようとしています。Userというクラスがあり、2つの列ID (primary key)NAME TEXTがあり、データベースに2人のユーザーデータを入力してから、列AGEをオブジェクトUserに追加し、移行定数にalter tableを追加してこの新しい列を追加し、最後にデータベースのバージョンを1から2に置き換えます。

ここにコードがあります

User.class

@Entity(tableName = "user")
  public class User {

  @PrimaryKey
  private int id;

  @ColumnInfo(name = "name")
  private String name;

  @ColumnInfo(name = "age")
  private int age;


  public int getId() {
      return id;
  }

  public void setId(int id) {
      this.id = id;
  }

  public String getName() {
      return name;
  }

  public void setName(String name) {
      this.name = name;
  }

  public int getAge() {
      return age;
  }

  public void setAge(int age) {
      this.age = age;
  }
}

データベースクラス

@Database(entities = {User.class}, version = 2)
public abstract class RoomDatabaseImpl extends RoomDatabase {
    abstract UserDao userDao();
}

移行コード

public static Migration MIGRATION_1_2 = new Migration(1, 2) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER");
    }
 };

そしてそれは呼び出す

Room.databaseBuilder(context, RoomDatabaseImpl.class, "Sample.db")
            .addMigrations(MIGRATION_1_2)
            .allowMainThreadQueries()
            .build();

AGEを追加して移行を実行するオブジェクトを変更する前に、2つのレジスタを追加すると動作します。

移行を実行した後、次のように新しいユーザーを追加しようとしました。

  User user = new User();
  user.setName("JoooJ");
  user.setId(3);
  user.setAge(18);

  List<User> userList = new ArrayList<>();
  userList.add(user);
  App.database(this).userDao().insertAll(userList);  // The crash happens here

その他の情報:

Android Studio 3と私は実際にテストしませんでした。

依存関係:

compile "Android.Arch.persistence.room:runtime:1.0.0-alpha9-1"
annotationProcessor "Android.Arch.persistence.room:compiler:1.0.0-alpha9-1"

compile "Android.Arch.persistence.room:rxjava2:1.0.0-alpha9-1"
gradle 2.3.3

誰かが私を助けてくれますか、私は本当に私が間違っているのか、それがバグであるのか分かりません。

24
diogojme

エラーメッセージの解析は困難ですが、違いがあります。

TableInfo {name = 'user'、columns = {name = Column {name = 'name'、type = 'TEXT'、notNull = false、primaryKeyPosition = 0}、age = Column {name = 'age'、type = 'INTEGER '、notNull = true、primaryKeyPosition = 0}、id = Column {name =' id ' 、type = 'INTEGER'、notNull = true、primaryKeyPosition = 1}}、foreignKeys = []}が見つかりました:

見つかった

TableInfo {name = 'user'、columns = {name = Column {name = 'name'、type = 'TEXT'、notNull = false、primaryKeyPosition = 0}、id = Column {name = 'id'、type = 'INTEGER '、notNull = true、primaryKeyPosition = 1}、age = Column {name =' age '、type =' INTEGER '、notNull = false、primaryKeyPosition = 0}}、foreignKeys = []}

年齢はnull可能ですが、Roomはnullでないと予測しました。

移行を次のように変更します。

database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL");

この例外の説明は解析が非常に難しいため、差分を作成する 小さなスクリプト を作成しました。

例:

mig "Java.lang.IllegalStateException: Migration failed. expected:TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, age=Column{name='age', type='INTEGER', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}}, foreignKeys=[]} , found:TableInfo{name='user', columns={name=Column{name='name', type='TEXT', notNull=false, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', notNull=true, primaryKeyPosition=1}, age=Column{name='age', type='INTEGER', notNull=false, primaryKeyPosition=0}}, foreignKeys=[]}"

結果:

expected/found diff

47
Benoit Duffez

私もあなたが見つけることができる小さなJSスクリプトを書きました https://hrankit.github.io/RoomSQLiteDifferenceFinder/

プロセスは非常に簡単です。

  1. 左の「期待される」列に期待されるエラーログを入力します。

  2. 見つかったエラーログを、正しい列である[検出]列に入力します。

  3. Goを押します。ボタン。エラーログはJSONに変換されます。

  4. 比較ボタンと出来上がりを押すと、必要な違いがあります。

このプラグインは、Android Studio Logcat。

比較の画像はこちらからご覧ください

11
HRankit

どのリンクでも正しい答えはありません。多くの実験の後、その方法を見つけました。 ALTERクエリを機能させるには、次の方法で記述する必要があります。

database.execSQL("ALTER TABLE 'user' ADD COLUMN 'age' INTEGER NOT NULL DEFAULT 0")

ただし、整数のデフォルト値は何でもかまいません。

文字列型の列を追加する場合は、次の方法で追加します。

database.execSQL("ALTER TABLE 'user' ADD COLUMN 'address' TEXT")

これは魅力のように機能します。

6
Prabhtej Singh

今日、この問題に直面しました。intIntegerフィールドをEntitiesに変更しました。 intをnullにすることはできませんが、Integerオブジェクトはnullにすることができます。

3
Zaid Mirza

NotNullの違いが生じている場合は、クラスフィールドに@NonNullアノテーションを付けるか、ALTER TABLEでSQLを変更するだけです。しかし、期待される列タイプの違いを取得している場合:TYPE = TEXT、見つかったTYPE = ''(COLLATE NOCASE)、または予想されたINTEGER、見つかったINT、唯一の解決策はテーブルを削除して再作成することです。 Sqliteでは、列の種類を変更できません。

INTの代わりにSqliteでINTEGERを使用して、Javaエンティティを@ColumnInfo(collat​​e = NOCASE)でマークします(SqliteでNOCASEを使用する場合))。

App\schemasの下のjsonファイルを見て、予想されるクエリのsqlを取得します。

static final Migration MIGRATION_2_3= new Migration(2, 3) {
        @Override
        public void migrate(SupportSQLiteDatabase database) {

            database.execSQL("DROP TABLE IF EXISTS table_tmp");

            database.execSQL("CREATE TABLE IF NOT EXISTS `table_tmp` ...");

            database.execSQL("insert into table_tmp (`id`, `name` , ...");

            database.execSQL("DROP INDEX IF EXISTS `index_table_name`");

            database.execSQL("CREATE INDEX IF NOT EXISTS `index_table_name` ON `table_tmp` (`name`)");

            database.execSQL("DROP TABLE IF EXISTS table");

            database.execSQL("alter table table_tmp rename to table");

        }
    };
1
live-love