Francisco Javier Martínez Páez

Consultor tecnológico de desarrollo de proyectos informáticos.

 Ingeniero Técnico en Telecomunicaciones

Puedes encontrarme en Autentia: Ofrecemos servicios de soporte a desarrollo, factoría y formación

Somos expertos en Java/J2EE

Ver todos los tutoriales del autor

Fecha de publicación del tutorial: 2006-09-26

Tutorial visitado 12.043 veces Descargar en PDF
ALGUNAS CARACTERÍSTICAS MENOS CONOCIDAS DEL API JDBC 2.0 ALGUNAS CARACTERÍSTICAS MENOS CONOCIDAS DEL API JDBC.

Siguiendo un poco con los tutoriales sobre JDBC, en este pretendo poner de manifiesto, si el lector aun no las conoce, algunas posibilidades que nos permite el API de JDBC (y si el driver nos lo permite) que son menos conocidas.

Los ejemplos de este tutorial están hechos con el siguiente entorno de desarrollo:

  • Jboss Eclipse IDE Milestone 5.

  • JDK 1.4

  • MySQL 5.0

  • MySQL Administrator (opcional)

  • MySQL Connector/J (Driver tipo 4 que implementa la versión JDBC 3.0)


Para los ejemplos del tutorial usaremos la tabla USUARIOS:

Creando la tabla con MySQLAdministrator



Vamos a crear una nueva clase que denominaremos CaracteristicasJDBC:

Creamos la clase


Copiamos el método getConnection() de otros tutoriales y las constantes que usa:

    protected static String dbClass = "com.mysql.jdbc.Driver";
    protected static String dbUrl = "jdbc:mysql:///paco";
      
    protected Connection getConnection() {
       
        Properties props = new Properties();
        props.put("user","<tu_usuario>");
        props.put("password","<tu_password>");       
        Connection conBBDD = null;      

        try {           
            Class.forName(dbClass);
            conBBDD=DriverManager.getConnection(dbUrl, props);
        } catch(Exception e) {
             return null;
        }       
        return conBBDD;
    }

Ya podemos empezar:

Ejecutar sentencias por lotes.

Nos basaremos para el ejemplo en tres métodos de Statement:
  • addBatch(String sentencia). Añade una sentencia a la lista de sentencias por lotes.
  • executeBatch(). Ejecuta la lista de sentencias por lotes. Retorna un array de enteros con el resultado de cada sentencia.
  • clearBatch(). Limpia la lista de sentencias por lotes.
Si tenemos la intención de realizar varias sentencias INSERT o UPDATE consecutivas, es mucho más eficiente utilizar la ejecución de sentencias por lotes que enviar las sentencias una por una.

Creamos un método que reciba una lista de sentencias y las ejecuta por lotes:

