java - the - Retroceso de transacción en SQLException usando el nuevo bloque try-with-resources
try with resources examples (4)
De acuerdo con la especificación del idioma, la conexión se cerrará antes de que se ejecute la cláusula catch ( http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.20.3.2 ) .
Una posible solución es anidar declaraciones 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);
}
Espero que eso ilustre el punto. Esto debería mejorar bastante si planea usarlo en el código de producción.
Por ejemplo, si está utilizando un grupo de conexiones, debe devolver la conexión tal como la obtuvo, entonces con.setAutoCommit (verdadero); debe hacerse en una cláusula finally. Esto significaría que el try-with-resources externo debería ser un try-catch-finally tradicional.
Tengo un problema con try-with-resources y solo estoy preguntando para estar seguro. ¿Puedo usarlo si necesito reaccionar ante una excepción y aún necesito el recurso en el bloque catch? Ejemplo dado es esto:
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
}
Me temo que todavía estoy condenado a utilizar el antiguo try-catch-finally en este caso, incluso según la documentación del oráculo: "atrapar y finalmente bloquear en una instrucción try-with-resources, cualquier bloqueo catch o finally se ejecuta después de los recursos declarado ha sido cerrado ".
En el ejemplo anterior, creo que es mejor poner con.commit()
dentro de try-catch
anidado porque también puede lanzar 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);
}
}
Tuvimos ese problema en nuestro entorno de producción con sesiones no cerradas.
En su código está capturando "SQLException" para realizar el reinicio de autoCommit. Cualquier tipo de excepción de tiempo de ejecución (como una excepción de puntero nulo) saldrá de su código sin restablecer el auto-commit.
La sintaxis try-with-resource hace que el compilador genere algún código maravilloso para cubrir todas las rutas de ejecución y mantenerse al día con todas las excepciones suprimidas a través de los cierres. Con un par de clases de ayuda, puede insertar commit / rollback y reset-auto-commit en el proceso de generación de código:
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);
}
}
Ahora puede controlar la retrotracción y la confirmación automática con la sintaxis "try with resource" como esta:
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
}
}