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

Bee*_*Bee 5 java transactions jdbc 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
}
Run Code Online (Sandbox Code Playgroud)

但是因为我应该在事务失败的情况下回滚,所以我不得不像这样更改代码.注意这两个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
}
Run Code Online (Sandbox Code Playgroud)

我的问题是,有更好的(我的意思是更简单)这样做的方式吗?我可以用一个try块实现这个目的吗?

Hol*_*ger 10

您可以使用

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

    conn.setAutoCommit(false);
    //do work
    conn.commit();
}
Run Code Online (Sandbox Code Playgroud)

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

因为AutoCloseable声明要抛出Exception将需要处理这种广泛的异常类型.它可以使用在其他情况下可能有用的自定义类型进行修复:

interface SQLCloseable extends AutoCloseable {
    @Override public void close() throws SQLException;
}
Run Code Online (Sandbox Code Playgroud)

...

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

    conn.setAutoCommit(false);
    //do work
    conn.commit();
}
Run Code Online (Sandbox Code Playgroud)

现在,只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;
}
Run Code Online (Sandbox Code Playgroud)

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

try(Connection conn = DAOUtil.getConnection();
    PreparedStatement ps = conn.prepareStatement(addSubscriptionSql);
    SQLCloseable finish = () -> { if(!conn.getAutoCommit()) conn.rollback(); }) {

    conn.setAutoCommit(false);
    //do work
    conn.commit();
    conn.setAutoCommit(true);
}
Run Code Online (Sandbox Code Playgroud)

  • 这是一个聪明的解决方案,所以+1为此,但我想知道有多少人会在代码库中遇到它时理解这一点. (2认同)

Gor*_*son 6

我可以用一个try块实现这个目的吗?

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