PSQLException: 当前事务已中止,命令将被忽略直到事务块结束。

272

我在 JBoss 7.1.1 Final 的 server.log 文件中看到以下被截断的堆栈跟踪:

Caused by: org.postgresql.util.PSQLException: 
ERROR: current transaction is aborted, commands ignored until end of 
transaction block

at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2102)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1835)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:257)
at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:512)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:374)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeUpdate(AbstractJdbc2Statement.java:302)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [rt.jar:1.6.0_23]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) [rt.jar:1.6.0_23]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) [rt.jar:1.6.0_23]
at java.lang.reflect.Method.invoke(Method.java:597) [rt.jar:1.6.0_23]
at org.postgresql.ds.jdbc23.AbstractJdbc23PooledConnection$StatementHandler.invoke(AbstractJdbc23PooledConnection.java:455)
at $Proxy49.executeUpdate(Unknown Source)   at org.jboss.jca.adapters.jdbc.WrappedStatement.executeUpdate(WrappedStatement.java:371)
at org.infinispan.loaders.jdbc.TableManipulation.executeUpdateSql(TableManipulation.java:154) [infinispan-cachestore-jdbc-5.1.2.FINAL.jar:5.1.2.FINAL]
... 154 more

检查Postgres日志文件会发现以下语句:

STATEMENT:  SELECT count(*) FROM ISPN_MIXED_BINARY_TABLE_configCache
ERROR:  current transaction is aborted, commands ignored until end of transaction block
STATEMENT:  CREATE TABLE ISPN_MIXED_BINARY_TABLE_configCache(ID_COLUMN VARCHAR(255) NOT NULL, DATA_COLUMN BYTEA, TIMESTAMP_COLUMN BIGINT, PRIMARY KEY (ID_COLUMN))
ERROR:  relation "ispn_mixed_binary_table_configcache" does not exist at character 22

我正在使用JBoss 7.1.1 Final附带的Infinispan,版本是5.1.2.Final。

所以我认为发生了以下情况:

  • Infinispan尝试运行SELECT count(*)...语句,以查看ISPN_MIXED_BINARY_TABLE_configCache中是否有记录;
  • 由于某种原因,Postgres不喜欢这个语句。
  • Infinispan忽略了这个问题,并继续执行CREATE TABLE语句。
  • Postgres出现错误,因为它仍然认为它是同一个事务,而Infinispan未能回滚此事务,并且从第一个SELECT count(*)...语句开始便出现问题。

这个错误是什么意思?有什么解决方法吗?


如果你像我一样在搜索上述PSQLException: current transaction is aborted... (25P02),或许还包括JPAHibernate,那么请注意了。最终我们发现是因为我们(很好的!)使用了Logback,并且将一个重载了toString()方法的DAO对象传递给它,导致了这个错误,并且被很好地吞掉了(但不幸的是我没有注意到):log.info( "bla bla: {}", obj )产生了**bla bla: [FAILED toString()]**。将其更改为log.info( "bla bla: {}", String.valueOf( obj )可以使其具有空值安全性,但不会将其吞掉,从而保留了事务并在不相关的查询失败时失败。 - Andreas Covidiot
我遇到了相同类型的错误。在执行 SQL 之前,我必须释放连接。我的代码是 connection.commit()。 - md. ariful ahsan
我在这里回答了 https://dev59.com/1nA85IYBdhLWcg3wBOzh#65172183 并且它有效! - Zvi
1
有两种方法可以解决这个问题。1)运行ROLLBACK; 这将解决当前选项卡的问题,2)打开一个新选项卡并运行相同的命令。尝试在运行查询时添加try catch。 - althaf a s
22个回答

313

我在使用Java和PostgreSQL进行表格插入时遇到了这个错误。我将说明如何重现此错误:

org.postgresql.util.PSQLException: ERROR: 
current transaction is aborted, commands ignored until end of transaction block

摘要:

您出现此错误的原因是,您输入了一个事务并且其中一个SQL查询失败了,您吞掉了这个失败并忽略了它。但这还不够,然后您使用同一连接使用相同的事务运行另一个查询。异常在第二个正确形成的查询上抛出,因为您正在使用破损的事务来执行其他工作。默认情况下,PostgreSQL会阻止您这样做。

