Room:如何自动迁移新表的数据库?

lii*_*iil 5 android android-room

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

我的房间版本是:2.4.1

根据https://medium.com/androiddevelopers/room-auto-migrations-d5370b0ca6eb,使用自动迁移这项任务应该很容易。

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

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

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

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

一切都编译得很好。应用程序运行直到第一次与数据库交互。

然后,应用程序崩溃了,我在日志中看到以下内容:

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=[]}
Run Code Online (Sandbox Code Playgroud)

我不知道该怎么办。“预期”和“发现”之间的差异是怎么回事?我原本希望 room 能够简单地看到新表,从中生成一个方案并创建表。像这一次一样编写 SQL 语句进行简单迁移的时代还没有结束吗?

Mik*_*keT 1

“预期”和“发现”之间的差异是怎么回事?

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

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

报告发现TableInfo{name='FlashCardRunEvent', columns={}, foreignKeys=[], indices=[]}表明FlasCardRunEvent表在自动迁移后不存在。

我原本希望 room 能够简单地看到新表,从中生成一个方案并创建表。像这一次一样编写 SQL 语句进行简单迁移的时代还没有结束吗?

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

一切都编译得很好。

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

为什么 Room 不创建表

一个推测:-

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

  • 也许尝试添加 3-4 中的自动迁移,然后运行。

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

在此输入图像描述

这应该是这样的:-

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`))");
  }
}
Run Code Online (Sandbox Code Playgroud)
  • 您只需要单个 execSQL,因为您只是添加单个表。

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

从上面可以看出,代码是从测试派生的,尽管初始表不同,但迁移 2-3 添加了 FlashCardRunEvent 表(通过根据预期构建实体类,即:-

@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
)
Run Code Online (Sandbox Code Playgroud)

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

运行测试并创建 FlasCardRunEvent 表:-

在此输入图像描述