1

PostgreSQL 9.3 データベースを使用する Qt アプリケーションにウィンドウがあります。ウィンドウは、新しいデータを表示、編集、および挿入するために使用されるフォームです。次のようになります。 ここに画像の説明を入力

そのビューには3つのSQLテーブルからのデータがあります。テーブルは外部キーに関連付けられています。

  • 請負業者 (メイン テーブル) - 「個人データ」セクションにマッピング
  • 連絡先 (contractors.ID への外部キーがあります)
  • アドレス (contractors.ID への外部キーがあります)

したがって、私のウィンドウのクラスには3つのメインモデルがあります(+「個人データ」セクションと「アドレスデータ」セクションにテーブルを転置するための2つのプロキシモデル)。QSqlTableModelこれらのセクションと、 contactData セクションに使用しQSqlRelationalTableModelます。そのウィンドウを「通常」(一部の請負業者を表示するために)開くときは、単に請負業者の ID をコンストラクタに渡し、それを適切な変数に保存します。QSqlTableModel::​setFilter(const QString & filter)また、モデルごとにメソッドを呼び出し、適切なフィルタリングを設定します。そのウィンドウを「新規追加」モードで開くと、「-1」または「0」の値を ID 変数に渡すだけなので、データがモデルにロードされません。3 つのモデルすべてにQSqlTableModel::OnManualSubmit editStrategyがあります。データを保存すると (適切なボタンをクリックしてトリガーされます)、トランザクションを開始します。そして、モデルを 1 つずつ提出します。personalData挿入後にモデルの PK を取得する必要があるため (他のモデルの FK フィールドに設定するため)、モデルが最初に送信されます。モデルの送信が失敗した場合、QSqlError コンテンツを含む messageBox を表示し、トランザクションをロールバックしますメソッドから戻ります。処理中の最初のモデルでエラーが発生した場合、何も挿入されていないので問題ありません。しかし、最初のモデルが保存されたが、2 番目または 3 番目のモデルが失敗した場合、少し問題があります。そのため、前と同じようにトランザクションをロールバックし、関数から戻ります。しかし、データを修正して再度送信した後、ロールバックがあったことを認識せず、データを再度挿入する必要があるため、最初のモデルは送信しようとしません。そのようなモデルを再度提出する必要があることを通知するには、どうすればよいでしょうか? 現時点では、次のようなものになりました。

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 の間に 1 つのトランザクションがあります。コードのある時点で問題が発生した場合、すべてのデータを問題なくロールバックします。

ロールバックに関して外部システムへの特定の機能(ロールバック)の制御を処理するため(データベースで発生しています)、現在行っていることは適切な設計ではありません。外部システムはそれを行うように設計されていませんが、反対側のデータベースはロールバックとトランザクションを処理するように作成および設計されています。とても上手です。この機能は非常に複雑であり、データベースの外部で再構築および再発明するには、多くの問題が必要です。データベース内で関数を使用する場合と同じ完璧なロールバック処理が得られることはありません。

各システムに最適な機能を実行させます。

私は以前にあなたの問題に会ったことがあり、私の場合はHibernateを使用してこの問題を解決するために同じ考えを持っていました. 私が自分の努力から離れて、状況を再評価するまで。データベースのロールバック メカニズムに取り組んでいる 3 つのチームがあります。1. データベース自体のソース コードを書いている男女、2. Hibernate コードを書いている男女、そして 3. 私です。最初のチームは、優れたロールバック メカニズムの作成に専念しています。彼らが失敗した場合、彼らは悪い製品を持っています。彼らは成功しました。2 番目のチームは、適切なロールバック メカニズムの作成に専念しています。彼らの製品は、非常に複雑な状況で機能していないときでも失敗しません。最後のチームである私は、この問題に専念していません。チーム 2 またはチーム 1 の作業に基づいて、チーム 1 のレベルに到達できなかったチーム 2 またはチーム 1 の人々よりも優れたソリューションを作成できるのは誰でしょうか? そこで、代わりにデータベース関数を使用することにしました。

于 2015-02-12T01:16:22.777 に答える