我使用的是:PostgreSQL 9.1.6 on x86_64-redhat-linux-gnu, compiled by gcc (GCC) 4.7.2 20120921 (Red Hat 4.7.2-2), 64-bit"。

我的PostgreSQL驱动程序是:postgresql-9.2-1000.jdbc4.jar

使用的Java版本是:Java 1.7

以下是表创建语句,以说明异常:

CREATE TABLE moobar
(
    myval   INT
);

Java程序引发错误:

public void postgresql_insert()
{
    try  
    {
        connection.setAutoCommit(false);  //start of transaction.
        
        Statement statement = connection.createStatement();
        
        System.out.println("start doing statement.execute");
        
        statement.execute(
                "insert into moobar values(" +
                "'this SQL statement fails, and it " +
                "is gobbled up by the catch, okfine'); ");
     
        //The above line throws an exception because we try to cram
        //A string into an Int.  I Expect this, what happens is we gobble 
        //the Exception and ignore it like nothing is wrong.
        //But remember, we are in a TRANSACTION!  so keep reading.

        System.out.println("statement.execute done");
        
        statement.close();
        
    }
    catch (SQLException sqle)
    {
        System.out.println("keep on truckin, keep using " +
                "the last connection because what could go wrong?");
    }
    
    try{
        Statement statement = connection.createStatement();
        
        statement.executeQuery("select * from moobar");

        //This SQL is correctly formed, yet it throws the 
        //'transaction is aborted' SQL Exception, why?  Because:
        //A.  you were in a transaction.
        //B.  You ran a SQL statement that failed.
        //C.  You didn't do a rollback or commit on the affected connection.
        
    }
    catch (SQLException sqle)
    {
        sqle.printStackTrace();
    }   

}

上述代码对我产生了以下输出:
start doing statement.execute

keep on truckin, keep using the last connection because what could go wrong?

org.postgresql.util.PSQLException: 
  ERROR: current transaction is aborted, commands ignored until 
  end of transaction block

解决方法:

你有几个选项:

  1. 最简单的解决方案:不要处于事务中。将connection.setAutoCommit(false);设置为connection.setAutoCommit(true);。它有效是因为失败的SQL语句只是被忽略了。你可以随便失败任何SQL语句,PostgreSQL不会阻止你。

  2. 继续在事务中,但当检测到第一个SQL失败时,回滚/重新开始或提交/重新开始事务。然后你就可以继续在该数据库连接上失败尽可能多的SQL查询。

  3. 不要捕获和忽略抛出的异常,当SQL语句失败时程序将停止在格式错误的查询上。

  4. 使用Oracle,当你在事务中失败了一个连接上的查询并继续使用该连接时,Oracle不会抛出异常。

为了保护PostgreSQL这样做的决定......Oracle让你变得软弱,让你做傻事并忽视它。


22
哈哈,选项4真是个笑话... 我在Oracle中进行了相当多的开发,并最近开始使用Postgres... 真的很烦人,Postgres会这样做,现在我们不得不重新编写我们从Oracle迁移到Postgres的大部分程序。为什么没有像第一个选项那样的选项,使它的行为像Oracle一样,但不自动提交 - ADTC
3
经过一些尝试后发现选项2是最接近Oracle行为的。如果需要执行多个更新操作,而一个失败不应该停止后续的更新操作,当捕获到 SQLException 时,只需在 Connection 上调用 rollback() 即可。[_无论如何_,我意识到这与PostgreSQL的哲学相一致,强制用户将所有内容都显式地表示出来,而Oracle的哲学则是隐式地处理大量事务。] - ADTC
4
“选项2”包含了不可能的分支or commit/restart the transaction。据我所见,在异常后没有提交的方式。当我尝试提交时,PostgreSQL执行回滚(rollback)。 - turbanoff
1
我可以确认@turbanoff提出的问题。这个问题也可以通过直接使用psql来重现。(1) 开始一个事务,(2) 发出一些有效的语句,(3) 发出一个无效的语句,(4) 提交--> psql将回滚而不是提交。 - Alphaaa
4
这是一个关于这个话题的有趣讨论。如果这个问题是由约束违规引起的,PostgreSQL开发人员建议预先检查冲突(在更新/插入之前查询)或使用“savepoints”回滚到更新/插入之前的点。参见https://dev59.com/d2kv5IYBdhLWcg3wtTG7#28640557获取示例代码。 - Gili
显示剩余2条评论

