我试图在事务范围内获取插入行的主键,因为我不想让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();
}
答案 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可以正确处理事务,但是如果仅看解决方案,您可能会忘记它。