Flutter Drift复合唯一键失效 insertOrReplace误替换数据问题
配置错误点
你的表定义存在核心配置疏漏,直接导致两类异常:
- Drift的Table类默认规则:如果没有显式覆写primaryKey getter,会自动将名为
id的IntColumn识别为表的单字段主键,主键本身自带全局唯一约束。你虽然手动配置了{id, order}复合唯一键,但系统自动生成的id单字段主键约束依然存在,两个唯一约束同时生效。 - 第一类异常的原因:SQLite的insertOrReplace(对应REPLACE冲突策略)只要命中任意唯一/主键约束冲突,就会删除触发冲突的旧记录。插入同id不同order的记录时,会直接命中
id单主键的唯一约束,自动删除同id的旧记录,和order字段的值无关。 - 第二类异常的原因:只要新插入记录的id和已有记录重复,哪怕id+order的组合完全不重复,也会触发
id单主键的唯一约束报错,和复合唯一键的校验结果无关。 - 额外冗余问题:复合主键本身已经自带联合唯一约束,不需要再重复在
uniqueKeys里配置相同的字段集合,会生成冗余的索引约束。
修复方案
根据业务场景二选一即可:
方案1:直接使用复合主键(适合无外部表关联Parts主键的场景)
显式覆写primaryKey,将id和order设为复合主键,去掉冗余的uniqueKeys配置即可,修正后的表定义代码:
@DataClassName('Part') class Parts extends Table { IntColumn get id => integer()(); TextColumn get startDate => text().withLength(min: 17, max: 17)(); TextColumn get endDate => text().withLength(min: 0, max: 17)(); TextColumn get observations => text()(); IntColumn get order => integer().customConstraint('NOT NULL REFERENCES orders (id)')(); @override Set<Column> get primaryKey => {id, order}; }
方案2:保留独立自增主键+复合唯一键(适合Parts表需要被其他表外键关联的场景)
新增独立的自增主键字段,打破Drift对id字段的默认主键识别,保留原本的复合唯一键配置即可,修正后的表定义代码:
@DataClassName('Part') class Parts extends Table { // 新增独立自增主键,Drift检测到显式主键后不会再把id字段设为默认主键 IntColumn get partId => integer().autoIncrement()(); IntColumn get id => integer()(); TextColumn get startDate => text().withLength(min: 17, max: 17)(); TextColumn get endDate => text().withLength(min: 0, max: 17)(); TextColumn get observations => text()(); IntColumn get order => integer().customConstraint('NOT NULL REFERENCES orders (id)')(); @override List<Set<Column>> get uniqueKeys => [ {id, order} ]; }
生效注意事项
修改表结构后必须更新数据库的schema版本号,编写对应的表迁移逻辑;如果还在开发调试阶段、没有线上存量数据,直接卸载应用清空旧数据库缓存即可生效,不需要写迁移。
修复后两类异常都会消失:
- insertOrReplace仅在
id和order两个字段同时和已有记录重复时,才会替换旧记录,同id不同order的记录可以正常共存。 - 普通插入操作仅在
id+order组合和已有记录重复时,才会抛出唯一约束冲突错误,不会出现无冲突误报错的问题。
内容的提问来源于stack exchange,提问作者trikimg
相关产品推荐
相关产品推荐

