【问题标题】:pqxx reuse / reactivate a work transactionpqxx 重用/重新激活工作事务
【发布时间】:2014-10-20 11:01:44
【问题描述】:

我想将 pqxx::work 用于多个查询和承诺,而 commit 功能阻止我再次使用它。 这是一个简单的例子:

pqxx::connection G_connexion("dbname=basetest user=usertest password=1234");
pqxx::work G_work(G_connexion);

int main(int argc, char* argv[]) {
    G_work.exec("insert into test.table1(nom) VALUES('foo');");
    G_work.commit();//until here, no problem
    G_work.exec("insert into test.table1(nom) VALUES('bar');"); //error, transaction already closed
    G_work.commit();
}

当我尝试插入 'bar' 值时,在提交后,我得到一个 pqxx::usage_error : Error executing query. Attempt to activate transaction<READ COMMITTED> which is already closed

提交更改后如何避免关闭连接?我可以使用 G_work=pqxx::work(G_connexion) 或其他的后续等效项来重置 G_work 吗? 此外,一个错误的请求不应该使整个进程崩溃,而只是一个正在处理的请求(G_work 在失败后仍然可用)。

我必须保留相同的变量 G_Work,因为它将是一个从程序中很多地方调用的全局变量。

【问题讨论】:

  • 你找到答案了吗?
  • 没有。由于似乎没有人有答案,我正在为我的软件中的每个类添加一个数据库类,并在需要时通过方法传递它。一个独特的全局事务太危险了,因为如果它失败了我就无法重新激活它。

标签: c++ postgresql libpqxx


【解决方案1】:

pqxx::work 只是一个pqxx::transaction<>,它的大部分逻辑最终都来自pqxx::transaction_base

这个类不打算为多个事务服务。相反,它适用于 try/catch 块中的单个事务。它有一个状态成员变量 (m_Status),即使在提交之后也不会重新初始化。

正常模式是:

{
    pqxx::work l_work(G_connexion);
    try {
        l_work.exec("insert into test.table1(nom) VALUES('foo');");
        l_work.commit();
    } catch (const exception& e) {
        l_work.abort();
        throw;
    }
}

可以说,libpqxx 可以在删除时回滚事务(以完全避免 try/catch),但事实并非如此。

这似乎不适合您的使用模式,因为您希望 G_work 成为可从程序中的多个位置访问的全局变量。请注意,pqxx::work 不是连接对象的类,而只是一种使用 C++ 异常处理封装开始/提交/回滚的方法。

尽管如此,libpqxx 还允许您在事务之外(或至少在 libpqxx 管理的事务之外)执行语句。您应该使用 pqxx::nontransaction 类的实例。

#include "pqxx/nontransaction"

pqxx::connection G_connexion("dbname=basetest user=usertest password=1234");
pqxx::nontransaction G_work(G_connexion);

int f() {
    G_work.exec("insert into test.table1(nom) VALUES('foo');");
    G_work.exec("insert into test.table1(nom) VALUES('bar');");
}

请注意,这相当于:

#include "pqxx/nontransaction"

pqxx::connection G_connexion("dbname=basetest user=usertest password=1234");

int f() {
    pqxx::nontransaction l_work(G_connexion);
    l_work.exec("insert into test.table1(nom) VALUES('foo');");
    l_work.exec("insert into test.table1(nom) VALUES('bar');");
}

最终,没有什么能阻止您使用pqxx::nontransaction 管理交易。如果您想要savepoints,则尤其如此。如果您的事务旨在超出函数范围(例如在全局范围内),我还建议使用 pqxx::nontransaction

#include "pqxx/nontransaction"

pqxx::connection G_connexion("dbname=basetest user=usertest password=1234");
pqxx::nontransaction G_work(G_connexion);

int f() {
    G_work.exec("begin;");
    G_work.exec("insert into test.table1(nom) VALUES('foo');");
    G_work.exec("savepoint f_savepoint;");
    // If the statement fails, rollback to checkpoint.
    try {
        G_work.exec("insert into test.table1(nom) VALUES('bar');");
    } catch (const pqxx::sql_error& e) {
        G_work.exec("rollback to savepoint f_savepoint;");
    }
    G_work.exec("commit;");
}

【讨论】:

  • 好的,但是使用 pqxx::nontransaction 类会丢失我想要保留的事务部分。确实我想要检查点之类的东西:当软件达到某些重要点时,它可以提交/回滚,然后继续使用相同的全局连接。
  • 您可以使用 pqxx::nontransaction 自己管理事务。我相应地编辑了答案。
  • 好吧我已经想到了,即使使用 pqxx::transaction 我也能够使用 exec("commit"); exec("begin") 而不是 G_work.begin(),解决部分问题的方法。但是我仍然需要能够在一个请求失败的情况下重置连接而不重置软件。例如,如果由于某些限制我无法插入一行,我必须能够继续进行其他操作。
  • 这正是 SAVEPOINT 的目的。见postgresql.org/docs/current/static/sql-savepoint.html
  • 好的! G_work 的非事务类可防止在基础上发生非法操作时断开连接,并且回滚适合我的检查点,这对我有用。感谢您的帮助保罗
猜你喜欢
  • 2011-08-26
  • 1970-01-01
  • 1970-01-01
  • 2013-07-19
  • 2017-06-24
  • 2018-02-27
  • 1970-01-01
  • 2020-07-19
  • 2020-02-13
相关资源
最近更新 更多