Откат транзакции в SQLException с использованием нового блока try-with-resources
У меня проблема с try-with-resources, и я прошу просто убедиться. Могу ли я использовать его, если мне нужно реагировать на исключение, и мне все еще нужен ресурс в блоке 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-finally в этом случае, даже в соответствии с документацией oracle - "catch and finally blocks в инструкции try-with-resources любой catch или finally block запускается после закрытия объявленных ресурсов."
4 ответов
согласно спецификации языка, соединение будет закрыто до выполнения предложения catch(http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.20.3.2).
возможным решением является вложение операторов try-with-resources:
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);
}
надеюсь, это иллюстрирует точку зрения. Это должно быть немного улучшено, если вы планируете использовать его в производственном коде.
например, если вы используете пул соединений, то вы должны вернуть связь, как вы ее получили, так что Кон.setAutoCommit (true); должно быть сделано в предложении finally. Это означало бы, что внешняя попытка-с-ресурсами должна быть традиционной попыткой-поймать-наконец.
в вашем коде вы ловите "SQLException"для выполнения автоматического сброса. Любое исключение среды выполнения (например, исключение нулевого указателя) будет выделяться из вашего кода без сброса автоматической фиксации.
синтаксис try-with-resource заставляет компилятор генерировать замечательный код для покрытия всех путей выполнения и идти в ногу со всеми подавленными исключениями через закрытия. С помощью нескольких вспомогательных классов вы можете вставить commit/rollback и reset-auto-commit в процесс генерации кода:
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 with resource " следующим образом:
try(Connection conn = getConnection(),
AutoSetAutoCommit a = new AutoSetAutoCommit(conn,false),
AutoRollback tm = new AutoRollback(conn))
{
// Do stuff
tm.commit();
}
//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
}
}
в приведенном выше примере я думаю, что лучше поставить 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);
}
}
мы имели такую проблему в нашей производственной среде с незамкнутыми встречами.