Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ TEMA 1 ACCESO A BASE DE DATOS 1. QUE ES UN JDBC – ODBC? JDBC es la API estándar de acceso a Bases de Datos con Java, y se incluye con el Kit de Desarrollo de Java (JDK) a partir de la versión 1.1. Sun optó por crear una nueva API, en lugar de utilizar APIs ya existentes, como ODBC, con la intención de obviar los problemas que presenta el uso desde Java de estas APIs, que suelen ser de muy bajo nivel y utilizar características no soportadas directamente por Java, como punteros, etc. Aunque el nivel de abstracción al que trabaja JDBC es alto en comparación, por ejemplo, con ODBC, la intención de Sun es que sea la base de partida para crear librerías de más alto nivel, en las que incluso el hecho de que se use SQL para acceder a la información sea invisible. Para trabajar con JDBC es necesario tener controladores (drivers) que permitan acceder a las distintas Bases de Datos: cada vez hay más controladores nativos JDBC. Sin embargo, ODBC es hoy en día la API más popular para acceso a Bases de Datos: Sun admite este hecho, por lo que, en colaboración con Intersolv (uno de principales proveedores de drivers ODBC) ha diseñado un puente que permite utilizar la API de JDBC en combinación con controladores ODBC. Algunos fabricantes, como Microsoft, ofrecen sus propias APIs, en lugar de JDBC, como RDO, etc. Aunque estas APIs pueden ser muy eficientes, y perfectamente utilizables con Java, su uso requiere tener muy claras las consecuencias, sobre todo la pérdida de portabilidad, factor decisivo en la mayor parte de los casos para escoger Java en lugar de otro lenguaje. JDBC es un API de Java que permite que programas en Java ejecuten comandos SQL, permitiendo esto que los programas puedan usar bases de datos en ese esquema. Dado que casi todos los sistemas de administración de bases de datos relacionales (DBMS) soportan SQL(Structured Query Language), y que Java corre en la mayoría de las plataformas, JDBC hace posible escribir una sóla aplicación de base de datos que pueda correr en diferentes plataformas e interactuar con distintos DBMS. La conectividad de la base de datos de Java (JDBC) es un marco de rogramación para los desarrolladores de Java que escriben los programas que tienen acceso a la informAción guardada en bases de datos, hojas de calculo, y archivos "planos". JDBC se utiliza comúnmente para conectar un programa del usuario con una base de datos por “detrás de la escena”, sin importar qué software de administración o manejo de base de datos se utilice para controlarlo. De esta manera, JDBC es una plataforma-cruzada. El acceso a la base de datos de los programas de Java ________________________________________________________________________________________ Proyecto de Software 101 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ utilizan las clases JDBC API, que está disponible para la transferencia directa libre del sitio de Sun. Los puentes de JDBC-ODBC se han creado para permitir que los programas de Java conecten con el software compatible ODBC de la base de datos. JDBC no sólo provee un interfaz para acceso a motores de bases de datos, sino que también define una arquitectura estándar, para que los fabricantes puedan crear los drivers que permitan a las aplicaciones java el acceso a los datos. Sun define JDBC como: “The JDBC API is the industry standard for databaseindependent connectivity between the Java programming language and a wide range of databases.” 2. FILOSOFIA Y OBJETIVOS DE JDBC Algunas características son: • API A NIVEL SQL. JDBC es un API de bajo nivel, es decir, que está orientado a permitir ejecutar comandos SQL directamente, y procesar los resultados obtenidos. Esto supone que será tarea del programador crear APIs de más alto nivel apoyándose directamente sobre JDBC. • COMPATIBLE CON SQL. Cada motor de Base de Datos implementa una amplia variedad de comandos SQL, y muchos de ellos no tienen porque ser compatibles con el resto de motores de Base de Datos. JDBC, para solventar este problema de incompatibilidad, ha tomado la siguiente posición: o JDBC permite que cualquier comando SQL pueda ser pasado al driver directamente, con lo que una aplicación Java puede hacer uso de toda la funcionalidad que provea el motor de Base de Datos, con el riesgo de que esto pueda producir errores o no en función del motor de Base de Datos. o Con el objetivo de conseguir que un driver sea compatible con SQL (SQL compliant), se obliga a que al menos, el driver cumpla el Estándar ANSI SQL 92. • JDBC debe ser utilizable sobre cualquier otro API de acceso a Bases de Datos, o más en particular ODBC (Open Database Connectivity) • JDBC debe proveer un interfaz homogéneo al resto de APIs de Java. • JDBC debe ser un API simple, y desde ahí, ir creciendo. ________________________________________________________________________________________ Proyecto de Software 102 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ • JDBC debe ser fuertemente tipado, y siempre que sea posible de manera estática, es decir, en tiempo de compilación, para evitar errores en tiempo de ejecución. • JDBC debe mantener los casos comunes de acceso a Base de Datos lo más sencillo posible: o Mantener la sencillez en los casos más comunes (SELECT, INSERT, DELETE y UPDATE). o Hacer realizables los casos menos comunes: Invocación de procedimientos almacenados. • Crear múltiples métodos para múltiple funcionalidad. JDBC ha preferido incluir gran cantidad de métodos, en lugar de hacer métodos complejos con gran cantidad de parámetros. ________________________________________________________________________________________ Proyecto de Software 103 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ TEMA 2 ACCESO A BASE DE DATOS CONTROLADORES JDBC 1. LOS CONTROLADORES Los controladores (conectores o drivers) JDBC proveen clases que implementan la interfaz JDBC. JDBC define cuatro tipos de controladores, y cada una tiene una mezcla variada de ejecutables sistema operativo dependientes y código de Java como se indica a continuación: Tipo 1. JDBC-ODBC bridge más driver ODBC: “BRIDGE” Permite el acceso a Base de Datos JDBC mediante un driver ODBC. Cada máquina cliente que use el puente, debe tener librerías clientes de ODBC(dll propias del S.O). • Ventajas: Buena forma de aprender JDBC. También puede ser buena idea usarlo, en sistemas donde cada máquina cliente tenga ya instalado los drivers ODBC. También es posible que sea la única forma de acceder a ciertos motores de Bases de Datos. • Inconvenientes: No es buena idea usar esta solución para aplicaciones que exijan un gran rendimiento, ya que la transformación JDBC-ODBC es costosa. Tampoco es buena solución para aplicaciones con alto nivel de escalabilidad. Tipo 2. Driver Java parciales: “NATIVE” Traducen las llamadas al API de JDBC Java en llamadas propias del motor de Base de Datos (Oracle, Informix...). Al igual que el tipo anterior, exige en las máquinas clientes código binario propio del cliente de la Base de datos específica y del sistema operativo. • Ventajas: Mejor rendimiento que el anterior. Quizá puede ser buena solución para entornos controlados como intranets. Ejemplo OCI oracle. • Inconvenientes: Principalmente la escalabilidad, ya que estos drivers exigen que en la máquina cliente librerías del cliente de la Base de Datos. Tipo 3. Driver JDBC a través de Middleware: “NETWORK” Traduce las llamadas al API JDBC en llamadas propias del protocolo específico del broker. Éste se encargará de traducirlas de nuevo en sentencias propias del motor de Base de Datos de cada caso. • Ventajas: Buena solución cuando necesitamos acceder a Bases de Datos distintas y se quiere usar un único driver JDBC para acceder a las mismas. ________________________________________________________________________________________ Proyecto de Software 104 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ • Al residir la traducción en el servidor del middleware, los clientes no necesitan librerías específicas, tan solo el driver. Inconvenientes: La desventaja principal reside en la configuración del servidor donde se encuentra el middleware. Necesitará librerías específicas para cada motor de base de datos distinto, etc. Tipo 4: Driver java puro (acceso directo a Base de Datos): “THIN” Convierte o traduce las llamadas al API JDBC en llamadas al protocolo de red usado por el motor de bases de datos, lo que en realidad es una invocación directa al motor de bases de datos. • Ventajas: 100 % portable. Buen rendimiento. El cliente sólo necesita el driver. • Inconvenientes: Al ser independiente de la plataforma, no aprovecha las características específicas del S.O. 2. INSTALACION DE JDBC El proceso de instalación de JDBC se podría dividir en tres partes distintas: Descargar el API de JDBC JDBC viene incluido en el corazón de la plataforma Java, tanto en la J2SE como en la J2EE. http://java.sun.com/products/jdbc/download.html Instalar el driver en la máquina cliente (máquina que crea las conexiones) La instalación del driver dependerá en gran medida del tipo de driver que hayamos seleccionado. Para los tipo 1 y 2, además de las librerías del driver, necesitaremos ciertas librerías propias del cliente del motor de base de datos que estemos usando y quizá ficheros de configuración. Para el tipo 3, en la máquina cliente tan solo necesitaremos el driver. En el servidor donde resida el middleware, necesitaremos drivers específicos de cada motor de base de datos y en su caso, librerías propias del cliente del motor de base de datos. Para el tipo 4, tan solo necesitaremos el driver. ________________________________________________________________________________________ Proyecto de Software 105 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ Instalar el controlador o motor de base de datos Evidentemente, necesitaremos tener instalado un motor de base de datos. Esta instalación será distinta en función de la Base de Datos y del S.O. donde vaya a correr. 3. ARQUITECTURAS JDBC La arquitectura básica de JDBC es simple. Una clase llamada DriverManager provee un mecanismo para controlar un conjunto de drivers JDBC. Esta clase intenta cargar los drivers especificados en la propiedad del sistema jdbc.drivers. También podemos cargar un driver explicitamente usando Class.forName(). Durante la carga, el driver intentará registrarse a si mismo usando el método clase DriverManager.registerDriver(). Cuando se invoque al método driverManager.getConnection(), ésta buscará el primer driver de los registrados que pueda manejar una conexión como la descrita en la URL y retornará un objeto que implemente el interfaz java.sql.Connection. Sin embargo, en función de la localización de la base de datos, el driver, la aplicación y el protocolo de comunicación usado, nos podemos encontrar distintos escenarios que accedan a Base de Datos a través de JDBC: 1. Aplicaciones standalone 2. Applets comunicando con un servidor Web 3. Aplicaciones y applets comunicando con una base de datos a través de un puente JDBC/ODBC. 4. Aplicaciones accediendo a recursos remotos usando mecanismos como Java RMI. Todos ellos se pueden agrupar en dos tipos distintos de arquitecturas: Arquitecturas JDBC en dos capas La aplicación que accede a la base de datos reside en el mismo lugar que el driver de la base de datos. El driver accederá al servidor donde corra el motor de base de datos. En este caso, será el driver el encargado de manejar la comunicación a través de la red. En el ejemplo, una aplicación java corriendo en una máquina cliente que usa el driver también local. Toda la comunicación a través de la red con la base de datos será manejada por el driver de forma transparente a la aplicación Java. Arquitecturas JDBC en tres capas Una aplicación o applet corriendo en una máquina y accediendo a un driver de base de datos situado en otra máquina. Ejemplos de esta situación: ________________________________________________________________________________________ Proyecto de Software 106 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ 1. Un applet accediendo al driver a través de un Web Server 2. Una aplicación accediendo a un servidor remoto que comunica localmente con el driver 3. Una aplicación comunicando con un servidor de aplicaciones que accede a la base de datos por nosotros. 4. PASOS PARA EL ACCESO DE BASE DE DATOS CON JDBC Lo primero que tenemos que hacer es asegurarnos de que disponemos de la configuración apropiada. Esto incluye los siguientes pasos. 1. Instalar Java y el JDBC en nuestra máquina. Para instalar tanto la plataforma JAVA como el API JDBC, simplemente tenemos que seguir las instrucciones de descarga de la última versión del JDK (Java Development Kit). Junto con el JDK también viene el JDBC.. El código de ejemplo de desmostración del API del JDBC 1.0 fue escrito para el JDK 1.1 y se ejecutará en cualquier versión de la plataforma Java compatible con el JDK 1.1, incluyendo el JDK1.2. Teniendo en cuenta que los ejemplos del API del JDBC 2.0 requieren el JDK 1.2 y no se podrán ejecutar sobe el JDK 1.1. 2. Instalar un driver en nuestra máquina. Nuestro Driver debe incluir instrucciones para su instalación. Para los drivers JDBC escritos para controladores de bases de datos específicos la instalación consiste sólo en copiar el driver en nuesta máquina; no se necesita ninguna configuración especial. El driver "puente JDBC-ODBC" no es tan sencillo de configurar. Si descargamos las versiones Solaris o Windows de JDK 1.1, automáticamente obtendremos una versión del driver Bridge JDBC-ODBC, que tampoco requiere una configuración especial. Si embargo, ODBC, si lo necesita. Si no tenemos ODBC en nuestra máquina, necesitaremos preguntarle al vendedor del driver ODBC sobre su instalación y configuración. 3. Instalar nuestro Controlador de Base de Datos si es necesario. Si no tenemos instalado un controlador de base de datos, necesitaremos seguir las instrucciones de instalación del vendedor. La mayoría de los usuarios tienen un controlador de base de datos instalado y trabajarán con un base de datos establecida. ________________________________________________________________________________________ Proyecto de Software 107 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ 4.1 Procedimiento de conexión y acceso a datos con JDBC Consideraciones previas El proceso de acceso a una Base de Datos a través de JDBC, exige dar una serie de pasos previos antes de crear la conexión al motor de Base de Datos. El primer paso es determinar el entorno en el que el proyecto va a ser instalado, y más en concreto, que parámetros del entorno afectan directamente a JDBC: ¿ Qué motor de Base de Datos vamos a usar ? Debemos considerar las características específicas de una base de datos, como por ejemplo, como mapear los tipos de datos SQL a Java. ¿ Qué driver vamos a usar ? Es probable encontrarnos varios drivers distintos para la misma fuente de datos. Debemos saber detectar cual es el driver más adecuado para nuestra aplicación, por ejemplo, si elegimos un driver ODBC/JDBC, tendremos más flexibilidad para elegir distintas fuentes de datos, pero si por ejemplo trabajamos con una Base de Datos Oracle, un driver JDBC diseñado específicamente para esta base de datos será mucho más eficiente. ¿ Donde estará localizado el driver ? En función de donde se encuentre el driver físicamente, debemos considerar aspectos de rendimiento y seguridad. Por ejemplo, si cargamos el driver desde un servidor remoto tendremos que considerar aspectos sobre seguridad de Java. 4.2 Procedimiento de conexión El procedimiento de conexión con el controlador de la base de datos, independientemente de la arquitectura es siempre muy similar. 1. Cargar el driver. Cualquier driver JDBC, independientemente del tipo debe implementar el interfaz java.sql.Driver. La carga del driver se puede realizar de dos maneras distintas: • Definiendo los drivers en la variable sql.driver (variable que mantiene todos las clases de los drivers separados por comas) Cuando la clase DriverManager se inicializa, busca esta propiedad en el sistema. • El programador puede forzar la carga de un driver específico, usando el método Class.forName(driver). 2. Registro del driver. Independientemente de la forma de carga del driver que llevemos a cabo, será responsabilidad de cada driver registrarse a sí mismo, usando el método DriverManager.registerDriver. Esto permite a la clase DriverManager, usar cada driver para crear conexiones con el ________________________________________________________________________________________ Proyecto de Software 108 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ controlador de Base de Datos. Por motivos de seguridad, la capa que gestiona JDBC, controlará en todo momento que driver es el que se está usando, y cuando se realicen conexiones, sólo se podrán usar drivers que estén en el sistema local de ficheros o que usen el mismo ClassLoader que el código que está intentando crear la conexión. En el primero de los casos (a través de la propiedad sql.driver), JDBC usará el primer driver que permita conectarse correctamente a la Base de Datos. Crear una conexión. El objetivo es conseguir un objeto del tipo java.sql.Connection a través del método driverManager.getConnection(String url). La capa de gestión, cuando este método es invocado, tratará de encontrar un driver adecuado para conectar a la base de datos especificada en la URL, intentándolo por el orden especificado en la variable sql.driver. Cada driver debería examinar si ellos proveen el “subprotocolo” que especifica la URL. JDBC provee un mecanismo para permitir nombrar las bases de datos, para que los programadores puedan especificar a que base de datos desean conectarse. Este sistema debe tener las siguientes características: • • • • Drivers de diferentes tipos pueden tener diferentes formas de nombrar las bases de datos. Este sistema de nombrado debe ser capaz de acoger diversos parámetros de configuración de la red. Debería ser capaz de acoger cierto nivel de indirección, para que los nombres pudiesen ser resueltos de alguna manera (DNS...) URL. Estas características están ya estandarizadas en el concepto de URL. JDBC recomienda la siguiente estructura de nombrado de bases de datos: jdbc:<subprotocol>:<subname> ♦ jdbc: parte fija ♦ subprotocol: mecanismo particular se acceso a base de datos ♦ subname: dependerá del subprotocolo. JDBC recomienda seguir también la convención de nombrado URL: //hostname:port/subsubname • • El subprotocolo odbc: Este subprotocolo ha sido reservado para aquellos que quieran seguir la convención ODBC para nombrado de bases de datos: jdbc:odbc:<data-source-name>[;<attribute-name>=<attribute-value>] El API JDBC, también provee la posibilidad de pasar una lista de propiedades para crear la conexión: DriverManager.getConnection(String URL, Properties props); Dos propiedades definidas por convención son • user • password ________________________________________________________________________________________ Proyecto de Software 109 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ TEMA 3 ACCESO A BASE DE DATOS LA API DE JDBC Pasamos en este punto a dar una breve descripción de cada una de las interfaces y clases que componen el paquete java.sql. 1. INTERFACES Driver Es la interfaz que deben implementar todos los controladores JDBC en alguna de sus clases. Cuando se cargue una clase Driver, ésta creará una instancia de sí misma que se registrará posteriormente mediante el administrador de controladores. Connection Representa una sesión de trabajo con una base de datos. Sus métodos, aparte de permitir modificarla y consultar sus tablas, también permiten obtener información sobre su estructura. Statement Mediante esta interfaz, se envían las órdenes SQL individuales a la base de datos a través del controlador JDBC y se recogen los resultados de las mismas. ResultSet Mediante esta interfaz, se organizan en tablas los resultados de las órdenes SQL. Las filas deben leerse una detrás de otra, pero las columnas pueden leerse en cualquier orden. ResultSetMetaData Contiene información sobre las columnas de los objetos ResultSet. PreparedStatement Los objetos PreparedStatement contienen órdenes SQL precompiladas que pueden por tanto ejecutarse muchas veces. CallableStatement Los objetos CallableStatement se emplean para ejecutar procedimientos almacenados. DatabaseMetaData Contiene diversa, y abundantísima, información sobre la base de datos. Algunos de sus métodos devuelven los resultados como tablas ResultSet, y pueden tratarse como éstos. ________________________________________________________________________________________ Proyecto de Software 110 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ Clases DriverManager Contiene métodos para trabajar con un conjunto de controladores JDBC. Types Define una serie de constantes usadas para identificar los tipos de datos SQL. Time Esta clase añade a la clase Date del paquete java.util los métodos y características para trabajar con cláusulas de escape JDBC identificándolo como un tipo TIME de SQL. Date Esta clase añade a la clase Date del paquete java.util los métodos y características para trabajar con cláusulas de escape JDBC como un tipo DATE de SQL. Timestamp Esta clase añade a la clase Date del paquete java.util los métodos y características para trabajar con cláusulas de escape JDBC identificándolo como un tipo TIMESTAMP de SQL. DriverPropertyInfo Contiene métodos para averiguar y trabajar con las propiedades de los controladores JDBC. SQLException Proporciona diversa inmformación acerca de los errores producidos durante el acceso a una base de datos. SQLWarning Contiene información sobre los avisos que se producen durante el trabajo con una base de datos. Los avisos se encadenan al objeto que los provocó. 2. INSTRUCCIONES SQL En JDBC existen dos formas principales de ejecutar y crear sentencias SQL, la primera es usando un Statement, y la segunda es creando un PreparedStatement. Statement Fue la primera manera de enviar sentencias SQL usando java, este método está cada vez más en desuso, ya que no protege ante la inyección de código SQL. La forma de usarlo seria la siguiente: ________________________________________________________________________________________ Proyecto de Software 111 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ Statement stmt = con.createStatement(); String condicion="nombre='"+nombreDeLaWeb+"' AND pass='"+passDeLaWeb+"'";//Creamos la sentencia SQL String sqlSelect = "SELECT * FROM tabla WHERE "+condicion; //Ejecutamos la sentencia SQL stmt.executeQuery(sqlSelect); Nota:Observeseque sipassDeLaWebes = “' OR '1'='1 ”tendríamos una un fallo en la seguridadde la aplicación ya que tendría acceso sin saber la contraseña. PreparedStatement Es otra forma de crear sentencias SQL, esta forma evita la inyección y si la sentencia es más larga se entiende mejor. La forma de usarlo es la siguiente: //Creamos la sentencia SQL String sqlSelect = "SELECT * FROM tabla WHERE nombre=? AND pass=?";//Ejecutamos la sentencia SQL PreparedStatement ps = con.createPreparedStatement(sqlSelect); ps.setString(1,nombreDeLaWeb); ps.setString(2,passDeLaWeb); ps.executeQuery(); Obtener los resultados Cada vez que ejecutamos una sentencia SELECT tanto el Statement como el PreparedStatement al hacer el executeQuery() nos devuelve un objeto de tipo ResultSet, este objeto como se puede adivinar por el nombre es un Set, por lo tanto es iterable. Para obtener los resultados de la base de datos usando esta clase resulta muy facil, solo hay que usar getes, es decir si en la base de datos tenemos almacenado un entero haríamos getInt(“nombreCol”); si es una cadena getString(“nombreCol”); ... veamos un trozo de código: String sqlSelect = "SELECT * FROM tabla"; ResultSetrs = con.createStatement().executeQuery(sqlSelect);while(rs.next()){ System.out.println(rs.getString("nombre")); System.out.println(rs.getString("pass")); } ________________________________________________________________________________________ Proyecto de Software 112 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ TEMA 4 ACCESO A BASE DE DATOS MEDIANTE ODBC El puente JDBC-ODBC (Ejemplo de Conexión entre Java y Access). 1. EL PUENTE JDBC-ODBC En este punto se explica la manera de implementar el puente JDBC-ODBC para que una aplicación escrita en Java pueda acceder una base de datos en MS Access. El puente JDBC-ODBC también puede implementarse para conectar a Java con bases de datos como FoxPro. Se mostrará también la manera de crear y configurar un origen de datos con nombre DSN. Introducción al Puente JDBC-ODBC Existiendo ODBC drivers, estos pueden utilizarse para aplicaciones Java por medio de el puente JDBC-ODBC proporcionado por Sun. Usar el puente JDBCODBC no es una solución ideal dado que requiere de la instalación de drivers ODBC y entradas de registro. Los drivers ODBC a su vez son implementados nativamente lo que compromete el soporte multiplataforma y la seguridad de applets. El puente es por si mismo un driver basado en tecnología JDBC que está definida en la clase sun.jdbc.odbc.JdbcOdbcDriver. El puente JDBC-ODBC debe ser considerado como una solución transitoria. Sun Microsystems y las tecnologías DataDirect estan trabajando para hacerlo más confiable y robusto, pero ellos no lo consideran un producto recomendado. Con el desarrollo de drivers puros en Java, el puente se volverá innecesario. Lo ideal es “Java Puro”: no código nativo, no características de plataforma dependiente. Sin embargo puede utilizarse si se requiere desarrollar inmediatamente sin poder esperar a que el fabricante de la base de datos utilizada proporcione el driver JDBC. Los drivers parcialmente nativos, como el puente JDBC-ODBC, le permiten crear programas que pueden fácilmente ser adaptados a drivers puros en Java tan pronto como estén disponibles. Hasta aquí con la pequeña introducción acerca del puente JDBC-ODBC. La última parte de esta introducción nos habla de adaptar nuestras aplicaciones basadas en puentes JDBC-ODBC a drivers puros JDBC. Esto es verdadero, sin casi nada de cambio en el código. Personalmente he creado proyectos que trabajan inicialmente con Access y el puente JDBC-ODBC, posteriormente he migrado estas aplicaciones a servidores como MySQL y SQLServer, para los cuales solo debe implementarse el driver JDBC correspondiente. ________________________________________________________________________________________ Proyecto de Software 113 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ 2. CONECTANDO UNA APLICACIÓN JAVA CON ACCESS MEDIANTE PUENTE JDBC-ODBC Supongamos ahora que necesita conectar una aplicación en Java con Access, sea porque es su primer pinino o porque realmente los requerimientos de su sistema se adaptan a esta base de datos. Para este ejemplo, supongamos que la base de datos de Access se crea en el siguiente directorio: C:\proyecto\bd1.mdb Antes de mostrar el código en java para conectar la aplicación a esta base de datos, se mostrará la forma de crear el driver u origen de datos (DSN) de la base de datos, para ello se procede de la siguiente manera (Windows 2000, XP) 1. 2. 3. 4. 5. 6. 7. 8. Hacer click en el botón inicio. Seleccionar Panel de Control. Hacer doble click en el icono de Herramientas administrativas. Hacer doble click en orígenes de datos (ODBC). Aparecerá la pantalla Administrador de orígenes de datos ODBC. De la pantalla anterior elija la pestaña DSN de usuario y posteriormente oprima el botón Agregar. Elija de la lista de drivers desplegada, el driver “Microsoft Access Driver (*.mdb)” y enseguida oprima el botón finalizar. Aparecerá la pantalla de Configuración de ODBC Microsoft Access, llene los campos de esta pantalla como se muestra a continuación. En esta misma pantalla, en el apartado Base de datos oprima el botón Seleccionar y seleccione el directorio de la base de datos (C:\proyecto\bd1.mdb). Oprima enseguida el botón Avanzadas. En la pantalla de opciones avanzadas debe crear un Nombre de inicio de sesión y Contraseña . En nuestro caso el inicio de sesión será cvazquez y la contraseña vazquez. En el apartado Opciones, seleccione el campo Driver y establezca el Valor de Driver como PDRV o como lo haya llamado. Oprima el botón Aceptar. Oprima a su vez los botones aceptar de las pantallas subsecuentes. Con esto el driver ya ha sido creado y puede ser accedido desde nuestra aplicación. El resultado de la creación del driver de la base de datos que se usa en este proyecto de ejemplo es el siguiente: Nombre de driver: PDRV Nombre de inicio de sesión: cvazquez Contraseña: vazquez Estos campos serán utilizados desde la aplicación en Java para conectarse a la base de datos. ________________________________________________________________________________________ Proyecto de Software 114 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ La siguiente clase en Java, contiene los métodos necesarios para conectar nuestra aplicación con la base de datos Access. import java.sql.*; public class cConnection { /*Atributos*/ private String url = "jdbc:odbc:"; private String driver = "PDRV"; private String usr = "cvazquez"; private String pswd = "vazquez"; private Connection con; /*Constructor, carga puente JDBC-ODBC*/ public cConnection() { loadDriver(); } /** * Carga el driver de la conexión a la base de datos */ private void loadDriver() { try { //Instancía de una nueva clase para el puente //sun.jdbc.odbc.JdbcOdbcDriver //El puente sirve entre la aplicación y el driver. Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver" ); } catch(ClassNotFoundException e) { System.out.println("Error al crear el puente JDBC-ODBC"); } } /** * Obtiene una conexión con el nombre del driver especificado * @param driverName Nombre del driver de la base de datos * @return */ public Connection mkConection() { url = url + driver; System.out.println("Estableciendo conexión con " + url); ________________________________________________________________________________________ Proyecto de Software 115 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ try { //Obtiene la conexión con = DriverManager.getConnection( url,usr,pswd); } catch(SQLException sqle) { System.out.println("No se pudo establecer la conexión"); return null; } System.out.println("Conexión establecida con:\t " + url); //Regresa la conexión return con; } /* Cerrar la conexión. */ public boolean closeConecction() { try { con.close(); } catch(SQLException sqle) { System.out.println("No se cerro la conexión"); return false; } System.out.println("Conexión cerrada con éxito "); return true; } } El siguiente fragmento de código muestra la manera de establecer la conexión con la base de datos. … //Crear un objeto de la clase de conexión cConnection conect = new cConnection(); //Obtener la conexión Connection con = conect.mkConection(); if(con == null){ //Error al establecer la conexión } ________________________________________________________________________________________ Proyecto de Software 116 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ Si la conexión fue realizada con éxito, podemos ejecutar sentencias SQL. El siguiente fragmento de código muestra la ejecución de la sentencia SQL select (suponiendo que tablas de la base de datos fueron creadas con anterioridad). … ResultSet rs = null; Statement stm = con.createStatement(); String strSQL = "SELECT * FROM mitabla"; //Ejecuta la consulta SQL rs = stm.executeQuery(strSQL); //Trabajar con el result set… //Cerrar todo rs.close(); stm.close(); boolean isClosed = conect.closeConecction() if(!isClosed){ //Error al cerrar la conexión } ________________________________________________________________________________________ Proyecto de Software 117 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ TEMA 5 EJEMPLO DE APLICACIÓN ACCESO A UNA BD ACCESS 1. PREPARACIÓN DEL ENTORNO DE DESARROLLO Antes de comenzar a estudiar las distintas clases proporcionadas por JDBC, vamos a abordar la creación de una fuente de datos ODBC, a través de la cuál accederemos a una Base de Datos Interbase. Hemos escogido este modo de acceso en lugar de utilizar un driver JDBC nativo para Interbase porque ello nos permitirá mostrar el uso de drivers ODBC: de este modo, aquél que no disponga de Interbase, podrá crear una Base de Datos Access, BTrieve o de cualquier otro tipo para el que sí tenga un driver ODBC instalado en su sistema, y utilizar el código fuente incluido con el diskette que acompaña a la revista. El Listado A muestra el SQL utilizado para crear nuestra pequeña Base de Datos de ejemplo, incluida en el diskette. Nótese que el campo ID de la tabla CONTACTOS es del tipo contador o autoincremento, y requiere de la definición de un trigger y un contador en Interbase: otros sistemas de Base de Datos pueden tener soporte directo para este tipo de campo, lo que hará innecesario parte del código SQL del Listado A. /* No se hace commit automatico de sentencias de definicion de datos, DDL */ SET AUTODDL OFF; /********** Creacion de la base de datos **********/ /* Necesario USER y PASSWORD si no se ha hecho login previo */ CONNECT "h:\work\artículos\jdbc\1\fuente\agenda.gdb" USER "SYSDBA" PASSWORD "masterkey"; /* Eliminación de la Base de datos */ DROP DATABASE; CREATE DATABASE "h:\work\artículos\jdbc\1\fuente\agenda.gdb" USER "SYSDBA" PASSWORD "masterkey"; CONNECT "h:\work\artículos\jdbc\1\fuente\agenda.gdb" USER "SYSDBA" PASSWORD "masterkey"; /***** Creación de la tabla de Contactos *****/ CREATE TABLE CONTACTOS ( ID INTEGER NOT NULL, NOMBRE CHAR(30) NOT NULL, EMPRESA CHAR(30), CARGO CHAR(20), ________________________________________________________________________________________ Proyecto de Software 118 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ DIRECCION CHAR(40), NOTAS VARCHAR(150), PRIMARY KEY( ID ) ); CREATE INDEX I_NOMBRE ON CONTACTOS( NOMBRE ); /* Creamos un contador para CONTACTOS.ID */ CREATE GENERATOR CONTACTOS_ID_Gen; SET GENERATOR CONTACTOS_ID_Gen TO 0; /* Creamos trigger para insertar código del contador CONTACTOS */ SET TERM !! ; CREATE TRIGGER Set_CONTACTOS_ID FOR CONTACTOS BEFORE INSERT AS BEGIN New.ID = GEN_ID( CONTACTOS_ID_Gen, 1); END !! SET TERM ; !! en /**** Creación de la tabla de teléfonos de contactos ****/ CREATE TABLE TELEFONOS ( CONTACTO INTEGER NOT NULL, TELEFONO CHAR(14) NOT NULL, NOTAS VARCHAR(50), PRIMARY KEY( CONTACTO, TELEFONO ) ); /* Se hace COMMIT */ EXIT; Listado A: SQL utilizado para crear la Base de Datos de ejemplo, AGENDA.GDB, con Interbase. El primer paso, evidentemente, será crear la Base de Datos, que en nuestro caso se llama AGENDA.GDB. Una vez hecho esto, debemos crear una fuente de datos ODBC: para ello, se debe escoger en el Panel de Control el icono "32bit ODBC", correspondiente al gestor de fuentes de datos ODBC, y hacer click sobre el botón "Add..." de la página "User DSN", con lo que aparecerá la ventana de la Figura A. Figura A: Ventana del gestor de fuentes de datos ODBC utilizada para añadir nuevas fuentes de datos. Hecho esto, queda configurar la fuente de datos, indicando el nombre que le vamos a dar (que utilizaremos a la hora de conectarnos a la Base de Datos), así ________________________________________________________________________________________ Proyecto de Software 119 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ como otros datos. La Figura B muestra la ventana de configuración de una fuente de datos Interbase, utilizando un driver de Visigenic. Data Source Name es el nombre que utilizaremos para identificar la fuente de datos, y en nuestro caso será JDBC_AGENDA. Description es un texto explicativo optativo, y Database especifica cómo se llama y dónde se encuentra la Base de Datos que vamos a acceder a través de esta fuente de datos. Para poder utilizar los programas de ejemplo, deberemos introducir aquí el directorio donde copiemos los ejemplos, seguido del nombre de la Base de Datos, AGENDA.GDB. En mi caso, Database es h:\work\artículos\jdbc\1\fuente\AGENDA.GDB. El hecho de que para acceder una Base de Datos mediante ODBC se utilice el nombre de la fuente de datos, en lugar del nombre de la Base de Datos, nos permite cambiar la ubicación e incluso el nombre de la misma sin tener que modificar el código fuente de nuestros programas, ya que estos utilizarán el nombre de la fuente de datos para identificarla. En cuanto a los demás parámetros de la fuente de datos, se debe escoger como Network Protocol la opción <local>. El nombre de usuario (User name) y la contraseña (Password), se indicarán manualmente desde dentro de nuestro programa: no suele ser aconsejable especificarlo en la misma fuente de datos, dado que esto daría acceso a la Base de Datos a cualquier programa que acceda a la misma a través de la misma. Figura B: Ventana de configuración de una fuente de datos ODBC. Como último paso, no debemos olvidar arrancar el servidor de Base de Datos, si utilizamos un gestor que incluye uno, como sucede con Interbase. 2. EL PRIMER PROGRAMA CON JDBC Como es lógico, la API JDBC incluye varias clases que se deben utilizar para conseguir acceso a una Base de Datos. La Tabla A muestra la lista de clases e interfaces más importantes que JDBC ofrece, junto con una breve descripción. Estas clases se encuentran en el paquete java.sql. Clase/Interf ace Driver Descripción Permite conectarse a una Base de Datos: cada gestor de Base de Datos requiere un Driver distinto. DriverManag Permite gestionar todos los Drivers instalados en el er sistema. DriverProper Proporciona diversa información acerca de un Driver. tyInfo Connection Representa una conexión con una Base de Datos. Una aplicación puede tener más de una conexión a ________________________________________________________________________________________ Proyecto de Software 120 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ más de una Base de Datos. DatabaseMe Proporciona información acerca de una Base de tadata Datos, como las tablas que contiene, etc. Statement Permite ejecutar sentencias SQL sin parámetros. PreparedStat Permite ejecutar sentencias SQL con parámetros de ement entrada. CallableStat Permite ejecutar sentencias SQL con parámetros de ement entrada y salida, típicamente procedimientos almacenados. ResultSet Contiene las filas o registros obtenidos al ejecutar un SELECT. ResultSetMe Permite obtener información sobre un ResultSet, tadata como el número de columnas, sus nombres, etc. Tabla A: Clases e interfaces definidos por JDBC. El mejor modo de comenzar a estudiar la API de JDBC es empezar con un pequeño programa. El Listado B corresponde a un programa que carga el driver utilizado para conectarnos a bases de datos utilizando controladores ODBC. // Importamos el paquete que da soporte a JDBC, java.sql import java.sql.*; import java.io.*; class jdbc1 { public static void main( String[] args ) { try { // Accederemos a la fuente de datos // ODBC llamada JDBC_AGENDA String urlBD = "jdbc:odbc:JDBC_AGENDA"; String usuarioBD = "SYSDBA"; String passwordBD = "masterkey"; // Cargamos la clase que implementa // el puente JDBC=>ODBC Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver" ); // Establecemos una conexion con la Base de Datos System.out.println( "Estableciendo conexión con " + urlBD + "..." ); Connection conexion = DriverManager.getConnection( urlBD, usuarioBD, passwordBD ); System.out.println( "Conexión establecida." ); conexion.close(); System.out.println("Conexión a " + urlBD " cerrada."); ________________________________________________________________________________________ Proyecto de Software 121 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ } catch( Exception ex ) { System.out.println( "Se produjo un error." ); } } } Listado B: Código básico para llevar a cabo la conexión con una Base de Datos a través de ODBC. Establecer una conexión con una Base de Datos mediante JDBC es sencillo: en primer lugar, registramos el Driver a utilizar (que en nuestro caso es el puente JDBC/ODBC), mediante el código: Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver" ); Si no se registra este driver, se producirá un error intentar la conexión. A continuación, se lleva a cabo la conexión a la Base de Datos mediante el código: Connection conexion passwordBD ); = DriverManager.getConnection( urlBD, usuarioBD, La clase DriverManager gestiona los Drivers registrados en el sistema: al llamar a getConnection, recorre la lista de Drivers cargados hasta encontrar uno que sea capaz de gestionar la petición especificada por urlBD, que en nuestro ejemplo es "jdbc:odbc:JDBC_AGENDA". Los parámetros usuarioBD y passwordBD corresponden al nombre del usuario y su contraseña, necesarios la mayor parte de las veces para acceder a cualquier Base de Datos. Cómo se especifica la Base de Datos a utilizar depende del Driver utilizado: en nuestro caso, en que utilizamos el puente ODBC/JDBC, todas las peticiones serán de la forma "jdbc:odbc:NOMBRE_FUENTE_DATOS". La cadena utilizada para indicar la Base de Datos siempre tiene tres partes, separadas por el carácter ":". La primera parte es siempre "jdbc". La segunda parte indica el subprotocolo, y depende del Sistema de Gestión de Base de Datos utilizado: en nuestro caso, es "odbc", pero para SQLAnyware, por ejemplo, es "dbaw". La tercera parte identifica la Base de Datos concreta a la que nos deseamos conectar, en nuestro caso la especificada por la fuente de datos ODBC llamada "JDBC_AGENDA". Aquí también se pueden incluir diversos parámetros necesarios para establecer la conexión, o cualquier otra información que el fabricante del SGBD indique. Siguiendo con el Listado B, a continuación se establece una conexión con la Base de Datos, mediante el código: ________________________________________________________________________________________ Proyecto de Software 122 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ Connection conexion = controlador.connect( urlBD, usuarioBD, passwordBD ); Acto seguido cerramos la conexión, con: conexión.close(); Es conveniente cerrar las conexiones a Bases de Datos tan pronto como dejen de utilizarse, para liberar recursos rápidamente. Sin embargo, ha de tenerse en cuenta que establecer una conexión es una operación lenta, por lo que tampoco se debe estar abriendo y cerrando conexiones con frecuencia. 3. CONSULTAS EN JDBC Un programa que realice una consulta y quiera mostrar el resultado de la misma requerirá del uso de varias clases: la priemra es DriverManager, que permitirá llevar a cabo una conexión con una Base de Datos, conexión que se representa mediante un objeto que soporta el interface Connection. También será necesario además ejecutar una sentencia SELECT para llevar a cabo la consulta, que se representará por un objeto que soporte el interface Statement (o PreparedStatement, o CallableStatement, que estudiaremos más adelante). Una sentencia SELECT puede devolver diversos registros o filas: esta información es accesible mediante un objeto que soporte el interface ResultSet. El Listado C muestra el código fuente correspondiente a un pequeño programa que muestra los nombres de todos los contactos que hemos almacenado en la Base de Datos AGENDA.GDB, y que utiliza las clases anteriormente mencionadas. import java.sql.*; import java.io.*; class jdbc2 { public static void main( String[] args ) { try { // Accederemos al alias ODBC llamado JDBC_DEMO String urlBD = "jdbc:odbc:JDBC_AGENDA"; String usuarioBD = "SYSDBA"; String passwordBD = "masterkey"; // Cargamos el puente JDBC=>ODBC Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver"); // Intentamos conectarnos a la base de datos JDBC_DEMO Connection conexion = DriverManager.getConnection ( urlBD, usuarioBD, passwordBD ); ________________________________________________________________________________________ Proyecto de Software 123 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ // Creamos una sentencia SQL Statement select = conexion.createStatement(); // Ejecutamos una sentencia SELECT ResultSet resultadoSelect = select.executeQuery( "SELECT * FROM CONTACTOS ORDER BY NOMBRE" ); // Imprimimos el nombre de cada contacto encontrado // por el SELECT System.out.println( "NOMBRE" ); System.out.println( "------" ); int col = resultadoSelect.findColumn( "NOMBRE" ); boolean seguir = resultadoSelect.next(); // Mientras queden registros... while( seguir ) { System.out.println( resultadoSelect.getString( col ) ); seguir = resultadoSelect.next(); }; // Liberamos recursos rápidamente resultadoSelect.close(); select.close(); conexion.close(); } catch( SQLException ex ) { // Mostramos toda la informaci¢n sobre // el error disponible System.out.println( "Error: SQLException" ); while (ex != null) { System.out.println ("SQLState: " + ex.getSQLState ()); System.out.println ("Mensaje: " + ex.getMessage ()); System.out.println ("Vendedor: " + ex.getErrorCode ()); ex = ex.getNextException(); System.out.println (""); } } catch( Exception ex ) { System.out.println( "Se produjo un error inesperado" ); } } } ________________________________________________________________________________________ Proyecto de Software 124 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ Listado C: Ejecución de una sentencia SELECT e impresión de los resultados con java. Echemos un vistazo al código en el Listado C. El código para registrar el controlador (Driver) a utilizar es el mismo del Listado B. A continuación se crea una sentencia SQL (Statement), y se ejecuta, obteniendo seguidamente el resultado de la misma (ResultSet), para lo que se utiliza el siguiente código: // Creamos una sentencia SQL Statement select = conexion.createStatement(); // Ejecutamos una sentencia SELECT ResultSet resultadoSelect = select.executeQuery( "SELECT * FROM CONTACTOS ORDER BY NOMBRE" ); Además del método executeQuery, utilizado para ejecutar sentencias SELECT, Statement también proporciona otros métodos: executeUpdate ejecuta una sentencia UPDATE, DELETE, INSERT o cualquier otra sentencia SQL que no devuelva un conjunto de registros, y retorna el número de registros afectados por la sentencia (o -1 si no los hubo). El método getResultSet devuelve el ResultSet de la sentencia, si lo tiene, mientras que getUpdateCount devuelve el mismo valor que executeUpdate. Es posible limitar el número máximo de registros devuelto al hacer un executeQuery mediante setMaxRows, y averiguar dicho número mediante getMaxRows. Es posible también obtener el último aviso generado al ejecutar una sentencia, mediante getWarning, así como limitar el tiempo en segundos que el controlador esperará hasta que el SGBD devuelva un resultado, mediante setQueryTimeout. Por último, el método close libera los recursos asociados a la sentencia. El interface ResultSet es el que encapsula el resultado de una sentencia SELECT. Para recuperar la información es necesario acceder a las distintas columnas (o campos), y recuperarla mediante una serie de métodos getString, getFloat, getInt, etc. Al utilizar estos métodos debe indicarse el número correspondiente a la columna que estamos accediendo: si lo desconocemos, podemos averiguar el número correspondiente a una columna, dado el nombre, mediante findColumn. Por último, dado que un ResultSet puede contener más de un registro, para ir avanzando por la lista de registros que contiene deberemos utilizar el método next, que devuelve un valor booleano indicando si existe otro registro delante del actual. El uso de los métodos anteriores se ilustra en el Listado C, que recorre la lista de contactos en la Base de Datos, obteniendo el valor del campo (o columna) "NOMBRE", e imprimiéndolo, mediante un bucle en el que se llama repetidamente al método next y a getString. Además de estos métodos, ResultSet también cuenta con getWarnings, que devuelve el primer aviso obtenido al manipular los registros, así como wasNull, ________________________________________________________________________________________ Proyecto de Software 125 Unidad IV: Acceso A Base De Datos Mediante Jdbc- Odbc ________________________________________________________________________________________ que indica si el contenido de la última columna accedida es un NULL SQL. Por último, el método close libera los recursos asociados al ResultSet. Excepción SQLExcepti on SQLWarnin g DataTrunca tion Descripción Error SQL. Advertencia SQL. Producida cuando se truncan datos inesperadamente, por ejemplo al intentar almacenar un texto demasiado largo en un campo. Tabla B: Excepciones que puede generar la API de JDBC Al utilizar la API JDBC es posible obtener diversos errores debido a que se ha escrito incorrectamente una sentencia SQL, a que no se puede establecer una conexión con la Base de Datos por cualquier problema, etc. El paquete java.sql proporciona tres nuevas excepciones, listadas en la Tabla A. En el Listado C se muestra un ejemplo de uso de las excepciones del tipo SQLException: es posible que se encadenen varias excepciones de este tipo, motivo por el que esta clase proporciona el método getNextException. La excepción SQLWarning es silenciosa, no se suele elevar: para averiguar si la Base de Datos emitió un aviso se debe utilizar el método getWarnings de Statement y otras clases, que devuelve excepciones de esta clase. Dado que es posible que se obtengan varios avisos encadenados, esta clase proporciona el método getNextWarning, que devuelve el siguiente aviso, si lo hay. Por último, las excepciones del tipo DataTruncation se producen cuando se trunca información inesperadamente, ya sea al leerla o al escribirla. ________________________________________________________________________________________ Proyecto de Software 126