我在使用 PostgreSQL 9.3 数据库的 Qt 应用程序中有一个窗口。窗口是用于显示、编辑和插入新数据的窗体。t 看起来像这样:
我在该视图中有来自 3 个 sql 表的数据。这些表与外键相关:
- 承包商(主表) - 映射到“个人数据”部分
- 联系人(有外键承包商.ID)
- 地址(有外键承包商.ID)
所以 - 在我的窗口类中,我有 3 个主要模型(+ 2 个代理模型来转置“个人数据”和“地址数据”部分中的表)。我QSqlTableModel
用于这些部分,以及QSqlRelationalTableModel
用于contactData 部分。当“正常”打开该窗口(查看某些承包商)时,我只需将承包商的 ID 传递给构造函数并将其存储在适当的变量中。QSqlTableModel::setFilter(const QString & filter)
此外,我为每个模型调用该方法,并设置适当的过滤。在“添加新”模式下打开该窗口时,我只需将“-1”或“0”值传递给 ID 变量,因此不会将数据加载到模型中。所有 3 个模型都有QSqlTableModel::OnManualSubmit
editStrategy。保存数据时(通过单击适当的按钮触发),我开始交易。然后我一个接一个地提交模型。personalData
首先提交模型,因为我需要在插入后获取它的PK(在其他模型的FK字段中设置)。当模型提交失败时,我显示一个带有 QSqlError 内容的消息框,回滚事务并从方法返回。当我正在处理的第一个模型出现错误时 - 没问题,因为没有插入任何内容。但是当第一个模型被保存,但第二个或第三个失败时 - 有一个小问题。所以我像以前一样回滚事务,并从函数返回。但是在更正数据并再次提交之后——第一个模型并没有尝试提交——因为它不知道有回滚,需要再次插入数据。什么是注意到这种模型需要再次提交的好方法?此刻我最终得到了类似的东西:
void kontrahenciSubWin::on_btnContractorAdd_clicked() {
//QStringList errorList; // when error occurs in one model - whole transacion gets broken, so no need for a list
QString error;
QSqlDatabase db = QSqlDatabase::database();
//backup the data - in case something fails and we have to rollback the transaction
QSqlRecord personalDataModelrec = personalDataModel->record(0); // always one row. will get erased by SubmitAll, as no filter is set, because I don't have its ID.
QList<QSqlRecord> contactDataModelRecList;
for (int i = 0 ; i< contactDataModel->rowCount(); i++) {
contactDataModelRecList.append( contactDataModel->record(i) );
}
QList<QSqlRecord> addressDataModelRecList;
for (int i = 0 ; i< addressDataModel->rowCount(); i++) {
addressDataModelRecList.append( addressDataModel->record(i) );
}
db.transaction();
if ( personalDataModel->isDirty() && error.isEmpty() ) {
if (!personalDataModel->submitAll()) //submitAll calls select() on the model, which destroys the data as the filter is invalid ("where ID = -1")
//errorList.append( personalDataModel->lastError().databaseText() );
error = personalDataModel->lastError().databaseText();
else {
kontrahentid = personalDataModel->query().lastInsertId().toInt(); //only here can I fetch ID
setFilter(ALL); //and pass it to the models
}
}
if ( contactDataModel->isDirty() && error.isEmpty() )
if (!contactDataModel->submitAll()) //slot on_contactDataModel_beforeInsert() sets FK field
//errorList.append( contactDataModel->lastError().databaseText() );
error = contactDataModel->lastError().databaseText();
if ( addressDataModel->isDirty() && error.isEmpty() )
if (!addressDataModel->submitAll()) //slot on_addressDataModel_beforeInsert() sets FK field
//errorList.append( addressDataModel->lastError().databaseText() );
error = addressDataModel->lastError().databaseText();
//if (!errorList.isEmpty()) {
// QMessageBox::critical(this, tr("Data was not saved!"), tr("The following errors occured:") + " \n" + errorList.join("\n"));
if (!error.isEmpty()) {
QMessageBox::critical(this, tr("Data was not saved!"), tr("The following errors occured:") + " \n" + error);
db.rollback();
personalDataModel->clear();
contactDataModel->clear();
addressDataModel->clear();
initModel(ALL); //re-init models: set table and so on.
//re-add data to the models - backup comes handy
personalDataModel->insertRecord(-1, personalDataModelrec);
for (QList<QSqlRecord>::iterator it = contactDataModelRecList.begin(); it != contactDataModelRecList.end(); it++) {
contactDataModel->insertRecord(-1, *it);
}
for (QList<QSqlRecord>::iterator it = addressDataModelRecList.begin(); it != addressDataModelRecList.end(); it++) {
addressDataModel->insertRecord(-1, *it);
}
return;
}
db.commit();
isInEditMode = false;
handleGUIOnEditModeChange();
}
有没有人有更好的主意?我怀疑在尝试插入记录之前是否可以省略备份记录。但也许有更好的方法将它们“重新添加”到模型中?我尝试使用"setRecord"
, 和"remoweRows" & "insertRecord"
组合,但没有运气。重置整个模型似乎最简单(我只需要重新初始化它,因为它会在清除时丢失表、过滤器、排序和其他所有内容)