9.9 Sentencias predefinidas

Para solucionar el problema de crear sentencias sql complejas, se utiliza PreparedStatement.

JDBC dispone de un objeto derivado del Statement que se llama PreparedStatement, al que se le pasa la sentencia SQL en el momento de crearlo, no en el momento de ejecutar la sentencia (como pasaba con Statement). Y además esta sentencia puede admitir parámetros, lo que nos puede ir muy bien en determinadas ocasiones.

De cualquier modo, PreparedStatement presenta ventajas sobre su antecesor Statement cuando nos toque trabajar con sentencias que se hayan de ejecutar varias veces. La razón es que cualquier sentencia SQL, cuando se envía al SGBD será compilada antes de ser ejecutada.

  • Utilizando un objeto Statement, cada vez que hacemos una ejecución de una sentencia, ya sea vía executeUpdate o bien vía executeQuery, el SGBD la compilará, ya que le llegará en forma de cadena de caracteres.
  • En cambio, en PreparedStament la sentencia nunca varía y por lo tanto se puede compilar y guardar dentro del mismo objeto, por lo que las siguientes veces que se ejecute no habrá que compilarse. Esto reducirá sensiblemente el tiempo de ejecución.

En algunos sistemas gestores, además, usar PreparedStatements puede llegar a suponer más ventajas, ya que utilizan la secuencia de bytes de la sentencia para detectar si se trata de una sentencia nueva o ya se ha servido con anterioridad. De esta manera se propicia que el sistema guarde las respuestas en la memoria caché, de manera que se puedan entregar de forma más rápida.

La principal diferencia de los objetos PreparedStatement en relación a los Statement, es que en los primeros se les pasa la sentencia SQL predefinida en el momento de crearlo. Como la sentencia queda predefinida, ni los métodos executeUpdate ni executeQuery requerirán ningún parámetro. Es decir, justo al revés que en el Statement.

Los parámetros de la sentencia se marcarán con el símbolo de interrogación (?). Se identificarán por la posición que ocupan en la sentencia, empezando a contar desde la izquierda a partir del número 1. El valor de los parámetros se asignará utilizando el método específico, de acuerdo con el tipo de datos a asignar. El nombre empezará por set y continuará con el nombre del tipo de datos (ejemplos: setString, setInt, setLong, setBoolean …). Todos estos métodos siguen la misma sintaxis:

setXXXX(<posiciónEnLaSentenciaSQL>, <valor>);

Este es el mismo método para insertar un usuario pero usando PreparedStatement:

public void insertUserPrepared(String nombre, String contraseña){ 
    PreparedStatement st = null; 
    String sql = "INSERT INTO usuarios (nombre, contraseña) VALUES (?, ?)";

    try { 
        st = con.preparedStatement(sql); 
        st.setString(1, nombre);
        st.setString(2, contraseña);
        st.executeUpdate(sql); 

    } catch (SQLException e)) {
        System.out.println("ERROR al insertar el usuario: " + e.getMessage()); 

    } finally { 
        try{ 
            //Siempre se debe cerrar todo lo abierto
            if (st != null) {
                st.close(); 
            }
        } catch(java.sql.SQLException ex){ 
            System.out.println("ERROR: " + e.getMessage());
        }
    }
}

Podemos observar que, ahora, además, la sentencia sql es mucho más fácil de escribir.

Ejemplo para eliminar una fila
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class DeleteRowjava {

    // URL de la base de datos, nombre de usuario y contraseña
    private static final String JDDB_URL = "jdbc:mysql://localhost:3306/pr_tuNombre";
    private static final String USUARIO = "pr_tuNombre";
    private static final String PASSWORD = "tuContraseña";

    public static void main(String[] args) {
        Connection conexion = null;
        PreparedStatement preparedStatement = null;

        try {
            // Establecer la conexión a la base de datos
            conexion = DriverManager.getConnection(JDDB_URL, USUARIO, PASSWORD);

            String sql = "DELETE FROM usuarios WHERE id = ?";

            // Crear el PreparedStatement
            preparedStatement = connection.prepareStatement(sql);

            // Establecer valor de parámetro `?` (p.e. id = 3)
            preparedStatement.setInt(1, 3);

            // Ejecutar la consulta
            int filasAfectadas = preparedStatement.executeUpdate();

            // Comprobar cuántas filas fueron afectadas
            if (filasAfectadas > 0) {
                System.out.println("Fila borrada correctamente.");
            } else {
                System.out.println("No se encontró ninguna fila con ese id.");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // Cerrar los recursos en el bloque finally para asegurar que se cierren independientemente de lo que pase
            try {
                if (preparedStatement != null) {
                    preparedStatement.close();
                }
            } catch (SQLException ex) {
                System.out.println("ERROR: " + ex.getMessage());
            }
            try {
                if (connection != null) {
                    conexion.close();
                }
            } catch (SQLException ex) {
                System.out.println("ERROR: " + ex.getMessage());
            }                
        }
    }
}