[英]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/
這個過程很簡單。
在左一的預期列中輸入預期錯誤日志。
在 Found 列中輸入 Found 錯誤日志,這是正確的。
按開始。 按鈕。 錯誤日志被轉換為 JSON。
按比較按鈕和瞧,你有你需要的差異。
該插件從 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(collate = 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
)
我遇到了 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.