使用 Android 字符串数组在 Room 中迁移
Migration in Room with an Android Array of Strings
我在 Android 中有一个数据库,我从中删除了一列。我正在做迁移,我发现它不像对已删除的列进行 DROP 那样简单。
然后我看到我必须采取一系列步骤,创建一个临时 table,稍后将成为新的 table 与删除的列,但问题是这个 table 包含一个字符串数组字段,我不知道如何在 SQL 中声明。
@Entity(tableName = "recipe_table")
data class RecipesDb(
@PrimaryKey
@ColumnInfo(name = "id")
val id: Long,
@ColumnInfo(name = "name")
val name: String,
@ColumnInfo(name = "category")
val category: List<String>,
@ColumnInfo(name = "isRecommended")
val isRecommended: Boolean,
@ColumnInfo(name = "images")
val images: List<String>,
@ColumnInfo(name = "ingredients")
val ingredients: List<String>,
@ColumnInfo(name = "date")
val date: Long,
@ColumnInfo(name = "time")
val time: Int,
@ColumnInfo(name = "difficult")
val difficult: String,
@ColumnInfo(name = "originalUrl")
val originalURL: String? = null,
@ColumnInfo(name = "author")
val author: String,
@ColumnInfo(name = "siteName")
val siteName: String
)
现在我删除了 ingredients
列。我想做这样的事情:
private val MIGRATION_3_2 = object : Migration(3,2) {
override fun migrate(database: SupportSQLiteDatabase) {
//Drop column isn't supported by SQLite, so the data must manually be moved
with(database) {
execSQL("CREATE TABLE Users_Backup (id INTEGER, name TEXT, PRIMARY KEY (id))")
execSQL("INSERT INTO Users_Backup SELECT id, name FROM Users")
execSQL("DROP TABLE Users")
execSQL("ALTER TABLE Users_Backup RENAME to Users")
}
}
}
但是当我声明新的临时 table User_Backup 时,我不知道如何指定其中一个字段是数组。最后我能够使用 Room 的 AutoMigrations 并创建一个界面来做到这一点,但我也想知道如何以这种方式做到这一点。
简单的方法是编译代码 (Ctrl+F9) 并在 @Database 注释的实体列表中使用更改后的 @Entity 注释 classes。
然后查看生成的 java(通过 Android Studio 中的 Android 视图可见)。将有一个 class 与注释为 class 的 @Database 同名,但后缀为 _Impl.
在这个 class 中会有一个名为 createAllTables 的方法,这包括房间用于创建 tables.
只需复制并粘贴适当的 SQL,然后更改 table 名称,这不仅会使用正确的类型,还会应用 Room 期望的正确的列约束。
我会建议
- 在创建 table 之前添加一个
execSQL("DROP TABLE IF EXISTS the_backup_table_name;")
(以防万一它已经存在)
- 而不是使用
execSQL("DROP TABLE Users")
使用 execSQL("DROP TABLE IF EXISTS the_original_table_name")
- 就我个人而言,我总是重命名原始的 table 名称,然后重命名新的 table,最后删除重命名的原始名称。
我会使用:-
private val MIGRATION_3_2 = object : Migration(3,2) {
override fun migrate(database: SupportSQLiteDatabase) {
//Drop column isn't supported by SQLite, so the data must manually be moved
with(database) {
execSQL("DROP TABLE IF EXISTS Users_Backup")
execSQL("CREATE TABLE IF NOT EXISTS ....) //<<<<< SEE NOTES BELOW, the SQL MUST BE CHANGED.
execSQL("INSERT INTO Users_Backup SELECT id, name FROM Users")
execSQL("ALTER TABLE Users RENAME TO Old_Users")
execSQL("ALTER TABLE Users_Backup RENAME to Users")
execSQL("DROP TABLE IF EXISTS Old_users")
}
}
}
- note ....表示SQL是从生成的java复制过来的,table名字是从用户到Users_Backup
- 第一行将删除 Uers_backup 以防万一它恰好存在,它在异常情况下失败的可能性较小。
- 而不是在 Users_Backup 的重命名之前删除 Users table 到 用户。第 4 个 execSQL 更改了 Users table 的名称,因此更改 Users_Backup[ 是否应该存在问题? =48=] table 成为 Users table,那么原始 Uers table 可用作 Old_users.
- 当所有都完成后,原来的用户 table,现在名为 Old_Users,然后被删除。
- 这些都只是一点safer/secure.
我在 Android 中有一个数据库,我从中删除了一列。我正在做迁移,我发现它不像对已删除的列进行 DROP 那样简单。
然后我看到我必须采取一系列步骤,创建一个临时 table,稍后将成为新的 table 与删除的列,但问题是这个 table 包含一个字符串数组字段,我不知道如何在 SQL 中声明。
@Entity(tableName = "recipe_table")
data class RecipesDb(
@PrimaryKey
@ColumnInfo(name = "id")
val id: Long,
@ColumnInfo(name = "name")
val name: String,
@ColumnInfo(name = "category")
val category: List<String>,
@ColumnInfo(name = "isRecommended")
val isRecommended: Boolean,
@ColumnInfo(name = "images")
val images: List<String>,
@ColumnInfo(name = "ingredients")
val ingredients: List<String>,
@ColumnInfo(name = "date")
val date: Long,
@ColumnInfo(name = "time")
val time: Int,
@ColumnInfo(name = "difficult")
val difficult: String,
@ColumnInfo(name = "originalUrl")
val originalURL: String? = null,
@ColumnInfo(name = "author")
val author: String,
@ColumnInfo(name = "siteName")
val siteName: String
)
现在我删除了 ingredients
列。我想做这样的事情:
private val MIGRATION_3_2 = object : Migration(3,2) {
override fun migrate(database: SupportSQLiteDatabase) {
//Drop column isn't supported by SQLite, so the data must manually be moved
with(database) {
execSQL("CREATE TABLE Users_Backup (id INTEGER, name TEXT, PRIMARY KEY (id))")
execSQL("INSERT INTO Users_Backup SELECT id, name FROM Users")
execSQL("DROP TABLE Users")
execSQL("ALTER TABLE Users_Backup RENAME to Users")
}
}
}
但是当我声明新的临时 table User_Backup 时,我不知道如何指定其中一个字段是数组。最后我能够使用 Room 的 AutoMigrations 并创建一个界面来做到这一点,但我也想知道如何以这种方式做到这一点。
简单的方法是编译代码 (Ctrl+F9) 并在 @Database 注释的实体列表中使用更改后的 @Entity 注释 classes。
然后查看生成的 java(通过 Android Studio 中的 Android 视图可见)。将有一个 class 与注释为 class 的 @Database 同名,但后缀为 _Impl.
在这个 class 中会有一个名为 createAllTables 的方法,这包括房间用于创建 tables.
只需复制并粘贴适当的 SQL,然后更改 table 名称,这不仅会使用正确的类型,还会应用 Room 期望的正确的列约束。
我会建议
- 在创建 table 之前添加一个
execSQL("DROP TABLE IF EXISTS the_backup_table_name;")
(以防万一它已经存在) - 而不是使用
execSQL("DROP TABLE Users")
使用execSQL("DROP TABLE IF EXISTS the_original_table_name")
- 就我个人而言,我总是重命名原始的 table 名称,然后重命名新的 table,最后删除重命名的原始名称。
我会使用:-
private val MIGRATION_3_2 = object : Migration(3,2) {
override fun migrate(database: SupportSQLiteDatabase) {
//Drop column isn't supported by SQLite, so the data must manually be moved
with(database) {
execSQL("DROP TABLE IF EXISTS Users_Backup")
execSQL("CREATE TABLE IF NOT EXISTS ....) //<<<<< SEE NOTES BELOW, the SQL MUST BE CHANGED.
execSQL("INSERT INTO Users_Backup SELECT id, name FROM Users")
execSQL("ALTER TABLE Users RENAME TO Old_Users")
execSQL("ALTER TABLE Users_Backup RENAME to Users")
execSQL("DROP TABLE IF EXISTS Old_users")
}
}
}
- note ....表示SQL是从生成的java复制过来的,table名字是从用户到Users_Backup
- 第一行将删除 Uers_backup 以防万一它恰好存在,它在异常情况下失败的可能性较小。
- 而不是在 Users_Backup 的重命名之前删除 Users table 到 用户。第 4 个 execSQL 更改了 Users table 的名称,因此更改 Users_Backup[ 是否应该存在问题? =48=] table 成为 Users table,那么原始 Uers table 可用作 Old_users.
- 当所有都完成后,原来的用户 table,现在名为 Old_Users,然后被删除。
- 这些都只是一点safer/secure.