Acceso a base de datos - JDBC Cualquier sistema de computo necesita almacenar información en un medio persistente. Un manejador de base de datos es el responsable de aplicar el manejo de datos y darles una organización predeterminada. Un manejador de base de datos relacional (RDBMS) concibe una base de datos como una serie de relaciones entre entidades. Cualquier RDBMS actual proporciona diversos servicios. Siempre ha existido la necesidad de comunicar módulos de software con el manejador de base de datos. El RDBMS ofrece siempre un API para que se interactue con él. Tipicamente está escrito en C y su manejo es bastante elaborado. Se han hecho esfuerzos en la industria para estandarizar el acceso programatico a una base de datos. Microsoft propuso el estandar de ODBC (Open DataBase Connectivity) que permite la interacción con un RDBMS y actualmente los RDBMS ofrecen un ODBC. Con el surgimiento de Java, empezaron los esfuerzos para integrar Java con los distintos RDBMS y de ahi surgio la propuesta de una colección de objetos que permitieran el acceso a un RDBMS, denominado Java Database Connectivity JDBC tiene como metas de diseño el lograr la transaparencia en la conexión al RDBMS, donde el programador solo debe preocuparse por especificar el manejador JDBC que se encarga de administrar la conexion y el envio de instrucciones al RDBMS es identico sin importar el tipo de RDBMS que se tenga Desde el punto de vista de JDBC, la interacción con el RDBMS es por medio de SQL y las siguientes definciones se aplican Una base de datos es un contenedor de tablas Una tablas es un contenedor compuesto de renglones Un renglon o tupla es un contenedor compuesto de columnas Una columna es un dato en particular que tiene un nombre, tipo y valor Para que Java pueda interactuar con un RDBMS se debe disponer de un manejador (driver) de JDBC, tipicamente provisto por el fabricante del RDBMS. Su función es lograr que la conexión al RDBMS sea transparente. Pueden existir 4 tipos de JDBC drivers JDBC-ODBC Driver - Lo trae por omision JDK y utiliza ODBC para la comunicación JDBC con API nativo - El driver esta escrito en Java y parte en C para comunicarse con el API del RDBMS JDBC Java puro nativo al RDBMS - El driver esta escrito al 100% en Java y utiliza el protocolo propietario del RDBMS JDBC Net - Permite aislar en un solo nodo las llamadas nativas y en el cliente se incorpora un driver JDBC ligero JDBC proporciona una serie de clases, localizadas en java.sql Driver DriverManager Connection - permite interactuar con la base de datos Statement - permite generar queries SQL de solo texto PreparedStatement - permite generar queries SQL con parametros CallableStatement - permite generar llamadas a procedimientos remotos ResultSet - contiene una coleccion de renglones compuestos de columnas DatabaseMetaData - permite interactuar con el metadata del RDBMS ResultSetMetaData - resultado de inspeccionar el MetaData Para mostrar el uso de JDBC se utilizara una base de datos hecha en Java, hsqldb (http://hsqldb.sourceforge.net/index.html) 1. Instalacion de hsqldb Obtener el archivo zip de la ultima version estable de hsqldb (hsqldb_1_7_1.zip) y descomprimirlo Donde se descomprime debe crear un subdirectorio denominado hsqldb Tiene los siguiente directorios bin - comandos para administrar al manejador lib - Archivos JAR con el driver de JDBC para hsqldb data - Directorio donde se almacena la base de datos doc - documentacion 2. Arrancando el servidor de base de datos Cambiarse al directorio hsqldb\bin Ejecutar start java -cp ..\lib\hsqldb.jar org.hsqldb.Server -database ..\data\basedatos y dejar el proceso activo, levanta en 127.0.0.1 en el puerto 9001 un socket Levanta 3. Creacion y llenado de la base de datos Para crear la base de datos se deben aplicar los siguientes pasos Cambiarse al directorio hsqldb\bin Ejecutar runUtil DatabaseManager Este arranca una interfaz AWT y seleccionar lo siguiente Type : HSQL Database Engine In-Memory Driver: org.hsqldb.jdbcDriver URL: jdbc:hsqldb:hsql://localhost User: sa Password: Aceptarlo (checar que en el directorio data crea tres archivos con nombre basedatos y con extension data, properties y script respectivamente) El paso siguiente es crear una tabla El esquema de la base de datos se presenta a continuacion CREATE TABLE JJJJData ( Entry INTEGER NOT NULL, Customer VARCHAR (20) NOT NULL, DOW VARCHAR (3) NOT NULL, Cups INTEGER NOT NULL, Type VARCHAR (10) NOT NULL, PRIMARY KEY( Entry ) ) (se puede guardar este comando en un archivo, por ejemplo esquema.sql, y con la herramienta en File->Open Script indicar el script) Llenar la base de datos con los sig. datos INSERT INTO JJJJData VALUES ( 1, 'John', 'Mon', 1, 'JustJoe' ) INSERT INTO JJJJData VALUES ( 2, 'JS', 'Mon', 1, 'Cappuccino' ) INSERT INTO JJJJData VALUES ( 3, 'Marie', 'Mon', 2, 'CaffeMocha' ) Para probar que existe, en el campo de texto poner SELECT * FROM JJJJData y debe mostrar el resultado de la consulta. (se puede guardar este comando en un archivo, por ejemplo datos.sql, y con la herramienta en File->Open Script indicar el script) 3. Conexion con la base de datos usando JDBC En NetBeans introducir el siguiente código import java.sql.*; public class Conexion { /** Creates a new instance of Conexion */ public Conexion() { } public static void main(String [] args) { Connection conn=null; /*objeto de conexion al RDBMS*/ String driverName= "org.hsqldb.jdbcDriver"; String url="jdbc:hsqldb:hsql://localhost"; String user="sa"; String pass=""; try { Class.forName(driverName); conn = java.sql.DriverManager.getConnection( url,user,pass); System.out.println("conectado "); conn.close(); } catch( java.lang.ClassNotFoundException ex) { ex.printStackTrace(); } catch (java.sql.SQLException ex){ ex.printStackTrace(); } } } En este caso, se carga a memoria la clase org.hsqldb.jdbcDriver y si tiene exito el siguiente paso es crear un objeto Connection, pidiendo al objeto DriverManager que lo otorge, indicando el url de la base de datos, usuario y password El URL se compone tipicamente por el nombre del protocolo, el nombre del driver y el nombre de la base de datos "jdbc:hsqldb:basedatos" El ultimo paso es cerrar la conexion, invocando al metodo close del objeto Connection El siguiente paso es poner disponible al Driver JDBC de hsqldb Desde NetBeans, montar el JAR que contiene al Driver Mount->Archive JAR Seleccionar hsqldb\lib\hsqldb.jar Ejectuar el codigo 4. Generando una consulta Despues de obtener una conexion es posible generar una consulta utilizando al objeto de tipo Connection import java.sql.*; public class Consulta { /** Creates a new instance of Conexion */ public Consulta() { } public static void main(String [] args) { Connection conn=null; /*objeto de conexion al RDBMS*/ String driverName= "org.hsqldb.jdbcDriver"; String url="jdbc:hsqldb:hsql://localhost"; String user="sa"; String pass=""; try { Class.forName(driverName); conn = java.sql.DriverManager.getConnection( url,user,pass); System.out.println("conectado "); Statement stmt=conn.createStatement(); ResultSet res=stmt.executeQuery("select * from JJJJDATA"); if (res!=null) { while (res.next()) { System.out.println( res.getInt("Entry")+" "+ res.getString("Customer")+" "+ res.getString("Dow")+" "+ res.getInt("Cups")+" "+ res.getString("Type") ); } res.close(); } stmt.close(); conn.close(); } catch( java.lang.ClassNotFoundException ex) { ex.printStackTrace(); } catch (java.sql.SQLException ex){ ex.printStackTrace(); } } } Con el objeto conn se pide via createStatement un nuevo objeto de tipo Statement, al cual se le puede indicar la ejecucion de un query en forma de una cadena de caracteres. Cuando arroja un resultado, se toma en un objeto de tipo ResultSet ResultSet es un objeto que almacena en memoria el resultado y se puede iterar en el con el metodo next(), el cual regresa false cuando ya no tiene datos. Por cada renglon, se puede preguntar por cada columna, dando su nombre y usando un metodo getXXX() donde XXX es el tipo de dato El mapeo de JDBC a SQL es getString VARCHAR o CHAR getInt INTEGER 6. Modificando datos Para modificar datos se puede seguir el mismo procedimiento, savlo que en lugar de usar executeQuery, se usa el metodo execute import java.sql.*; public class Modificar { /** Creates a new instance of Conexion */ public Modificar() { } public static void main(String [] args) { Connection conn=null; /*objeto de conexion al RDBMS*/ String driverName= "org.hsqldb.jdbcDriver"; String url="jdbc:hsqldb:hsql://localhost"; String user="sa"; String pass=""; try { Class.forName(driverName); conn = java.sql.DriverManager.getConnection( url,user,pass); System.out.println("conectado "); Statement stmt=conn.createStatement(); stmt.execute("UPDATE JJJJDATA SET Cups=10"); stmt.close(); conn.close(); } catch( java.lang.ClassNotFoundException ex) { ex.printStackTrace(); } catch (java.sql.SQLException ex){ ex.printStackTrace(); } } } 6.5 Eliminación de datos. /* * Borrar.java * * Created on September 23, 2003, 6:48 PM */ /** * * @author al990864 */ import java.sql.*; public class Borrar { /** Creates a new instance of Borrar */ public Borrar() { } public static void main(String [] args) { Connection conn=null; /*objeto de conexion al RDBMS*/ String driverName= "org.hsqldb.jdbcDriver"; String url="jdbc:hsqldb:hsql://localhost"; String user="sa"; String pass=""; try { Class.forName(driverName); conn = java.sql.DriverManager.getConnection( url,user,pass); System.out.println("conectado "); String sql = "DELETE FROM JJJJDATA WHERE CUSTOMER=?"; PreparedStatement stmt=conn.prepareStatement(sql); stmt.setString(1,"JS"); stmt.executeUpdate(); stmt.close(); conn.close(); } catch( java.lang.ClassNotFoundException ex) { ex.printStackTrace(); } catch (java.sql.SQLException ex){ ex.printStackTrace(); } } } 7. Sentencias preparadas El componer el texto SQL en el programa implica poner caracteres especiales para aquellos datos de tipo String Una sentencia preparada permite pasar como parametros los valores del query, ademas de agilizar la interpretacion de la sentencia El siguiente codigo muestra el uso de sentencias preparadas import java.sql.*; public class ConsultaPrepared { /** Creates a new instance of Conexion */ public ConsultaPrepared() { } public static void main(String [] args) { Connection conn=null; /*objeto de conexion al RDBMS*/ String driverName= "org.hsqldb.jdbcDriver"; String url="jdbc:hsqldb:hsql://localhost"; String user="sa"; String pass=""; try { Class.forName(driverName); conn = java.sql.DriverManager.getConnection( url,user,pass); System.out.println("conectado "); String sql = "SELECT * FROM JJJJDATA WHERE CUSTOMER=?"; PreparedStatement stmt=conn.prepareStatement(sql); stmt.setString(1,"JS"); ResultSet res=stmt.executeQuery(); if (res!=null) { while (res.next()) { System.out.println( res.getInt("Entry")+" "+ res.getString("Customer")+" "+ res.getString("Dow")+" "+ res.getInt("Cups")+" "+ res.getString("Type") ); } res.close(); } stmt.close(); conn.close(); } catch( java.lang.ClassNotFoundException ex) { ex.printStackTrace(); } catch (java.sql.SQLException ex){ ex.printStackTrace(); } } } En este caso, la cadena SQL queda como "SELECT * FROM JJJJDATA WHERE CUSTOMER=?", donde ? es el primer parametro que se debe indicar. Se observar que al objeto de tipo Connection se le pide con el metodo prepareStatement que gener una sentencia SQL, dando el SQL parametrizado Despues, se indica via el metodo setString, que recibe el numero del parametro y su valor, para que con el metodo executeQuery se aplique la consulta. La manera de leer los datos es similar que en el ejemplo de la primera consulta. 8. Dando de baja al servidor Cambiarse al directorio hsqldb\bin runUtil ShutdownServer 9. Arquitectura C/S con JDBC A nivel cliente servidor se puede diseñar una aplicacion cliente/servidor de dos capas Cliente----JDBC---RDBMS En este caso el componente cliente tiene toda la logica, control y visualización del sistema Se puede poner una arquitectura con RMI Cliente---RMI---Objeto RMI----JDBC----RDBMS En este caso el Cliente solo visualiza y el objeto RMI se encarga de la logica y control de las peticiones a la base de datos via JDBC 10. Ejercicio.