簡體   English   中英

Room 數據庫遷移無法正確處理 ALTER TABLE 遷移

[英]Room Database Migration doesnt properly handle ALTER TABLE migration

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的類,它有兩列ID (primary key)NAME TEXT ,然后我用兩個用戶數據填充數據庫,然后在對象User添加列AGE和在遷移常量中,我添加了一個alter table來添加這個新列,最后我將數據庫 1 的版本替換為 2。

這是代碼

用戶類

@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並執行遷移之前,我添加了兩個寄存器並且它可以工作。

執行遷移后,我只是嘗試添加一個新用戶如下:

  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

有人可以幫我嗎,我真的不知道我做錯了什么,或者這是否是一個錯誤。

錯誤消息很難解析,但有區別:

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=[]}

Age 可以為空,但 Room 預計它不為空。

將您的遷移更改為:

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=[]}"

結果:

預期/發現差異

我也寫了一個小的 JS 腳本,你可以找到https://hrankit.github.io/RoomSQLiteDifferenceFinder/

這個過程很簡單。

  1. 在左一的預期列中輸入預期錯誤日志。

  2. 在 Found 列中輸入 Found 錯誤日志,這是正確的。

  3. 按開始。 按鈕。 錯誤日志被轉換為 JSON。

  4. 按比較按鈕和瞧,你有你需要的差異。

該插件從 Android Studio Logcat 中找出了兩個 Expected 和 Found 轉儲的區別。

在此處查看比較圖像

任何鏈接中的答案都不正確。 經過多次試驗,找到了方法。 ALTER 查詢需要按以下方式編寫才能使其工作:

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

但是,Integer DEFAULT 值可以是任何值。

如果要添加String類型的列,添加方式如下:

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

這就像一個魅力。

我今天遇到了這個問題,我只是在Entities中將int字段更改為Integer 因為int不能為 null 但Integer對象可以為 null。

如果要添加整數類型列,請添加此代碼

database.execSQL("ALTER TABLE users"
                    + " ADD COLUMN year INTEGER NOT NULL DEFAULT 0 ")

如果您遇到 notNull 差異,您可以簡單地使用 @NonNull 注釋標記您的類字段,或者使用 ALTER TABLE 更改您的 sql。 但是,如果您遇到列類型差異,例如預期的:TYPE=TEXT,然后找到 TYPE='' (COLLATE NOCASE),或者預期的 INTEGER,找到 INT,那么唯一的解決方案是刪除並重新創建您的表。 Sqlite 不允許更改列類型。

在 Sqlite 中使用 INTEGER 而不是 INT 並使用 @ColumnInfo(collat​​e = NOCASE) 標記您的 Java 實體(如果您在 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");

        }
    };

我在 kotlin 中遇到過 notNull 差異並發現如下異常

Expected:
TableInfo{name='enDic', columns={definition=Column{name='definition', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, _id=Column{name='_id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='null'}, favourite=Column{name='favourite', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, word=Column{name='word', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, usage=Column{name='usage', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
Found:
TableInfo{name='enDic', columns={usage=Column{name='usage', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, definition=Column{name='definition', type='text', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, _id=Column{name='_id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}, favourite=Column{name='favourite', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, word=Column{name='word', type='text', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}

然后我用下面提到的代碼來解決這個問題

@Entity(tableName = "enDic")
data class Word(

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "_id")
    var _id: Int?,

    @ColumnInfo(name = "word")
    var word: String?,

    @ColumnInfo(name = "definition")
    var definition: String,

    @ColumnInfo(name = "favourite")
    var favourite: Int?,

    @ColumnInfo(name = "usage")
    var usage: Int?

)

而不是這個代碼

@Entity(tableName = "enDic")
data class Word(

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "_id")
    var _id: Int,

    @ColumnInfo(name = "word")
    var word: String,

    @ColumnInfo(name = "definition")
    var definition: String,

    @ColumnInfo(name = "favourite")
    var favourite: Int,

    @ColumnInfo(name = "usage")
    var usage: Int

)

@HRankit 在他的回答中提到的工具:當我今天嘗試時,鏈接似乎不起作用。 如果您也是這種情況,請繼續閱讀:

如果有人在問題中提到的錯誤消息中苦苦掙扎,並且有一個包含大量列的巨大表,您可能想嘗試使用在線工具來檢查預期和找到的模式。

在生成<db_version_number>.json (例如: 13.json )文件之前決定列名和數據類型也非常重要。 如果 json 文件已經生成,並且之后您對 Entity 類進行了一些更改,您可能需要刪除 json 文件並重建項目以使用正確的值集生成它。

最后,您應該檢查有關遷移本身的 sql 語句。

我遇到了 roomVersion '2.4.0-alpha01' 的問題,這個版本沒有為我的情況生成索引表

@Entity(
    tableName = "SoundRules",
    indices = [
        Index(value = ["remoteId"], unique = true)
    ]
) 

我解決了剛剛將房間版本更新為“2.4.0-alpha03”的問題

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM