7

dbを論理的に一貫性のない状態のままにしたくないため、トランザクションスコープ内に挿入された行の主キーを取得しようとしています。

私の問題は、以前に実行されたクエリのID値を取得する方法が見つからないことです。これは、次の挿入クエリに使用します。トランザクションが有効なときにPostgreSQLデータベースをクエリすると、非外部キーテーブルに結果が表示されません(行はまだコミットされていませんか?)。これは、トランザクションの分離レベルによるものだと思います。

以下は、わかりやすくするために少し編集して1つの関数に絞り込んだものですが、本番コードで実行しようとしていることです。const int lastInsertIdは常に0です。これは、このコンテキストでは値が見つからなかったことを意味します(技術的にはそのtoInt()関数は失敗しました)。LASTVAL()有効な非外部キー行を手動で挿入してから、期待される結果(挿入された行のID)を生成する呼び出しを試みました。

だから、私は何が間違っているのですか?私はここで何が欠けているか誤解していますか?

void createEntityWithoutForiegnKeyConstraint(const QString &nameOfEntity)
{
  db_.transaction();

  QSqlQuery insertQuery(db_);
  insertQuery.prepare("INSERT INTO \"EntityWithoutForeignKey\" (\"name\") VALUES (:name);");

  insertQuery.bindValue(":name", nameOfEntity);
  execQuery(__LINE__, insertQuery);
  QSqlQuery lastIdQuery("SELECT LASTVAL();", db_); // auto executes
  const int lastInsertId = lastIdQuery.value(0).toInt();

  if (lastInsertId <= 0) // 0 is not a valid ID
    throw exception("Oh noes.");

  createEntityWithForeignKeyConstraint(lastInsertId, someData);

  if (!db_.commit())
    db_.rollback();
}
4

2 に答える 2

2

@Kasheenは完全に正しいですが、覚えておくべき1つの重要な側面に焦点を当てたいと思います.1つのトランザクションにすべてをカプセル化することを忘れないでください.

なんで?生成された主キー ( で取得q.lastInsertId()) を使用した 2 回目の挿入が失敗した場合、時間を節約し、データベースの破損を回避します。

したがって、挿入は次のようになります(これは基本的に@Kasheenの回答にいくつか追加されています):

db_.transaction(); // Starts a transaction

QSqlQuery q;

// first insert
q.prepare("INSERT INTO table_name VALUES(:some_column_name)");
q.bindValue(":some_column_name", "FooBar");
q.exec();

auto pk == q.lastInsertId().toInt;

// second insert
q.prepare("INSERT INTO other_table VALUES(:other_column_name, :fk)");
q.bindValue(":other_column_name", "OtherText");
q.bindValue(":fk", pk);
q.exec();

db_.commit(); // Commits transaction

@average joe はトランザクション処理を正しく行いますが、ソリューションだけを見ていると忘れてしまう可能性があります。

于 2018-09-15T09:44:38.530 に答える