如何使用QSqlQuery获取事务中先前插入的行的ID

时间:2012-12-10 14:11:05

标签: qt transactions qtsql

我试图在事务范围内获取插入行的主键,因为我不想让db处于逻辑上不一致的状态。

我的问题是我找不到一种方法来检索先前执行的查询的ID值,我想将其用于下一个插入查询。在事务生效时查询PostgreSQL数据库在非外键表中没有显示结果(该行尚未提交?)。我认为这是由于交易的隔离级别。

以下是我正在尝试使用生产代码进行的操作,尽管已进行了轻微编辑,但为了清晰起见,缩小为一个功能。 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();
}

2 个答案:

答案 0 :(得分:5)

我意识到这是一个老问题但是在Qt 5.10(可能更早)中有一个函数QSqlQuery::lastInsertId()可以在QSqlQuery::exec()之后调用。

如果您使用的数据库(如SQLite)不支持RETURNING语句中的INSERT子句,则非常有用。

QSqlQuery::lastInsertId() documentation.

用法与以下内容类似:

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

qDebug() << "Last ID was:" << q.lastInsertId();

答案 1 :(得分:0)

@Kasheen是完全正确的,但我想将重点放在您应该牢记的一个重要方面:不要忘记将所有内容封装在一个交易中

为什么?如果使用生成的主键(q.lastInsertId()获得)的第二次插入失败,则可以节省时间并避免数据库损坏。

因此,您的插入内容应如下所示(这基本上是@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可以正确处理事务,但是如果仅看解决方案,您可能会忘记它。