我正在用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
}
我的问题是,有更好的(我的意思是更简单)这样做吗?我可以通过一个试块来实现这个目标吗?
答案 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
调用。)