当前位置: 首页 > 知识库问答 >
问题:

使用新的try with resources块对SQLException进行事务回滚

广昊昊
2023-03-14

我在尝试资源方面有问题,我只是想确认一下。如果我需要对异常做出反应,并且我仍然需要catch块中的资源,我可以使用它吗?示例如下:

try (java.sql.Connection con = createConnection())
{
    con.setAutoCommit(false);
    Statement stm = con.createStatement();
    stm.execute(someQuery); // causes SQLException
}
catch(SQLException ex)
{
    con.rollback();
    // do other stuff
}

我担心在这种情况下,我仍然注定要使用旧的try-catch-最终,即使根据甲骨文留档-“在try-with-资源语句中,任何一抓或最终块都是在声明的资源被关闭后运行的。”

共有3个答案

宗政永望
2023-03-14
    //try with resources
    try(Connection conn = this.connectionProvider.getConnection()){//auto close BEFORE reach this , catch block, so we need a inner try block for statement
        boolean oldAutoCommit=conn.getAutoCommit();
        conn.setAutoCommit(false);//auto commit to false
        try(
            Statement stm = con.createStatement()
        ){
            stm.execute(someQuery); // causes SQLException
            conn.commit();//commit
        }
        catch (SQLException ex){
            conn.rollback();//error, rollback
            throw ex;//If you need to throw the exception to the caller
        }
        finally {
            conn.setAutoCommit(oldAutoCommit);//reset auto commit
        }
    }
华易安
2023-03-14

在代码中,您正在捕获“SQLException”以执行自动提交重置。任何类型的运行时异常(如空指针异常)都会从代码中冒泡出来,而不会重置自动提交。

try-with-resource语法使编译器生成一些精彩的代码来覆盖所有执行路径,并通过关闭来跟上所有被抑制的异常。通过几个助手类,您可以将提交/回滚和重置自动提交插入到代码生成过程中:

import java.sql.SQLException;
import java.sql.Connection;

public class AutoRollback implements AutoCloseable {

    private Connection conn;
    private boolean committed;

    public AutoRollback(Connection conn) throws SQLException {
        this.conn = conn;        
    }

    public void commit() throws SQLException {
        conn.commit();
        committed = true;
    }

    @Override
    public void close() throws SQLException {
        if(!committed) {
            conn.rollback();
        }
    }

}

public class AutoSetAutoCommit implements AutoCloseable {

    private Connection conn;
    private boolean originalAutoCommit;

    public AutoSetAutoCommit(Connection conn, boolean autoCommit) throws SQLException {
        this.conn = conn;
        originalAutoCommit = conn.getAutoCommit();
        conn.setAutoCommit(autoCommit);
    }

    @Override
    public void close() throws SQLException {
        conn.setAutoCommit(originalAutoCommit);
    }

}

现在您可以使用“尝试使用资源”语法控制回滚和自动提交,如下所示:

    try(Connection conn = getConnection(),
        AutoSetAutoCommit a = new AutoSetAutoCommit(conn,false),
        AutoRollback tm = new AutoRollback(conn)) 
    {

        // Do stuff

        tm.commit();
    } 
隆璞
2023-03-14

根据语言规范,连接将在执行catch子句之前关闭(http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.20.3.2).

一种可能的解决方案是嵌套try-with-资源语句:

try (java.sql.Connection con = createConnection())
{
    con.setAutoCommit(false);
    try (Statement stm = con.createStatement())
    {
        stm.execute(someQuery); // causes SQLException
    }
    catch(SQLException ex)
    {
        con.rollback();
        con.setAutoCommit(true);
        throw ex;
    }
    con.commit();
    con.setAutoCommit(true);
}

希望这说明了这一点。如果您计划在生产代码中使用它,这应该会得到很大的改进。

例如,如果您使用的是连接池,那么您必须在获得连接时返回连接,因此con.setAutoCommit(true);应该在最终子句中完成。这意味着外部的try-with-资源应该是传统的try-cat-Final。

编辑(2018)

我仍然看到人们对此发表评论,所以我想我会在2018年给它一个回复。我不再使用Java了,主要是在Scala、Clojure和Kotlin中工作,这段代码还没有经过测试,所以请将其视为另一个示例。然而,由于Java有lambdas,我认为下面的方法要好得多。我在其他语言的生产代码中也做过类似的事情。

在这种方法中,有一个inTransaction函数处理所有讨厌的事务。但是用法非常简单。

public class Foo {

