1

我在使用 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"组合,但没有运气。重置整个模型似乎最简单(我只需要重新初始化它,因为它会在清除时丢失表、过滤器、排序和其他所有内容)

4

1 回答 1

1

我建议您使用用语言 PLPGSQL 编写的函数。它在 BEGIN 和 END 之间有一个事务。如果它在代码的某个点出错,那么它将完美地回滚所有数据。

您现在正在做的不是一个好的设计,因为您处理了与回滚相关的某个功能(回滚)到外部系统的控制(它发生在数据库中)。外部系统不是为此而设计的,而相反的数据库是为处理回滚和事务而创建和设计的。它非常擅长。在数据库之外重建和重新发明这个相当复杂的功能会带来很多麻烦。您将永远无法获得与使用数据库中的函数相同的完美回滚处理。

让每个系统尽其所能。

我之前遇到过您的问题,并且在我的情况下使用 Hibernate 解决了这个问题。直到我退出我的努力并重新评估情况。数据库的回滚机制有三个团队:1. 编写数据库本身源代码的男性和女性,2. 编写 Hibernate 代码的男性和女性,以及 3. 我。第一个团队致力于创建良好的回滚机制。如果他们失败了,他们的产品就不好。他们成功了。第二个团队致力于创建良好的回滚机制。他们的产品在非常复杂的情况下不工作时不会失败。最后一个团队,我,并没有专门解决这个问题。我是谁来编写一个比团队 2 或团队 1 的人更好的解决方案,基于团队 2 的工作,他们无法达到团队 1 的水平?那是我决定改用数据库函数的时候。

于 2015-02-12T01:16:22.777 回答