在Java 8中回滚()语句执行失败的最佳方法是什么?

时间:2016-11-24 11:52:16

标签: java jdbc transactions java-8

我正在用Java 8编写一个事务。首先,我的代码是这样的。

try (Connection conn = DAOUtil.getConnection();
     PreparedStatement ps = conn.prepareStatement(addSubscriptionSql)) {
    conn.setAutoCommit(false);
    //do work
    conn.commit();
} catch (SQLException e) {
    e.printStackTrace(); //handle error
}

但是因为我应该在事务失败的情况下回滚,所以我不得不像这样更改代码。请注意两个try块。

try (Connection conn = DAOUtil.getConnection()) {
    try (PreparedStatement ps = conn.prepareStatement(addSubscriptionSql)) {
        conn.setAutoCommit(false);
        //do work
        conn.commit();
    } catch (SQLException e) {
        conn.rollback();
        e.printStackTrace(); //handle error
    }
} catch (SQLException e) {
    e.printStackTrace(); //handle error
}

我的问题是,有更好的(我的意思是更简单)这样做吗?我可以通过一个试块来实现这个目标吗?

2 个答案:

答案 0 :(得分:9)

您可以使用

try(Connection conn = DAOUtil.getConnection();
    PreparedStatement ps = conn.prepareStatement(addSubscriptionSql);
    AutoCloseable finish = conn::rollback) {

    conn.setAutoCommit(false);
    //do work
    conn.commit();
}

这将始终调用rollback(),但在成功完成commit()之后,回滚将变为无操作,因为它将状态重置为上次成功完成{{1}后的状态} ...

由于commit()声明要抛出需要处理此广泛异常类型的AutoCloseable。它可以使用在其他情况下可能有用的自定义类型进行修复:

Exception

...

interface SQLCloseable extends AutoCloseable {
    @Override public void close() throws SQLException;
}

现在,只强制处理异常类型try(Connection conn = DAOUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(addSubscriptionSql); SQLCloseable finish = conn::rollback) { conn.setAutoCommit(false); //do work conn.commit(); }

如果您不喜欢无条件地调用SQLException的想法,那么解决方案就会变得不那么优雅了:

rollback()

如果您在结束时重置自动提交状态,则可以将其用作回滚必要性的指示器:

boolean[] success = { false };
try(Connection conn = DAOUtil.getConnection();
    PreparedStatement ps = conn.prepareStatement(addSubscriptionSql);
    SQLCloseable finish = () -> { if(!success[0]) conn.rollback(); }) {

    conn.setAutoCommit(false);
    //do work
    conn.commit();
    success[0] = true;
}

答案 1 :(得分:6)

  

我可以通过一个试块实现这个目标吗?

不,因为您的conn对象在您的try-with-resources的catch块中不可用。如果要在执行PreparedStatement时捕获异常并显式执行conn.rollback(),则回滚必须在创建{{{}的资源的try内进行。 1}}对象(即使用第二个嵌套conn块进行try调用。)