table 上的房间异常:预打包的数据库具有无效的架构

Room exception on table: Pre-packaged database has an invalid schema

我的任务是将我们应用程序的当前架构(使用 Cupboard)迁移到 Room,我在第一项工作中遇到了一些问题,即迁移数据库对象,用 Java 编写现在(Cupboard 只支持 Java),让它们与 Room 一起工作。这是一个例子:

public class ItemDb {

public Long _id;
public String type;
public String subtype;
public long scheduledTime;
public int iteration;
public String data1;
public String data2;
public String data3;

这是它在数据库中的样子:

我对这个 entity 所做的是在 Kotlin 中创建一个 @Entity class 以在 Room 中使用,它看起来像这样:

@Entity(tableName = "ItemDb")
data class ItemDb(
        @PrimaryKey(autoGenerate = true)
        val _id: Long? = 0,
        val type: String,
        val subtype: String,
        val scheduledTime: Long,
        val iteration: Int,
        val data1: String? = null,
        val data2: String? = null,
        val data3: String? = null
)

每当我尝试查询数据库时,我都会收到此异常:

Pre-packaged database has an invalid schema: ItemDb(com.example.room.model.items.ItemDb). Expected: TableInfo{name='ItemDb', columns={scheduledTime=Column{name='scheduledTime', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, subtype=Column{name='subtype', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, data3=Column{name='data3', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, data2=Column{name='data2', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, data1=Column{name='data1', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, iteration=Column{name='iteration', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, _id=Column{name='_id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}, type=Column{name='type', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]} Found: TableInfo{name='ItemDb', columns={scheduledTime=Column{name='scheduledTime', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, subtype=Column{name='subtype', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, data3=Column{name='data3', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, data2=Column{name='data2', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, data1=Column{name='data1', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, iteration=Column{name='iteration', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, _id=Column{name='_id', type='integer', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}, type=Column{name='type', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}

通过比较两者,我可以看到有些字段应该被标记为 notNull 但它们似乎被标记为 nullable 而不是 即使 Kotlin class 通过避免使用可为空的 ?.

将字段声明为 notNull

我做错了什么? 如果您需要有关数据库架构的更多信息,我可以轻松提供。谢谢!

在错误消息中,"expected" 模式是 Room 根据 ItemDb 的 Kotlin 声明创建的。它正确地将 typesubTypescheduledTimeiteration 显示为非空列,所有其他列都可为空。

我没有使用过 Cupboard,我假设它会根据您的 ItemDb Java 声明创建一个数据库。 "found" 模式报告预先打包的 Cupboard 数据库的模式,表明当 Cupboard 创建数据库时,所有列都声明为可空(即没有 NOT NULL)。

要使用预先打包的数据库,您需要perform a migration step,这将创建一个新的table,并为适用字段指定NOT NULL。然后,您需要将预打包数据库中 ItemDb table 中的所有行复制到新的 table,并重命名一些 table(参见下面的示例)。

新 table 的创建语句将是这样的:

CREATE TABLE new_ItemDb (_id INTEGER PRIMARY KEY AUTOINCREMENT,
    type INTEGER NOT NULL, subType INTEGER NOT NULL, scheduledTime INTEGER NOT NULL,
    iteration INTEGER NOT NULL, data1 TEXT, data2 TEXT, data3 TEXT)

迁移操作可以仿照从 documentation for migrations:

复制的示例
// Migration from 2 to 3, Room 2.2.0
val MIGRATION_2_3 = object : Migration(2, 3) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("""
                CREATE TABLE new_Song (
                    id INTEGER PRIMARY KEY NOT NULL,
                    name TEXT,
                    tag TEXT NOT NULL DEFAULT ''
                )
                """.trimIndent())
        database.execSQL("""
                INSERT INTO new_Song (id, name, tag)
                SELECT id, name, tag FROM Song
                """.trimIndent())
        database.execSQL("DROP TABLE Song")
        database.execSQL("ALTER TABLE new_Song RENAME TO Song")
    }
}

在花费时间进行上述代码更改之前,您可以通过临时更改 ItemDb 的 Kotlin 定义以使所有字段都可为空,然后重试使用预先打包的数据库来确认我的分析。我希望导入成功。