Room数据库迁移表结构不匹配,如何修复该问题?
问题详情
迁移时触发Room表结构校验失败,预期与实际role表结构对比:
Expected:
TableInfo{name='role', columns={title=Column{name='title', type='TEXT', affinity='2', 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='role', columns={id=Column{name='id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}, title=Column{name='title', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
核心差异:
- 预期中
id和title均带NOT NULL约束,实际表中两者均无该约束 - 打印出的列顺序不一致(此为无关干扰项)
当前使用的实体类:
@TypeConverters(RoomConverters::class) @Entity(tableName = "role") class RoleRoom( @PrimaryKey val id: Int = 0, val title: String )
迁移类中的建表SQL:
database.execSQL("CREATE TABLE IF NOT EXISTS `role` (`id` INTEGER, `title` TEXT, PRIMARY KEY(`id`))")
问题原因
- 非空约束不匹配:Kotlin中
Int、无默认值的String都是非空类型,Room会自动为这些字段生成NOT NULL约束;但迁移SQL未声明该约束,导致实际表结构与预期不符。 - 列顺序干扰:
TableInfo中的columns是无序Map,打印顺序由Map遍历逻辑决定,和实体类属性顺序、实际表列顺序无关,不会引发校验失败。
修复方案
1. 修正新建表的SQL
如果是首次创建该表,直接修改迁移SQL,添加非空约束:
database.execSQL("CREATE TABLE IF NOT EXISTS `role` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, PRIMARY KEY(`id`))")
2. 修复已存在的旧表
如果旧表已经创建且数据已存在,需先确保表中id和title字段无NULL值,再执行ALTER语句添加约束:
-- 先处理NULL数据(示例:将NULL替换为默认值) database.execSQL("UPDATE `role` SET `id` = 0 WHERE `id` IS NULL") database.execSQL("UPDATE `role` SET `title` = '' WHERE `title` IS NULL") -- 添加非空约束 database.execSQL("ALTER TABLE `role` MODIFY COLUMN `id` INTEGER NOT NULL") database.execSQL("ALTER TABLE `role` MODIFY COLUMN `title` TEXT NOT NULL")
3. 长期注意事项
后续编写迁移SQL时,必须严格对齐实体类的字段约束:
- 非空类型(Kotlin不带
?的类型)对应SQL的NOT NULL - 主键字段默认带
NOT NULL约束,无需额外声明但也可明确写出 - 默认值需和实体类的默认参数保持一致
内容的提问来源于stack exchange,提问作者chrisChris