public int[] ejecutaPorLotes(List sentencias) {
        Connection conn = getConnection();
        int[] resultado = null;
        Statement st = null;
        try {
            st = conn.createStatement();
            for (int i = 0; i < sentencias.size(); i++) {
                String sentencia = (String) sentencias.get(i);
                st.addBatch(sentencia);
            }
            resultado = st.executeBatch();
            st.clearBatch();
            return resultado;
        } catch (SQLException e) {
            e.printStackTrace();
            return resultado;
        } finally {
            if (st != null) {
                try {
                    st.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }       

        }

    }

Vamos a probar el código:

public static void main(String[] args) {
       
        CaracteristicasJDBC car = new CaracteristicasJDBC();
       
        ArrayList lista = new ArrayList();
        lista.add("INSERT INTO USUARIOS VALUES ('Pepe',19)");
        lista.add("INSERT INTO USUARIOS VALUES ('Juan',21)");
       
        int[] resultado = car.ejecutaPorLotes(lista);
       
        for(int i=0;i<resultado.length;i++) {
            System.out.println("resultado: "+resultado[i]);
        }

    }

Ejecutamos y vemos la consola:

Resultado


Comprobamos los datos en la tabla:

datos

Sería buena idea también modificar el código para desactivar el modo autocommit (setAutoCommit(false)) en la conexión y realizar un rollback en caso de encontrarnos algún error en el array de enteros.


Utilizar un ResultSet Modificable.

En el siguiente ejemplo vamos a modificar el primero de los registros que introdujimos en el ejemplo anterior,  usando algunos métodos de ResultSet.

El código del siguiente ejemplo lo vamos a realizar directamente en el método main y lo explicaremos en los comentarios del código:

public static void main(String[] args) {

        CaracteristicasJDBC car = new CaracteristicasJDBC();

        Connection conn = car.getConnection();
       
        Statement st = null;
        ResultSet rs = null;

        try {
           
           
// Creamos un objeto de tipo Statement y le decimos al driver que vamos
           // a usar un ResultSet Scrollable y Updatable.

            st = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
                    ResultSet.CONCUR_UPDATABLE);

            String query = "SELECT NOMBRE, EDAD FROM USUARIOS";

            // Obtenemos el ResultSet que debería contener dos registros.
            rs = st.executeQuery(query);

            // Nos situamos sobre el primero de ellos.
            rs.first();

            // Modificamos la columna EDAD del primer registro del ResultSet
            // (sólo en el ResultSet y no en la tabla)
            rs.updateInt("EDAD", 29);
            // Cancelamos el UPDATE que acabamos de realizar
            rs.cancelRowUpdates();
            // Volvemos a modificar la columna EDAD del primer registro del
            // ResultSet con el nuevo valor
            rs.updateInt("EDAD", 30);
           
            // Enviamos los cambios a la base de datos.
            rs.updateRow();

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (st != null) {
                try {
                    st.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

        }

    }

Una vez ejecutado el código, comprobamos el resultado en la tabla:

resultado


Vamos a modificar el código anterior para eliminar el último registro (Pepe)

public static void main(String[] args) {

        CaracteristicasJDBC car = new CaracteristicasJDBC();

        Connection conn = car.getConnection();
       
        Statement st = null;
        ResultSet rs = null;

        try {
             
        // Creamos un objeto de tipo Statement y le decimos al driver que vamos
        // a usar un ResultSet Scrollable y Updatable.

            st = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
                    ResultSet.CONCUR_UPDATABLE);

            String query = "SELECT NOMBRE, EDAD FROM USUARIOS";

            // Obtenemos el ResultSet que debería contener dos registros.
            rs = st.executeQuery(query);

            // Nos situamos sobre el último de ellos.
            rs.last();

            // Eliminamos el registro actual.
            rs.deleteRow();

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (st != null) {
                try {
                    st.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

        }

    }

Una vez ejecutado el código, comprobamos el resultado en la tabla:

resultado


Vamos a modificar de nuevo el código anterior para insertar de nuevo a Pepe y a un amigo de Pepe que se llama Lucas.

public static void main(String[] args) {

        CaracteristicasJDBC car = new CaracteristicasJDBC();

        Connection conn = car.getConnection();

        Statement st = null;
        ResultSet rs = null;

        try {

            // Creamos un objeto de tipo Statement y le decimos al driver que
            // vamos a usar un ResultSet Scrollable y Updatable.
            st = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
                    ResultSet.CONCUR_UPDATABLE);

            String query = "SELECT NOMBRE, EDAD FROM USUARIOS";

            // Obtenemos el ResultSet que debería contener dos registros.
            rs = st.executeQuery(query);

            // Nos situamos sobre el primero de ellos.
            rs.first();

            // Nos situamos sobre "insertRow". Esta fila no es más que un buffer
            // que nos permite construir un registro para insertarlo en la base
            // de datos.
            rs.moveToInsertRow();

            // Creamos el registro Lucas,34
            rs.updateString("NOMBRE", "Lucas");
            rs.updateInt("EDAD", 34);

            // Lo insertamos en la base de datos.
            rs.insertRow();

            // Creamos el resgistro Pepe,24
            rs.updateString("NOMBRE", "Pepe");
            rs.updateInt("EDAD", 24);

            // Lo insertamos en la base de datos.
            rs.insertRow();

            // Volvemos a situarnos sobre el registro en el que estábamos
            // previamente a invocar al método moveToInsertRow() (el primero)
            rs.moveToCurrentRow();

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (st != null) {
                try {
                    st.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

        }

    }

Una vez ejecutado el código, comprobamos el resultado en la tabla:

resultado



Claves autogeneradas.

Algunos motores de base de datos (como mysql) permiten usar claves autogeneradas, es decir, como clave primaria de la tabla un campo autonumérico.
 
El problema viene cuando insertamos un registro en una tabla con claves autogeneradas y queremos saber cual es el valor de esa clave sin tener que hacer una consulta posterior. ¿ Es posible esto ? vamos a verlo.

Lo primero que vamos a hacer es crearnos una tabla llamada provincias con claves autogeneradas usando MySQLAdministrator:

La tabla tendrá dos campos:
  • ID_PROV (Primary Key autogenerada)
  • NOMBRE
Tabla

Para recuperar las claves autogeneradas haremos uso del método de Statement:
  • getGeneratedKeys(): Devuelve un ResultSet donde se almacenan las claves creadas.
Vamos al ejemplo:

public static void main(String[] args) {

        CaracteristicasJDBC car = new CaracteristicasJDBC();

        Connection conn = car.getConnection();

        Statement st = null;
        ResultSet rs = null;

        try {
           
            st = conn.createStatement();

            // Insertamos un registro.
            st.executeUpdate("INSERT INTO PROVINCIAS (NOMBRE) VALUES ('Madrid')");
                      
            // Obtenemos las claves autogeneradas y las mostramos por pantalla
            rs=st.getGeneratedKeys();

             while(rs.next()) {
                 int id = rs.getInt(1);
                 System.out.println("CLAVE: "+id);
             }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (st != null) {
                try {
                    st.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

        }

    }

Ejecutamos el código y mostramos la consola:

Consola


Vamos a ver los registros en la tabla:

tabla

Puntos intermedios de rollback.
JDBC permite manejar la transaccionalidad desactivando el modo autocommit (autoentrega) con el método de Connection: setAutoCommit(false). Todos conocemos además los métodos:
  • commit(). Realiza los cambios
  • rollback(). Deshace los cambios
A partir de la versión jdbc 3.0 se incluye una nueva posibilidad: crear puntos de salvaguarda o SavePoint para hacer rollback parciales:

        SavePoint sp = conn.setSavePoint(“NOMBRE”);
        ....
        conn.rollback(sp);

Vamos a hacer un ejemplo y comentaremos en el código lo interesante:

public static void main(String[] args) {

        CaracteristicasJDBC car = new CaracteristicasJDBC();

        Connection conn = car.getConnection();

        Statement st = null;
        ResultSet rs = null;

        try {
            // desactivamos el método de autoentrega.
            conn.setAutoCommit(false);
           
            st = conn.createStatement();
           
            // Insertamos un registro en la tabla de usuarios
            st.executeUpdate("INSERT INTO USUARIOS  VALUES ('Federico',50)");
           
            // Creamos un punto de salvaguarda en este momento.
            Savepoint sp = conn.setSavepoint("PUNTO1");
           
            // Modificamos la edad de Federico:
            st.executeUpdate("UPDATE USUARIOS SET EDAD = 45 WHERE            NOMBRE = 'Federico'");
           
            // Hacemos un rollback al punto PUNTO1:
            conn.rollback(sp);
           
            // Entregamos los cambios anteriores a punto1 a la BBDD:
            conn.commit();
           

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (st != null) {
                try {
                    st.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

        }

    }

Si ejecutamos el código anterior, deberíamos insertar un nuevo registro en la tabla de usuarios con nombre Federico y con edad 50, ya que el cambio posterior lo hemos anulado. Una vez ejecutado comprobamos la tabla:

Resultado 


Bueno, pues no ha sido demasiado dificil.

Lo de siempre, si quereis ayuda no tenéis mas que poneros en contacto con nosotros: http://www.autentia.com




A continuación puedes evaluarlo:

Regístrate para evaluarlo

Por favor, vota +1 o compártelo si te pareció interesante

Share |
Anímate y coméntanos lo que pienses sobre este TUTORIAL:

Fecha publicación: 2008-02-13-02:29:26

Autor:

[Luis Leon] Saludos amigos, los felicito por el tutorial. La informacion esta bien clara y muy bien explicada. Pero tengo una duda y queria saber si me la podrian aclarar. Cuando se hace un ResultSet modificable se hace referencia a un sentencia SQL de una tabla. ¿Es posible tener una consulta de varias tablas relacionadas y que el ResultSet haga la actualizacion? ... Gracias y saludos

Fecha publicación: 2007-01-26-05:45:55

Autor:

[José Carlos] Me parece muy util