使用新的尝试 - 以资源块上的SQLException事务回滚(Transaction rollba

2019-08-19 00:40发布

我有尝试,与资源问题,我问的只是要确定。 我可以使用它,如果我需要在异常反应,我还需要在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使用旧的,甚至可以根据Oracle文档 - “渔获物和finally块在try-与资源声明,任何catch或finally块的资源后运行宣布已经关闭“。

Answer 1:

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

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

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(真); 应该在finally子句来完成。 这将意味着,外层的try-与资源应该是一个传统的try-catch-finally程序。

编辑(2018)

我看到人们对这个评论是,所以我想我应该给它一个2018的答复。 我不是在Java中工作了,主要是在Scala中,Clojure中和科特林一直在努力,而这种代码没有经过测试,所以请将此视为只是另一个例子。 然而,由于Java有lambda表达式,我觉得有以下做法是好了很多。 而且我在其他语言做过类似的事情在生产代码。

在这种方法中有一个在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,如果这是它的初始值。 因此,如果从池中它是假的,它不应该被感动。

在尝试与 - 资源一锤定音。 我不认为这是一个很好的抽象,所以我会在更复杂的情况下使用它要小心。



Answer 2:

在你的代码,正赶上“的SQLException”执行自动提交复位。 任何一种运行时异常的(就像一个空指针异常)将泡沫从您的代码,无需重新设置自动提交。

试戴与资源语法使编译器生成一些精彩的代码来覆盖所有的执行路径,并与通过关门所有抑制异常跟上。 一对夫妇辅助类的,你可以插入提交/回滚和复位自动提交到代码生成过程:

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();
    } 


Answer 3:

    //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
        }
    }


Answer 4:

在上面的例子中,我认为这是更好地把con.commit()嵌套try-catch ,因为它也可以抛出SQLException

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

我们在生产环境未关闭会话过这样的问题。



文章来源: Transaction rollback on SQLException using new try-with-resources block