Room迁移唯一约束冲突求助:为每行生成唯一UUID
解决数据库迁移时UUID重复导致的唯一约束错误
你的问题根源在于:${UUID.randomUUID()}是在Kotlin代码编译SQL字符串时仅生成一次,最终所有插入行的id都会使用同一个UUID,直接触发主键唯一约束报错。以下是几种可行的修复方案:
方案1:使用SQLite内置UUID生成函数(推荐)
如果你的SQLite版本在3.31.0及以上(Room 2.3.0+默认适配的版本满足此要求),可以直接在SQL语句中调用uuid()函数,为每一行生成独立的UUID:
修改插入逻辑的代码:
val sql = "INSERT INTO $MIGRATION_TABLE_CODES (id, text, format, type, date, note, isFavorite) " + "SELECT uuid(), text, format, type, date, note, isFavorite " + "FROM $MIGRATION_TABLE_CODES_OLD"
该函数会为每一行返回唯一的标准UUID字符串,完全满足主键唯一性要求。
方案2:兼容低版本SQLite的替代方案
若你的SQLite版本不支持uuid()函数,可使用hex(randomblob(16))生成32位十六进制随机字符串作为唯一ID:
val sql = "INSERT INTO $MIGRATION_TABLE_CODES (id, text, format, type, date, note, isFavorite) " + "SELECT hex(randomblob(16)), text, format, type, date, note, isFavorite " + "FROM $MIGRATION_TABLE_CODES_OLD"
生成的字符串虽不符合UUID的标准格式,但具备同等唯一性,可正常作为主键使用。
方案3:逐行读取插入(Kotlin层面生成UUID)
如果上述SQL方案无法适配,可通过游标遍历旧表数据,在代码循环中为每一行生成独立UUID后插入新表:
// 开启事务提升批量插入性能 database.beginTransaction() try { val cursor = database.query("SELECT text, format, type, date, note, isFavorite FROM $MIGRATION_TABLE_CODES_OLD") while (cursor.moveToNext()) { val text = cursor.getString(cursor.getColumnIndexOrThrow("text")) val format = cursor.getInt(cursor.getColumnIndexOrThrow("format")) val type = cursor.getInt(cursor.getColumnIndexOrThrow("type")) val date = cursor.getLong(cursor.getColumnIndexOrThrow("date")) val note = cursor.getString(cursor.getColumnIndexOrThrow("note")) val isFavorite = cursor.getInt(cursor.getColumnIndexOrThrow("isFavorite")) val insertSql = "INSERT INTO $MIGRATION_TABLE_CODES (id, text, format, type, date, note, isFavorite) " + "VALUES (?, ?, ?, ?, ?, ?, ?)" database.execSQL(insertSql, arrayOf( UUID.randomUUID().toString(), text, format, type, date, note, isFavorite )) } cursor.close() database.setTransactionSuccessful() } finally { database.endTransaction() }
此方式性能略低于纯SQL方案,适合数据量较小的场景。
内容的提问来源于stack exchange,提问作者TOvchinnikova
相关产品推荐
相关产品推荐

