【问题标题】:How do I update the Room version in adnroid?如何在 android 中更新 Room 版本?
【发布时间】:2021-07-09 08:23:37
【问题描述】:

我的房间数据库如下

@Database(entities = [UserEntity::class], version = 6)
abstract class RoomDB : RoomDatabase() {
    abstract fun getDao(): RoomDao

    companion object {
        private var INSTANCE: RoomDB? = null

        val MIGRATION_5_6 = object : Migration(5, 6) {
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL(
                    "CREATE TABLE my_table_new (" +
                            "id INTEGER NOT NULL, " +
                            "name TEXT, " +
                            "age INTEGER NOT NULL, " +
                            "PRIMARY KEY(id))"
                )

                database.execSQL("INSERT INTO my_table_new (id, name, age) SELECT id, name, 0 FROM my_table")

                database.execSQL("DROP TABLE my_table")

                database.execSQL("ALTER TABLE my_table_new RENAME TO my_table")
            }
        }

        fun getInstance(context: Context): RoomDB {
            if (INSTANCE == null) {
                synchronized(RoomDB::class) {
                    INSTANCE = Room.databaseBuilder(
                        context.applicationContext,
                        RoomDB::class.java,
                        "user.db"
                    )
                        .addMigrations(MIGRATION_5_6)
                        .build()
                }
            }

            return INSTANCE!!
        }

    }
}

之前的版本是5,这次升级的版本是6。在版本5中没有age列,但是在版本6中新增了。所以如果你看INSERT INTO的声明,你可以看到数字0是强行插入的。从代码中可以看出,我们还编写了 MIGRATION 代码。道是:

@Dao
interface RoomDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insert(entity: UserEntity)

    @Delete
    fun delete(entity: UserEntity)

    @Query("SELECT * FROM my_table")
    fun getUser(): List<UserEntity>

    @Query("DELETE FROM my_table")
    fun deleteAll()
}

实体看起来像这样:

@Entity(tableName = "my_table")
data class UserEntity(
    @ColumnInfo(name = "id")
    @PrimaryKey var id: Int,
    @ColumnInfo(name = "name")
    var name: String,
    @ColumnInfo(name = "age")
    var age: Int
)

当我在这里调用 getUser() 时,我得到以下异常。

java.lang.IllegalStateException: Room 无法验证数据完整性。看起来您已更改架构但忘记更新版本号。您可以通过增加版本号来解决此问题。

我到底应该如何升级 Room 版本?是不是我做错了什么?

【问题讨论】:

  • 您好,您是否在应用的生产版本上测试了迁移?
  • 您是指在 Play 商店上架的生产版本吗?此应用程序尚未发布到生产环境。有一些建议说你可以删除并重新安装正在测试的应用程序,但我想要的是升级现有的数据库。
  • 如果您的应用未投入生产,那么您可以使用 RoomBuilder 中的 fallbackToDestructiveMigration 方法。
  • fallbackToDestructiveMigration 函数被称为永久删除现有数据并创建新表的函数。在我目前正在测试的 Room Database 中,在版本 5 中输入了一些没有age 列的数据。如果我使用你提到的功能,我在版本 5 中创建的数据会消失吗?如果是这样,那不是我想要的。

标签: android database android-room


【解决方案1】:

我刚刚测试了您的代码并且迁移运行但新表的架构不正确,您将收到 IllegalStateException:Migration did not proper handle: my_table

有:-

 Expected:
    TableInfo{name='my_table', columns={name=Column{name='name', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, age=Column{name='age', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='null'}}, foreignKeys=[], indices=[]}
     Found:
    TableInfo{name='my_table', columns={name=Column{name='name', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='null'}, age=Column{name='age', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)

如果您查看 Expected: 部分(根据实体的架构),name 列的定义 notNull=true 但 name Found: 部分中的列有 notNull=false。

架构必须匹配,因此您需要将 CREATE TABLE 更改为使用 "name TEXT NOT NULL, " BUT FINISH READING

我使用以下测试/检测上述内容(最初创建没有年龄列的 v5),然后使用 v6。

database.execSQL(
                    "CREATE TABLE my_table_new (" +
                            "id INTEGER NOT NULL, " +
                            "name TEXT, " + //<<<<<<<<<< incorrect 
                            "age INTEGER NOT NULL, " +
                            "PRIMARY KEY(id))"
                )
                Log.d("MIG5_6", "Migration has been invoked - Inserting rows from original")
                database.execSQL("INSERT INTO my_table_new (id, name, age) SELECT id, name, 0 FROM my_table")
                Log.d("MIG5_6", "Migration has been invoked - Dropping Original Table")
                database.execSQL("DROP TABLE my_table")
                Log.d("MIG5_6", "Migration has been invoked - Renaming new table to original table")
                database.execSQL("ALTER TABLE my_table_new RENAME TO my_table")

包含的日志:-

2021-04-14 21:20:41.502 D/MIG5_6: Migration has been invoked - Creating table to be renamed
2021-04-14 21:20:41.503 D/MIG5_6: Migration has been invoked - Inserting rows from original
2021-04-14 21:20:41.503 D/MIG5_6: Migration has been invoked - Dropping Original Table
2021-04-14 21:20:41.504 D/MIG5_6: Migration has been invoked - Renaming new table to original table

我不确定你是如何达到你所拥有的状态的,但我怀疑你现在有版本 6(它存储在数据库头文件中)并且它正在看到架构更改并希望你转到版本 7 .

您现在有点问题,因为表很可能已更改,但 name 列没有 NOT NULL。

您可以做的是从 5 迁移到 7(或者如果数据库为 V6,则可能是 6-7),然后将版本更改为 7。此迁移与 5 到 6 相同,除了根据:-

,名称列不为空
    val MIGRATION_5_7 = object : Migration(5, 7) {
        override fun migrate(database: SupportSQLiteDatabase) {
            Log.d("MIG5_7", "Migration has been invoked - Creating table to be renamed")
            database.execSQL(
                "CREATE TABLE my_table_new (" +
                        "id INTEGER NOT NULL, " +
                        "name TEXT  NOT NULL, " + //<<<<<<<<<< Correct
                        "age INTEGER NOT NULL, " +
                        "PRIMARY KEY(id))"
            )
            Log.d("MIG5_7", "Migration has been invoked - Inserting rows from original")
            database.execSQL("INSERT INTO my_table_new (id, name, age) SELECT id, name, 0 FROM my_table")
            Log.d("MIG5_7", "Migration has been invoked - Dropping Original Table")
            database.execSQL("DROP TABLE my_table")
            Log.d("MIG5_7", "Migration has been invoked - Renaming new table to original table")
            database.execSQL("ALTER TABLE my_table_new RENAME TO my_table")
        }
    }

当然,如果您有能力丢失数据,只需卸载应用程序并重新运行,就不需要迁移,而是使用生成的架构。

生成的架构

如果您在 Android Studio 中使用 Android View,您会在里面看到 java(generated)(总是先编译,然后再查看以获取最新版本),您将能够在其中找到 RoomDB_Impl。在其中您将看到一个方法 createAllTables,它具有用于创建表的 SQL,并且是预期的 SQL。

使用生成代码中的 SQL 应确保架构正确。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-30
    • 2018-07-02
    相关资源
    最近更新 更多