能否向QAbstractItemModel.beginInsertRows()传入插入行数高估值?
重写QSqlTableModel.insertRows()时传入插入行数上限的风险与正确做法
结论先行
直接给beginInsertRows()传插入行数上限不合规,必然会引发后续的模型视图一致性问题,绝对不能这么做。
为什么不能这么做?
Qt的模型视图框架依赖beginInsertRows()和endInsertRows()传递的精确行变更信息来同步视图状态。如果通知的行数和实际成功插入的行数不符:
- 视图会渲染出不存在的空行,或者出现行索引错位,用户操作时容易触发崩溃
- 后续调用
rowCount()返回的数值和视图缓存的计数不匹配,导致数据显示混乱 - 模型生成的
QModelIndex可能指向无效数据,引发越界访问错误
正确处理流程
针对SQL约束导致的部分插入失败场景,重写insertRows()时必须按以下步骤来:
- 先执行插入并确认实际成功行数:
- 把待插入的数据批量提交到数据库,通过捕获SQL异常、查询表行计数变化,或者追踪
QSqlQuery的执行结果,确定实际成功插入的行数 - 比如可以先开启事务,执行批量插入后,查询当前表的总行数和插入前的差值,得到实际插入数
- 把待插入的数据批量提交到数据库,通过捕获SQL异常、查询表行计数变化,或者追踪
- 精确调用行变更通知:
- 只有拿到实际成功插入的行数后,再调用
beginInsertRows(parent, start, start + actual_rows - 1) - 完成数据同步后调用
endInsertRows(),确保通知的行数和实际变更完全一致
- 只有拿到实际成功插入的行数后,再调用
- 处理插入失败的行:
- 把插入失败的行(比如违反UNIQUE约束的记录)返回给调用者,或者记录错误日志,不要让模型的状态和数据库脱节
关键注意点
- 绝对不能在
beginInsertRows()之后再修改通知的行数,框架要求begin和end之间的操作必须严格对应通知的范围 - 批量插入时务必开启数据库事务,插入失败时回滚,避免出现部分插入导致的数据不一致
- 重写模型方法时必须遵守Qt的模型一致性原则:模型的行计数、索引结构必须和底层数据库的实际状态完全匹配
内容的提问来源于stack exchange,提问作者amateur3057
相关产品推荐
相关产品推荐

