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

Posted

技术标签:

【中文标题】桌子上的房间异常:预打包的数据库具有无效的架构【英文标题】:Room exception on table: Pre-packaged database has an invalid schema 【发布时间】:2020-09-15 16:18:07 【问题描述】:

我的任务是将我们应用程序的当前架构(使用 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;

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

我对这个实体所做的是在 Kotlin 中创建一个 @Entity 类以在 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
)

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

预打包数据库的架构无效:ItemDb(com.example.room.model.items.ItemDb)。 预期的: TableInfoname='ItemDb', columns=scheduledTime=Columnname='scheduledTime', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null', subtype= Columnname='subtype', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null', data3=Columnname='data3', type='TEXT' ,affinity='2',notNull=false,primaryKeyPosition=0,defaultValue='null',data2=Columnname='data2',type='TEXT',affinity='2',notNull=false,primaryKeyPosition= 0,defaultValue='null',data1=Columnname='data1',type='TEXT',affinity='2',notNull=false,primaryKeyPosition=0,defaultValue='null',迭代=Column name='iteration',type='INTEGER',affinity='3',notNull=true,primaryKeyPosition=0,defaultValue='null',_id=Columnname='_id',type='INTEGER',affinity ='3',notNull=false,primaryKeyPosition=1,defaultValue='null',type=Columnname='type',type='TEXT',affinity='2',notNull=true,primaryKeyPosition=0, defaultValue='null',foreignKeys=[],indices=[] 成立: TableInfoname='ItemDb', columns=scheduledTime=Columnname='scheduledTime', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null', subtype= Columnname='subtype', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null', data3=Columnname='data3', type='TEXT' ,affinity='2',notNull=false,primaryKeyPosition=0,defaultValue='null',data2=Columnname='data2',type='TEXT',affinity='2',notNull=false,primaryKeyPosition= 0,defaultValue='null',data1=Columnname='data1',type='TEXT',affinity='2',notNull=false,primaryKeyPosition=0,defaultValue='null',迭代=Column name='iteration',type='INTEGER',affinity='3',notNull=false,primaryKeyPosition=0,defaultValue='null',_id=Columnname='_id',type='integer',affinity ='3',notNull=false,primaryKeyPosition=1,defaultValue='null',type=Columnname='type',type='TEXT',affinity='2',notNull=false,primaryKeyPosition=0, defaultValue='null',foreignKeys=[],indices=[]

通过比较两者,我可以看到一些字段应该被标记为notNull,但它们似乎被标记为nullable,而不是即使 Kotlin 类将这些字段声明为@987654328 @ 避免使用可以为空的?

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

【问题讨论】:

【参考方案1】:

在错误消息中,“预期”架构是 Room 根据您的 Kotlin 声明 ItemDb 创建的。它正确地将typesubTypescheduledTimeiteration 显示为非空列,而所有其他列都可以为空。

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

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

新表的创建语句是这样的:

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 定义以使所有字段都可以为空,然后重新尝试使用预打包的数据库来确认我的分析。我希望导入成功。

【讨论】:

感谢您为我指明正确的方向!解决方案是保持 Java 最初所做的事情(不使用任何 notNull 注释使所有字段成为 nullable)。然而,使用Migration 它也有助于我尊重满足请求的方向(有一个 Kotlin 类,它代表我们在应用程序中需要的对象,带有 nullablenot nullable 字段。)谢谢,超级有帮助!

以上是关于桌子上的房间异常:预打包的数据库具有无效的架构的主要内容,如果未能解决你的问题,请参考以下文章

例外:预打包的数据库具有无效的架构

有两张桌子的房间实体?

带有预填充数据库的房间

使用这些桌子阻塞房间

如何在房间数据库中预填充数据

创建表的标识符无效