如何在C++ Builder中为关联TDBGrid的SQLite TDataSource添加记录?
解决C++ Builder中SQLite添加记录时的数据库锁定问题
核心原因
SQLite是单文件型数据库,同一时间仅支持一个写入操作。当绑定DBGrid的TFDQuery处于打开状态时,数据集会持续持有数据库的读/写锁,此时直接调用ExecSQL或AppendRecord会因锁冲突触发数据库锁定错误。
解决方案一:使用数据集编辑模式(推荐)
通过TDataSet的原生方法添加记录,确保操作流程规范,同时自动同步DBGrid显示:
bool __fastcall TSqLiteDataModule::AddEntry(const sEntryData *entrydata) { try { // 确保数据集处于激活状态 if (!FDQuery1->Active) FDQuery1->Open(); FDQuery1->Append(); // 进入追加模式 // 为字段赋值(自增列无需手动设置,SQLite会自动生成) FDQuery1->FieldByName("Icon")->AsInteger = entrydata->uIcon; FDQuery1->FieldByName("EntryName")->AsString = entrydata->EntryName; FDQuery1->FieldByName("Notes")->AsString = entrydata->Notes; FDQuery1->Post(); // 提交新记录 FDQuery1->Refresh(); // 刷新数据集,让DBGrid显示新增内容 return true; } catch (Exception &e) { ShowMessage("添加失败:" + e.Message); // 若处于编辑/插入状态,取消未提交的操作 if (FDQuery1->State == dsEdit || FDQuery1->State == dsInsert) FDQuery1->Cancel(); return false; } }
解决方案二:独立执行SQL语句(避开数据集锁)
如果偏好直接用ExecSQL插入,需临时关闭数据集释放锁,操作完成后重新打开:
bool __fastcall TSqLiteDataModule::AddEntry(const sEntryData *entrydata) { try { // 记录数据集原状态,操作后恢复 bool wasActive = FDQuery1->Active; if (wasActive) FDQuery1->Close(); // 执行插入SQL FDConnection->ExecSQL( "INSERT INTO Entries(Icon, EntryName, Notes) VALUES (:Icon, :EntryName, :Notes)", OPENARRAY(Variant, (entrydata->uIcon, entrydata->EntryName, entrydata->Notes)) ); // 恢复数据集状态,刷新DBGrid if (wasActive) FDQuery1->Open(); return true; } catch (Exception &e) { ShowMessage("添加失败:" + e.Message); return false; } }
额外优化建议
- 连接参数调整:在
TFDConnection的Params中设置LockingMode=NORMAL、SharedCache=False,减少因缓存或锁模式导致的冲突。 - 事务包裹批量操作:如果有多次插入需求,用事务缩短锁持有时间:
FDConnection->StartTransaction(); try { // 执行批量插入逻辑 FDConnection->Commit(); } catch(...) { FDConnection->Rollback(); throw; } - 自增列处理:无需在
AppendRecord中传入0作为自增列值,直接跳过该列即可,SQLite会自动生成序列值。
内容的提问来源于stack exchange,提问作者user3161924
相关产品推荐
相关产品推荐

