JDBC, chiudere le connessioni in modo sicuro

Spesso su internet si trovano molti esempi che servono, a chi sta iniziando, ad iniziare ad approcciare le connessioni JDBC. Purtroppo però questi esempi basilari partono sempre dalla forma errata, ovvero senza alcuna gestione delle eccezioni col risultato che il malcapitato database di turno si può trovare dozzine di connessioni appese mentre il novello programmatore fa i suoi test con applicazioni rudimentali.

Normalmente il codice si trova grosso modo così, in questo caso ad esempio con una sessione oracle:

import java.sql.*;
public class JDBCDemo {
    public static void main(String[] args) {
        try {
            // Connect to the database
            try {
                Class.forName("oracle.jdbc.driver.OracleDriver");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            conn = DriverManager.getConnection("jdbc:oracle:thin:@oraserver:1521:XE", "orauser", "orapwd");
            // Execute the SQL statement
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery("SELECT * from oratable");
            System.out.println("Got results!");
            // Loop thru all the rows
            while (rs.next()) {
                String data = rs.getString("some_field");
                System.out.println(data);
            }
            stmt.close();
        } catch (Exception e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
    }
}

Perfettamente sintetizzabile come:

import;
public class whatever {
    public static void main( String[] args ) {
        try {
            open_connection;
            do_some_stuff;
            close_connection;
        } catch exception {
             do_some_stuff_with_exception_tipically_print;
        }
    }
}

Il problema è che se nel blocco do_some_stuff si verifica un’eccezione non si arriverà mai al close_connection. In sostanza, lascerete una connessione appesa nel database, in ogni caso, se il database non è installato da un paguro finirà per chiudere la connessione dopo un determinato tempo, resta il problema che se il vostro programma non funziona e, come capita spesso, lo provate mille volte prima di riuscire ad ottenere il risultato finirete per avere tante sessioni nel database, la questione è particolarmente delicata se ci troviamo in un ambiente con uno shared pool o con un connection pool fornito da un application server.
La soluzione è usare in modo adeguato lo statement finally:

Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;

try {
    do_some_stuff;
} catch (SQLException ex) {
    do_some_stuff_with_exception_tipically_print;
} finally {
    if (rs != null) {
        try {
            rs.close();
        } catch (SQLException e) { do_some_stuff_tipically_print }
    }
    if (stmt != null) {
        try {
            stmt.close();
        } catch (SQLException e) { do_some_stuff_tipically_print }
    }
    if (conn != null) {
        try {
            conn.close();
        } catch (SQLException e) { do_some_stuff_tipically_print }
    }
}

In questo caso chiuderemo sempre correttamente il resultset, lo statement ed infine la connessione, così il vostro pericolosissimo blocco do_some_stuff non arreccherà grossi danni.
Il nostro semplice programmino di prima diverrebbe quindi:

import java.sql.*;
public class JDBCDemo {
    public static void main( String[] args ) {
        try {
            // Connect to the database
            try {
                Class.forName("oracle.jdbc.driver.OracleDriver");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            conn = DriverManager.getConnection("jdbc:oracle:thin:@oraserver:1521:XE", "orauser", "orapwd");
            // Execute the SQL statement
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery("SELECT * from oratable");
            System.out.println("Got results!");
            // Loop thru all the rows
            while( rs.next() ) {
                String data = rs.getString( "some_field" );
                System.out.println( data );
            }
            rs.close();
            stmt.close();
            conn.close();
        }
        catch( SQLException sqlex ) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) { do_some_stuff_tipically_print }
            }
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) { do_some_stuff_tipically_print }
            }
            if (conn != null) {
                try {
                    conn.close();
            } catch (SQLException e) { do_some_stuff_tipically_print }
        }
    }
}

La legge quindi è sempre la stessa: try…catch…finally
Provate intenzionalmente a generare errori con e senza il blocco finally e tenete d’occhio la tabella v$session, per la gioia e ira del vostro dba!

One thought on “JDBC, chiudere le connessioni in modo sicuro”

Leave a Reply

Your email address will not be published. Required fields are marked *