You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Room数据库主键数据类型变更的迁移方案咨询

解决方案

要实现Log主键类型从String改为自增Int,同时按date排序生成新id且保留LogItem数据,需要通过临时表过渡+映射原外键关系的方式编写Room迁移脚本,避免外键CASCADE删除数据。以下是具体步骤:

1. 修改实体类

首先更新你的Kotlin实体类,匹配新的表结构:

@Entity
data class Log(
   @PrimaryKey(autoGenerate = true) val id: Int = 0, // 改为自增Int
   val date: Long,
   val note: String,
)

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = Log::class,
            parentColumns = ["id"],
            childColumns = ["logId"],
            onDelete = CASCADE,
        )
    ],
    indices = [Index("logId")]
)
data class LogItem(
   @PrimaryKey(autoGenerate = true) val id: Int = 0,
   val logId: Int, // 同步改为Int,匹配Log的新主键类型
   val name: String,
)

2. 编写Room迁移脚本

假设你的数据库当前版本是1,升级到2,添加如下Migration:

val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(database: SupportSQLiteDatabase) {
        // 1. 创建临时Log表(新结构)
        database.execSQL("""
            CREATE TABLE temp_log (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                date INTEGER NOT NULL,
                note TEXT NOT NULL
            )
        """.trimIndent())

        // 2. 按date排序插入原Log数据到临时表,生成自增id(从1开始)
        database.execSQL("""
            INSERT INTO temp_log (id, date, note)
            SELECT ROW_NUMBER() OVER (ORDER BY date ASC), date, note FROM Log
        """.trimIndent())

        // 3. 创建原id与新id的映射表
        database.execSQL("""
            CREATE TABLE log_id_mapping (
                old_id TEXT PRIMARY KEY,
                new_id INTEGER NOT NULL
            )
        """.trimIndent())
        database.execSQL("""
            INSERT INTO log_id_mapping
            SELECT id AS old_id, ROW_NUMBER() OVER (ORDER BY date ASC) AS new_id FROM Log
        """.trimIndent())

        // 4. 创建临时LogItem表(新结构,logId改为Int)
        database.execSQL("""
            CREATE TABLE temp_logitem (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                logId INTEGER NOT NULL,
                name TEXT NOT NULL
            )
        """.trimIndent())

        // 5. 迁移LogItem数据,替换logId为新的Int类型id
        database.execSQL("""
            INSERT INTO temp_logitem (id, logId, name)
            SELECT li.id, lim.new_id, li.name
            FROM LogItem li
            JOIN log_id_mapping lim ON li.logId = lim.old_id
        """.trimIndent())

        // 6. 删除原表
        database.execSQL("DROP TABLE Log")
        database.execSQL("DROP TABLE LogItem")

        // 7. 重命名临时表为正式表名
        database.execSQL("ALTER TABLE temp_log RENAME TO Log")
        database.execSQL("ALTER TABLE temp_logitem RENAME TO LogItem")

        // 8. 重建LogItem的索引(临时表不会继承原索引)
        database.execSQL("CREATE INDEX index_LogItem_logId ON LogItem(logId)")
    }
}

3. 注意事项

  • 确保迁移脚本中的SQL语句与SQLite语法兼容,ROW_NUMBER()需要SQLite 3.25.0及以上版本(Room默认集成的版本通常满足)。
  • 若原Log表中存在date相同的记录,ROW_NUMBER()会按原表顺序分配id,若需要更精确的排序规则,可在ORDER BY date ASC后追加其他字段(如note)。
  • 迁移过程中无需手动关闭外键约束,原外键会随原表删除而移除,重建后的表会带有新的外键约束。

内容的提问来源于stack exchange,提问作者Surendra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 12:12:16