使用 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 期望的正确的列约束。

我会建议

  1. 在创建 table 之前添加一个 execSQL("DROP TABLE IF EXISTS the_backup_table_name;")(以防万一它已经存在)
  2. 而不是使用 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.