【问题标题】:Room: How to automigrate DB for new table?房间:如何为新表自动迁移数据库?
【发布时间】:2023-01-31 22:26:56
【问题描述】:

我想对我的房间数据库做一个简单的更改:添加一个新表。

我的房间版本是:2.4.1

根据 https://medium.com/androiddevelopers/room-auto-migrations-d5370b0ca6eb 的说法,使用自动迁移应该可以轻松完成此任务。

这是我的数据库类的相关部分在迁移之前的样子:

@Database(
    entities = [FlashCard::class, Pool::class],
    version = 2
)
abstract class DB : RoomDatabase() {
 ...

对于迁移,我将该部分更改为:

@Database(
    entities = [FlashCard::class, Pool::class, FlashCardRunEvent::class],
    version = 3,
    autoMigrations = [
        AutoMigration (from = 2, to = 3)
    ]
)
abstract class DB : RoomDatabase() {
...

一切都很好。该应用程序一直运行到与数据库第一次交互为止。

然后,应用程序崩溃了,我在我的日志中得到了这个:

Caused by: java.lang.IllegalStateException: Migration didn't properly handle: FlashCardRunEvent(com.ravenala.flashy.room.FlashCardRunEvent).
     Expected:
    TableInfo{name='FlashCardRunEvent', columns={newBox=Column{name='newBox', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, flashCardId=Column{name='flashCardId', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, timeStampInSeconds=Column{name='timeStampInSeconds', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}, oldBox=Column{name='oldBox', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
     Found:
    TableInfo{name='FlashCardRunEvent', columns={}, foreignKeys=[], indices=[]}

我不知道该怎么做。 “预期”和“发现”之间的区别是什么?我曾希望 room 只会看到新表,从中生成一个方案并创建表。像这样写简单迁移的SQL语句的时代还没有结束吗?

【问题讨论】:

    标签: android android-room


    【解决方案1】:

    “预期”和“发现”之间的区别是什么

    预期是根据 Room 对通过 @Database 注释中的实体参数定义的类的处理的模式。

    Found 是打开数据库时在数据库中找到的模式,在您的情况下是在自动迁移之后。

    报告找到TableInfo{name='FlashCardRunEvent', columns={}, foreignKeys=[], indices=[]} 表示烧卡运行事件自动迁移后表不存在。

    我曾希望 room 只会看到新表,从中生成一个方案并创建表。像这样写简单迁移的SQL语句的时代还没有结束吗?

    从您提供的代码中看不出为什么。当然,AutoMigration 具有创建新表的能力(已经用一个简单的场景对此进行了测试)。

    一切都很好。

    您是否检查了构建日志是否有任何警告?这些,如果有的话,可能是正在发生的事情的线索。

    为什么 Room 没有创建表格

    一个推测:-

    您没有因为忘记某些东西(例如没有将 FlashCardRunEvent 实体添加到 @Database 注释中的实体列表中定义的实体)而无意中绕过迁移,从而将版本增加到版本 3;然后更正遗漏并在仍处于版本 3 时运行?

    • 或许尝试从 3-4 添加 AutoMigration,然后运行。

    否则我不确定。查看生成的 java(通过 Android View 可见),例如:-

    这应该是这样的:-

    class AppDatabase_AutoMigration_2_3_Impl extends Migration {
      public AppDatabase_AutoMigration_2_3_Impl() {
        super(2, 3);
      }
    
      @Override
      public void migrate(@NonNull SupportSQLiteDatabase database) {
        database.execSQL("CREATE TABLE IF NOT EXISTS `FlashCardRunEvent` (`id` INTEGER, `newBox` INTEGER NOT NULL, `flashCardId` INTEGER NOT NULL, `timeStampInSeconds` INTEGER NOT NULL, `oldBox` INTEGER NOT NULL, PRIMARY KEY(`id`))");
      }
    }
    
    • 您只希望添加单个 execSQL,因为您只是添加单个表。

    此外,在生成的 java 中,您会期望 createAllTables 方法的 DB_Impl 类中还包括 FlashCardRunEvent 表的 CREATE TABLE。

    从上面可以看出,代码是从测试派生的,尽管它使用不同的初始表,但是迁移 2-3 添加了 FlashCardRunEvent 表(通过从预期的) IE。 :-

    @Entity
    data class FlashCardRunEvent(
        /*
            TableInfo{name='FlashCardRunEvent',
            columns={
                newBox=Column{name='newBox', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'},
                flashCardId=Column{name='flashCardId', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'},
                timeStampInSeconds=Column{name='timeStampInSeconds', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'},
                id=Column{name='id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'},
                oldBox=Column{name='oldBox', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}},
                foreignKeys=[], indices=[]}
         */
        @PrimaryKey
        val id: Long?,
        val newBox: Long,
        val flashCardId: Long,
        val timeStampInSeconds: Long,
        val oldBox: Long
    )
    

    测试成功创建表。一个明显的区别是我在 @Database 注释中编码了 exportSchema = true 而不是依赖它默认为 true。

    运行测试并创建 FlasCardRunEvent 表:-

    【讨论】:

      【解决方案2】:

      您还可以检查您以前的 JSON 文件模式(在您的情况下为 1.json 和 2.json),并查看您新创建的表是否不存在。当您忘记在新功能开始时直接更新数据库版本时,就会发生这种情况。

      如果是这样,只需从那里删除表格来清理您的 JSON 文件。

      【讨论】:

        猜你喜欢
        • 2019-02-13
        • 2021-08-13
        • 2018-07-02
        • 2021-09-02
        • 1970-01-01
        • 1970-01-01
        • 2021-11-18
        • 2018-11-11
        • 1970-01-01
        相关资源
        最近更新 更多