46

在导致当前事务被中止的语句之前,检查输出。这通常意味着数据库抛出了一个异常,而你的代码忽略了它,现在期望下一个查询返回一些数据。

因此,你现在有一个状态不匹配,你的应用程序认为一切都很好,而数据库要求你回滚并从头重新开始事务。

在这种情况下,你应该捕获所有异常并回滚事务。

这里是类似的问题。


太好了,但在这种情况下,进行通信的是第三方库Infinispan而不是我的代码与Postgres。 - Jimidy
情况仍然如此——事务必须回滚。也许检查一下您正在使用的库是否有更新版本,或者在其错误跟踪器中提出问题。如果您找到了导致问题的确切SQL,则可以使用PostgreSQL可扩展性来消除该问题。 - vyegorov
看来你已经证实了我的怀疑 - 我现在要查看Infinispan 5.1.2的源代码。 - Jimidy
公平地说,在TableManipulation类中,尝试运行select count(*)时有一个try-catch...也许Postgres驱动程序没有抛出预期的异常之一。我将连接调试器到JBoss以尝试找出更多信息。 - Jimidy
涉及的Infinispan代码建议在此错误中提出:https://issues.jboss.org/browse/ISPN-1204?page=com.atlassian.jira.plugin.system.issuetabpanels:changehistory-tabpanel我已经将调试器附加到正在运行的JBoss 7.1.1实例上,并且Postgres在正确的位置抛出异常。也许是JdbcUtil.safeClose()语句没有发挥作用。我将与Infinispan提出这个问题。 - Jimidy

26

我认为最好的解决方案是使用java.sql.Savepoint

在执行可能会throw SQLException的查询之前,使用方法Connection.setSavepoint()。如果抛出异常,您只需要回滚到此保存点,而不是整个事务。

示例代码:

Connection conn = null;
Savepoint savepoint = null;
try {
    conn = getConnection();
    savepoint = conn.setSavepoint();
    //execute some query
} catch(SQLException e) {
    if(conn != null && savepoint != null) {
        conn.rollback(savepoint);
    }
} finally {
   if(conn != null) {
      try {
          conn.close();
      } catch(SQLException e) {}

   }
}

我不小心点了踩,直到现在才注意到。这并不是有意的,除非回答被编辑,否则我无法撤销。 - cerberos
1
保存点方式是实际的解决方案。在我的PHP、Doctrine2和Postgres(9.5)环境中也适用。谢谢。 - helvete

19

3
在我看来,这应该是这个问题的被接受的答案。此外,除了设置“autosave=always”之外,建议还设置“cleanupSavepoints=true”,以避免共享缓冲区耗尽。 - v3nM
运行得非常好。将连接字符串放入 jdbc://localhost:5432/xxxx?autosave=always 中。 - dian mushkov

16
尝试执行这个COMMIT;命令,我在pgadmin4中运行过,可能有帮助。它与先前的命令过早停止有关。

1
解决了我的问题。 - Frankie Drake

6
在Ruby on Rails PG中,我创建了一个迁移,迁移了我的数据库,但是忘记重新启动开发服务器。我重新启动了服务器,然后它就正常工作了。

这也是我的情况。我认为应该是一些愚蠢的问题,因为我并没有尝试做任何复杂的事情。 - Tashows

6

我在我的数据库应用程序工具中遇到了这个错误,因为有一个未提交的事务。而我正在尝试运行一个查询操作,这就导致了这个错误。你可以通过运行以下命令来解决这个问题:

commit;

或者

rollback;

5

你需要回滚。JDBC Postgres驱动程序非常糟糕。但是,如果你想保留你的事务,并仅回滚该错误,你可以使用保存点:

try {
_stmt = connection.createStatement();
_savePoint = connection.setSavepoint("sp01");
_result = _stmt.executeUpdate(sentence) > 0;
} catch (Exception e){
 if (_savePoint!=null){
 connection.rollback(_savePoint);
}
}

点击这里查看更多信息:

http://www.postgresql.org/docs/8.1/static/sql-savepoint.html


(注:该页面为英文原版,需要翻墙才能访问)

5

出现此错误的原因是在错误操作之前存在其他数据库,导致当前数据库操作无法执行。


3
问题已在Infinispan 5.1.5.CR1中得到解决:ISPN-2023

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接