[英]Room auto-migration with rename columns failing (NOT NULL constraint failed, bad generated Migration class)
我最近重命名了 Room 實體中的列,方法是在列上添加前綴setting_
:
在其他一些變化中,這是它產生的自動遷移 impl:
class AppDatabase_AutoMigration_15_16_Impl extends Migration {
private final AutoMigrationSpec callback = new SettingsRenamedColumnsAutoMigration();
public AppDatabase_AutoMigration_15_16_Impl() {
super(15, 16);
}
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE `merchants` ADD COLUMN `accessLevel` INTEGER NOT NULL DEFAULT 0");
database.execSQL("CREATE TABLE IF NOT EXISTS `merchantSettings` (`setting_id` INTEGER NOT NULL, `setting_group_id` INTEGER, `setting_type` TEXT NOT NULL, `setting_name` TEXT NOT NULL, `setting_value` TEXT NOT NULL, `setting_data_type` TEXT NOT NULL, `setting_created_at` INTEGER NOT NULL, `setting_updated_at` INTEGER NOT NULL, PRIMARY KEY(`setting_id`), FOREIGN KEY(`setting_group_id`) REFERENCES `merchants`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )");
database.execSQL("CREATE INDEX IF NOT EXISTS `index_merchantSettings_setting_group_id` ON `merchantSettings` (`setting_group_id`)");
database.execSQL("CREATE TABLE IF NOT EXISTS `_new_branchSettings` (`setting_id` INTEGER NOT NULL, `setting_branch_id` INTEGER, `setting_type` TEXT NOT NULL, `setting_name` TEXT NOT NULL, `setting_value` TEXT NOT NULL, `setting_data_type` TEXT NOT NULL, `setting_created_at` INTEGER NOT NULL, `setting_updated_at` INTEGER NOT NULL, PRIMARY KEY(`setting_id`), FOREIGN KEY(`setting_branch_id`) REFERENCES `branches`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )");
database.execSQL("INSERT INTO `_new_branchSettings` (`setting_type`,`setting_updated_at`) SELECT `setting_type`,`updated_at` FROM `branchSettings`");
database.execSQL("DROP TABLE `branchSettings`");
database.execSQL("ALTER TABLE `_new_branchSettings` RENAME TO `branchSettings`");
database.execSQL("CREATE INDEX IF NOT EXISTS `index_branchSettings_setting_branch_id` ON `branchSettings` (`setting_branch_id`)");
DBUtil.foreignKeyCheck(database, "branchSettings");
database.execSQL("CREATE TABLE IF NOT EXISTS `_new_branches` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT, `lat` TEXT, `lng` TEXT, `is_default` INTEGER, `email` TEXT, `phone` TEXT, `clover_category_id` TEXT, `clover_gift_item` TEXT, `clover_cashless_item` TEXT, `prep_time` INTEGER, `opening_hours` TEXT, `currency_code` TEXT, `created_at` INTEGER NOT NULL, `updated_at` INTEGER NOT NULL, PRIMARY KEY(`id`))");
database.execSQL("INSERT INTO `_new_` (`id`,`name`,`address`,`lat`,`lng`,`is_default`,`email`,`phone`,`prep_time`,`opening_hours`,`currency_code`,`created_at`,`updated_at`) SELECT `id`,`name`,`address`,`lat`,`lng`,`is_default`,`email`,`phone`,`prep_time`,`opening_hours`,`currency_code`,`created_at`,`updated_at` FROM `branches`");
database.execSQL("DROP TABLE `branches`");
database.execSQL("ALTER TABLE `_new_` RENAME TO `branches`");
this.callback.onPostMigrate(database);
}
}
使用以下自動遷移規范:
AutoMigration(
from = 15,
to = 16,
spec = AppDatabase.SettingsRenamedColumnsAutoMigration::class
)
@RenameColumn.Entries(
RenameColumn(
tableName = "branchSettings",
fromColumnName = "id",
toColumnName = "setting_id"
),
RenameColumn(
tableName = "branchSettings",
fromColumnName = "branch_id",
toColumnName = "setting_branch_id"
),
RenameColumn(
tableName = "branchSettings",
fromColumnName = "name",
toColumnName = "setting_name"
),
RenameColumn(
tableName = "branchSettings",
fromColumnName = "value",
toColumnName = "setting_value"
),
RenameColumn(
tableName = "branchSettings",
fromColumnName = "data_type",
toColumnName = "setting_data_type"
),
RenameColumn(
tableName = "branchSettings",
fromColumnName = "created_at",
toColumnName = "setting_created_at"
),
RenameColumn(
tableName = "branchSettings",
fromColumnName = "updated_at",
toColumnName = "setting_updated_at"
),
)
@DeleteColumn.Entries(
DeleteColumn(tableName = "branches", columnName = "checkout_category_id")
)
class SettingsRenamedColumnsAutoMigration : AutoMigrationSpec
這個錯誤正在影響我認為的每個更新程序:
android.database.sqlite.SQLiteConstraintException: NOT NULL constraint failed: _new_branchSettings.setting_name (code 1299 SQLITE_CONSTRAINT_NOTNULL)
at android.database.sqlite.SQLiteConnection.nativeExecuteForChangedRowCount(Native Method)
at android.database.sqlite.SQLiteConnection.executeForChangedRowCount(SQLiteConnection.java:831)
at android.database.sqlite.SQLiteSession.executeForChangedRowCount(SQLiteSession.java:756)
at android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:66)
at android.database.sqlite.SQLiteDatabase.executeSql(SQLiteDatabase.java:1806)
at android.database.sqlite.SQLiteDatabase.execSQL(SQLiteDatabase.java:1733)
at androidx.sqlite.db.framework.FrameworkSQLiteDatabase.execSQL(FrameworkSQLiteDatabase.java:255)
at loylap.core.sdk.data.local.db.AppDatabase_AutoMigration_15_16_Impl.migrate(AppDatabase_AutoMigration_15_16_Impl.java:31)
at androidx.room.RoomOpenHelper.onUpgrade(RoomOpenHelper.kt:91)
我不知道足夠多的 MySQL/Room 來確認,但看起來 15->16 遷移 class 至少有 2 個問題。 它創建一個_new_branchSettings
表,但只插入 2 個隨機列( setting_type
和setting_updated_at
)。 它不應該在這里插入所有東西嗎? 因此錯誤 - setting_name
應該不是 null。
另外,我很懷疑INSERT INTO ``_new_``
... _new_
是什么,誰創造了它? 對我來說,這看起來像是一個錯字,應該插入到_new_branches
中。
這似乎是一個可能的錯誤。 您可能希望將此報告為錯誤。
正如您所建議的,INSERT INTO 似乎正在省略列,因此由於列被省略,這些值將是 null。
我已經查看了INSERT INTO ``_new_``
並且(沒有非常仔細地檢查)似乎很好,盡管不是最好的命名約定(可能是一個錯誤,但只有 1 它可能不會導致問題)。
就個人而言,也許比你自己更可疑,我的解決方案是手動遷移,這將更改新分支設置表的 INSERT INTO 以在找到空值時插入默認值(只需通過從所有列中提取和分配值來進行更正就足夠了)。
作為手動遷移而不是自動遷移的解決方案將使用(注意未經測試,因此它是*原則代碼):-
database.execSQL("ALTER TABLE `merchants` ADD COLUMN `accessLevel` INTEGER NOT NULL DEFAULT 0");
database.execSQL("CREATE TABLE IF NOT EXISTS `merchantSettings` (`setting_id` INTEGER NOT NULL, `setting_group_id` INTEGER, `setting_type` TEXT NOT NULL, `setting_name` TEXT NOT NULL, `setting_value` TEXT NOT NULL, `setting_data_type` TEXT NOT NULL, `setting_created_at` INTEGER NOT NULL, `setting_updated_at` INTEGER NOT NULL, PRIMARY KEY(`setting_id`), FOREIGN KEY(`setting_group_id`) REFERENCES `merchants`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )");
database.execSQL("CREATE INDEX IF NOT EXISTS `index_merchantSettings_setting_group_id` ON `merchantSettings` (`setting_group_id`)");
/* SPLIT to make the code more readable */
database.execSQL(
"CREATE TABLE IF NOT EXISTS `_new_branchSettings` " +
"(`setting_id` INTEGER NOT NULL, " +
"`setting_branch_id` INTEGER, " +
"`setting_type` TEXT NOT NULL, " +
"`setting_name` TEXT NOT NULL, " +
"`setting_value` TEXT NOT NULL, " +
"`setting_data_type` TEXT NOT NULL, " +
"`setting_created_at` INTEGER NOT NULL, " +
"`setting_updated_at` INTEGER NOT NULL, " +
"PRIMARY KEY(`setting_id`), " +
"FOREIGN KEY(`setting_branch_id`) REFERENCES `branches`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE " +
")"
);
/* SPLIT to make the code more readable */
database.execSQL("INSERT INTO `_new_branchSettings` (" +
"`setting_id`," +
"`setting_branch_id`," +
"`setting_type`," +
"`setting_name`," +
"`setting_value`," +
"`setting_data_type`," +
"`setting_created_at`," +
"`setting_updated_at`" +
") " +
"SELECT " +
"`id`," +
"`branch_id`," +
"coalesce(`settng_type`,'SETTING_TYPEWASNULL')," +
"coalesce(`name`,'NAMEWASNULL')," +
"coalesce(`value`,'VALUEWASNULL')," +
"coalesce(`data_type`,'DATA_TYPEWASNULL')," +
"coalesce(`created_at`,-9999)," +
"coalsce(`updated_at`,-8888) " +
"FROM `branchSettings`" +
"");
/* Above INSERT replaces the following suspicious INSERT INTO */
//database.execSQL("INSERT INTO `_new_branchSettings` (`setting_type`,`setting_updated_at`) SELECT `setting_type`,`updated_at` FROM `branchSettings`");
database.execSQL("DROP TABLE `branchSettings`");
database.execSQL("ALTER TABLE `_new_branchSettings` RENAME TO `branchSettings`");
database.execSQL("CREATE INDEX IF NOT EXISTS `index_branchSettings_setting_branch_id` ON `branchSettings` (`setting_branch_id`)");
DBUtil.foreignKeyCheck(database, "branchSettings");
database.execSQL("CREATE TABLE IF NOT EXISTS `_new_branches` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT, `lat` TEXT, `lng` TEXT, `is_default` INTEGER, `email` TEXT, `phone` TEXT, `clover_category_id` TEXT, `clover_gift_item` TEXT, `clover_cashless_item` TEXT, `prep_time` INTEGER, `opening_hours` TEXT, `currency_code` TEXT, `created_at` INTEGER NOT NULL, `updated_at` INTEGER NOT NULL, PRIMARY KEY(`id`))");
database.execSQL("INSERT INTO `_new_` (`id`,`name`,`address`,`lat`,`lng`,`is_default`,`email`,`phone`,`prep_time`,`opening_hours`,`currency_code`,`created_at`,`updated_at`) SELECT `id`,`name`,`address`,`lat`,`lng`,`is_default`,`email`,`phone`,`prep_time`,`opening_hours`,`currency_code`,`created_at`,`updated_at` FROM `branches`");
database.execSQL("DROP TABLE `branches`");
database.execSQL("ALTER TABLE `_new_` RENAME TO `branches`");
//this.callback.onPostMigrate(database); /* I believe not needed in actual code as generated code should do this as needed */
????WASNULL
、 -9999
和-8888
為更可接受的值,並且可能包括以下 UPADTE 來更改/計算值。
盡管可能永遠不會使用替代品。
如果願意,請參閱coalesce 標量 function或使用ifnull 標量 function ,它們的工作方式相同,只是合並更靈活一點,而不是在這種情況下需要靈活性。
coalesce 更明顯,因此使用而不是 ifnull。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.