    interface ConnectionProvider {
        Connection get() throws SQLException;
    }

    public static <A> A doInTransation(ConnectionProvider connectionProvider, Function<Connection, A> f) throws SQLException {
        Connection connection = null;
        A returnValue;
        boolean initialAutocommit = false;
        try {
            connection = connectionProvider.get();
            initialAutocommit = connection.getAutoCommit();
            connection.setAutoCommit(false);
            returnValue = f.apply(connection);
            connection.commit();
            return returnValue;
        } catch (Throwable throwable) {
            // You may not want to handle all throwables, but you should with most, e.g.
            // Scala has examples: https://github.com/scala/scala/blob/v2.9.3/src/library/scala/util/control/NonFatal.scala#L1
            if (connection != null) {
                connection.rollback();
            }
            throw throwable;
        } finally {
            if (connection != null) {
                try {
                    if(initialAutocommit){
                        connection.setAutoCommit(true);
                    }
                    connection.close();
                } catch (Throwable e) {
                    // Use your own logger here. And again, maybe not catch throwable,
                    // but then again, you should never throw from a finally ;)
                    StringWriter out = new StringWriter();
                    e.printStackTrace(new PrintWriter(out));
                    System.err.println("Could not close connection " + out.toString());
                }
            }
        }
    }

    public static void main(String[] args) throws SQLException {
        DataSource ds = null;

        // Usage example:
        doInTransation(ds::getConnection, (Connection c) -> {
            // Do whatever you want in a transaction
            return 1;
        });
    }
}

我希望有一些经过战斗测试的库为你做这些事情,至少在其他语言中是这样的。

我看到一些关于自动提交和连接池的评论。上述示例应该不知道连接来自何处,也不知道连接是否来自池,也就是说,只有当连接的初始值为true时,才将其设置回true。因此,如果从池中得出的结果是错误的,则不应触摸它。

关于资源尝试的最后一句话。我不认为这是一个很好的抽象,所以我会小心地在更复杂的场景中使用它。

 类似资料:
  • 问题内容: 我在尝试资源时遇到问题,我只是想确定一下。如果我需要对异常做出反应并且仍然需要catch块中的资源,可以使用它吗?给出的示例是这样的: 我担心即使在oracle文档中,我仍然注定要在这种情况下最终使用旧的try-catch-finally-“在try-with- resources语句中捕获并最终阻止,任何catch或finally块都在资源之后运行宣布已关闭。” 问题答案: 根据语言

  • 下面是一个使用JdbcTemplate的示例。我的问题是,由于runTimeException,doMultipleCalls()在第3步失败,一切是否都会回滚。我只有对多个调用的事务注释,而没有对其他调用的注释。所有人是否共享同一个事务会话?。如果没有,那么如何跨多个服务器传递相同的事务会话?

  • 问题内容: 我正在使用TestNG 6.9.9来构建回归测试环境。但是遇到使用JUnit时从未遇到过的问题。在我看来,当每个测试用例完成时,如果测试方法在与调用方法相同的事务上下文中运行,则默认情况下将自动回滚每个数据的更改。但这似乎不是事实,而且我无法找出我的代码中是否有任何错误。请帮帮我。pom.xml中的属性,指示框架的版本 显然,它们都是最新的。 我的测试课: 创建一个实体实例,然后调用c

  • 问题内容: 有没有一种方法可以不使用ROLLBACK来回滚到事务的先前状态? 信息: ROLLBACK TRANSACTION请求没有相应的BEGIN TRANSACTION。 任何输入都会有很大的帮助。 谢谢 !!! 问题答案: 要在明确将IMPLICIT_TRANSACTIONS设置为ON时扩展gerrytans答案,可以使用ROLLBACK。请参阅与此相关的MSDN doco 。请注意,这不

  • 上下文2。XML 这会导致将抽象类的transactionManager属性设置为服务器启动期间创建的最后一个注释事务方面bean'org.springframework.transaction.interceptor.TransactionAspectSupport。 因此,如果实例化的最后一个bean是dataSource1,则在dataSource1上创建事务,否则在DataSource2上

  • 我发现实际上是Spring,我能够设置一些工作。现在,我想使用Hibernate/JPA将导入的数据保存到数据库中,但我总是出现这样的错误: 我明白问题出在交易上。以下是和的spring java配置: 这是导致异常的代码: 有什么解决这个问题的想法吗? [编辑]我也放入了作业定义和步骤定义。我所有的Spring配置都是用Java编写的。 任何人都可以给出更多的见解,如何在spring Batch