Está en la página 1de 30

JDBC son las siglas de Java DataBase Connectivity, o lo que es lo mismo: Conectividad de Bases de Datos Java.

Estas siglas son concedidas a una API que est especialmente dedicada a la gestin de bases de datos, permitiendo ejecutar sentencias en SQL (Structured Query Language o Lenguaje Estructurado de Consultas ), con las que se pueden crear tablas e ndices, modificar datos, eliminar informacin, aadir, consultar, etc.

Bases de datos
Las bases de datos son conjuntos de tablas, las cuales son entidades que almacenan la informacin de manera estructurada, de forma similar a una rejilla de celdas. Cada fila representa a un registro o coleccin de datos referente a una entidad, individuo, artculo, etc. Cada columna representa un campo o tipo de info rmacin, como puede ser un nombre, una direccin, un importe, una fecha, etc. Las tablas pueden tener asociados ndices, los cuales permiten una optimizacin de bsqueda a travs de uno o varios campos determinados de la tabla. Las bases de datos son, sin lugar a dudas, el motor principal para la gestin de la informacin, y por ello son utilizadas por empresas, organizaciones, particulares, etc. En el mercado existen multitud de empresas que se dedican a definir un producto de gestin de bases de datos . Pero las ms potentes y las ms utilizadas actualmente son: Oracle, SQL Server, Informix, Sybase, DB/2, Access, Paradox, FoxPro, dBASE, etc. Otro dato fundamental a tener en cuenta es que las bases de datos pueden ser gestionadas a travs de la red. Esto significa que la base de datos y toda su informacin pueden residir en un ordenador llamado servidor, que es quien provee y gestiona la bases de datos en relacin a una serie de permisos de acceso. Los ordenadores conectados por red a dicho servidor se d enominan clientes, y deben poseer los permisos y las condiciones necesarias para poder acceder a la informacin.

ODBC
Como resulta obvio deducir, cada sistema de base de datos implementa sus propias caractersticas, como pueden ser el modo de acceder a los datos, los tipos de datos soportados, los entornos, las configuraciones, etc. A pesar de que la mayora de los productos indicados anteriormente utilizan el lenguaje estndar SQL, cada uno trata de manera diferente (para el rendimiento ptimo) cada sentencia y cada uno difiere un poco del estndar y aaden su propia funcionalidad.

Para poder unificar toda esta diversidad, Microsoft plante una especificacin llamada ODBC (Object DataBase Connectivity), mediante la cual el fabricante de una base de datos determinada podra generar un driver siguiendo esta especificacin. Entonces, realizar una aplicacin que acceda a la base de datos es ya algo tangible, pues el driver ODBC resuelve muchos escollos de incompatibilidad. Una sentencia en SQL puede ser ejecutada tericamente para cualquier sistema de base de datos y los tipos de datos retornados y facilitados pueden ser aceptados casi sin problemas. Primero est ODBC, que tras comprobar que las sentencias SQL respetan la especificacin, remiten la solicitud de ejecucin a la base de datos remota. Pero no nos engaemos, ODBC ha sido un gran avance, pero no la solucin total a la incompatibilidad. Una sentencia SQL generada en por ejemplo Oracle puede no ser ejecutada correctamente en SQL Server o en Informix. Hay algunas sutilezas propias de un determinado gestor de bases de datos que hay que refinar, y por ello, una aplicacin que pueda atacar a distintas bases de datos debe tener en cuenta la sintaxis y el formato de los datos para cada gestor. No obstante, existe un mnimo estndar que se suele respetar, y hoy en da ODBC se ha convertido en una interfaz estndar e, indiscutiblemente, la ms utilizada en Windows.

JDBC
Javasoft desarroll un conjunto de clases que permiten gestionar bases de datos, independientemente de la base de datos utilizada y de la plataforma en que se ejecute la aplicacin o applet en Java. Este conjunto de clases conforman un API (Applications Programming Interface o Interface de Programacin de Aplicacione s), el cual se denomina JDBC (Java DataBase Connectivity o Conectividad de Bases de Datos de Java). Gran parte del diseo de JDBC estuvo influenciado por ODBC, en gran parte debido a la amplia aceptacin de ODBC y a la oportunidad que tena para populariz arse ms. Del mismo modo que ODBC, JDBC precisa de los drivers o controladores apropiados para cada sistema de base de datos. JDBC es un API, un conjunto de clases e interfaces estndar que permite un fcil y rpido acceso a las bases de datos, independientemente del sistema de bases utilizado. Este conjunto de clases e interfaces son gestionadas por un controlador, el cual se encarga de traducir las llamadas requeridas por la base de datos. Los controladores de cada sistema de base de datos deben ser comp atibles con la especificacin estndar ANSI SQL-2 Entry Level. A diferencia de ODBC, la conectividad JDBC est completamente escrita en cdigo Java, evitando as el uso de punteros y los conflictos que stos provocan.

Controladores JDBC

JDBC

puede

utilizar

cuatro

tipos

de

controladores.

Controlador JDBC-ODBC El controlador JDBC-ODBC permite reutilizar lo ya existente, estableciendo un puente entre JDBC y ODBC, convirtiendo las llamadas y los resultados entre ambos gestores. Este tipo de controlador es de los ms utilizados por su facilidad de acceso y su disponibilidad, ya que se puede prescindir del controlador especfico para JDBC. Asimismo, el medio ODBC es, hoy en da, el ms utilizado y cada fabricante de sistemas de bases de datos ofrece su driver correspondiente para la especificacin de Microsoft.

Por el contrario, el problema que se plantea es el de siempre: debe existir una copia del driver ODBC en cada mquina local para poder acceder a la base de datos, por lo que la seguridad peligra. Asimismo este tipo de controlador implica tambin que las aplicaciones corrern exclusivamente en la plataforma Windows, renegando del resto de plataformas. Por si esto fuese poco, si el gestor JDBC debe traducir las llamadas y los resultados a ODBC, y ODBC, a su vez, a cdigo nativo para comunicarse con el sistema de base de datos, el tiempo invertido para realizar el acceso a los datos puede ser excesivo.

Controlador Java nativo El controlador de Java nativo es un driver escrito ntegramente en Java, y permite comunicarse directamente con las libreras nativas del fabricante del sistema de base de datos.

La ventaja de este tipo de controlador es que no tiene que pasar por ODBC, con lo que se gana rapidez y en eficiencia. Por el contrario, se requiere de una parte en binario, que son las bibliotecas del fabricante, en cada mquina cliente.

Controlador Java

protocolo nativo

Este tipo de controlador utiliza un driver completamente realizado en Java, el cual se comunica directamente con el servidor de bases de datos, a travs del protocolo nativo de red.

Este tipo de controlador posee grandes ventajas: * No se necesita bibliotecas intermedias. * El driver convierte todas las consultas en peticiones directas contra el servidor a travs de la red. * Es totalmente independiente de la mquina en la que se ejecute. * Es una solucin ntegra en Java. Por el contrario, el principal problema es que cada sistema de base de datos utiliza un protocolo de red propio, lo que obliga a que el client e est irremediablemente vinculado a un sistema de base de datos determinado.

Controlador Java

protocolo independiente

Este tipo de controlador requiere que las bibliotecas del fabricante estn en el lado del servidor, que actuarn como intermediario entre las peticiones del driver JDBC del cliente y el servidor de la base de datos, traduciendo stas a peticiones nativas.

La principal ventaja de este tipo de controlador es que, adems de utilizar un driver 100% Java, es totalmente independiente con respecto al sistema de base de datos utilizado y del protocolo de red (se utiliza un protocolo de red genrico). Si en el futuro se decide cambiar el sistema de base de datos, nicamente hay que cambiar el intermediario. La mejor forma de ilustrar la gestin de bases de datos con Java es mediante un ejemplo. Por ello, este captulo estar dedicado a un ejemplo bastante simple, en el que una aplicacin en Java establecer una conexin con una base de datos a travs del puente JDBC-ODBC. Una vez establecida la conexin generar una consulta a travs de SQL, relacionando varias tablas y obteniendo un conjunto de registros, los cuales recorrer y visualizar. La aplicacin, por fines didcticos, no contendr ningn componente visual, y se dedicar ms directamente con el tratamiento de la base de datos. Aunque el ejemplo utilice ODBC (e, irremediablemente, sea exclusivo para los usuarios de Windows), es importante no pasar por alto este captulo, pues en l se adquieren conocimientos importantes para el resto de controladores JDBC. En captulos posteriores se detallar cada una las partes que intervienen en la gestin de bases de datos en Java.

Diseo de la base de datos


Para este ejemplo se ha utilizado una base de datos Microsoft Access 2000, aunque este aspecto es irrelevante, puesto que todo el trabajo lo hace ODBC y no importa el gestor de bases de datos utilizado. Este dato es ms que anecdtico, con el fin de que el lector pueda reproducir el ejemplo. Se ha construido un hipot tico caso de gestin de un almacn. Para ello se han definido cuatro tablas: Clientes, Productos, Pedidos y Detalle_Pedidos. Aunque en un caso real los criterios utilizados cambian, los campos pueden ampliarse y el nmero de tablas y de relaciones tambin, para ilustrar este ejemplo se ha escogido lo ms indispensable para ilustrar un ejemplo. En primer lugar, se ha definido la tabla Clientes, con el fin de recoger toda la informacin inherente a los clientes de la empresa. Estos clientes harn pedidos, q ue sern gestionados con los productos que hay en el almacn. El diseo, en su formato bsico, es el siguiente:
Campo Tipo Longitud Cod_Cliente Numrico Entero Largo Nombre Texto 50 Direccion Texto 50 Poblacion Texto 30

Provincia Texto Telefono Texto

25 20

El ndice recae sobre el campo Cod_Cliente, el cual asigna un identificador nico al cliente (puede haber varios clientes con el mismo nombre y/o con los mismos datos, pero cada uno contendr un ID diferente, y por tanto ser identificado y tratado como cliente diferente). Por tanto, este campo no podr contener valores nulos, ser obligatorio rellenarlo y adems no podr estar repetido. Se dice entonces que es una clave principal. A continuacin se define la tabla Productos, la cual contendr la informacin referente a cada uno de los productos que vende la empresa y que posee en el almacn, y permite la gestin del stock. Su diseo se ilustra a continuacin:
Campo Tipo Longitud Cod_Producto Numrico Entero Largo Descripcion Texto 50 PVC Numrico Double PVP Numrico Double Unidades Numrico Entero Largo Reservas Numrico Entero Largo

Los campos PVC y PVP recogen la informacin sobre el coste del producto. El campo PVC (Precio Venta Cesin) almacena el precio de coste del producto, mientras que el campo PVP (Precio Venta Pblico) almacena el precio de venta. La diferencia entre PVP y PVC representa el margen de beneficio de la venta. De cada producto se registran las unidades reales existentes en el almacn, mediante al campo Unidades. Pero hay que tener en cuenta que el que exista un nmero determinado de productos no significa que estn todos disponibles, pues pueden existir pedidos sin despachar. En el campo Reservas se registra las unidades que se reservan tras un pedido. La diferencia entre las unidades reales y las unidades reservadas representa las unidades disponibles realmente. El ndice suministrado recae sobre el campo Cod_Producto, el cual posee el identificador nico del producto y, por consiguiente, ser una clave. Por tanto, al igual que en la tabla Clientes, este campo no admitir valores nulos, ser obligatorio rellenarlo y no permitir duplicidad en sus valores. El siguiente paso es definir la tabla Pedidos, la cual registrar cada uno de los pedidos realizados por el cliente. Su diseo es el siguiente:
Campo Tipo Longitud Num_Pedido Numrico Entero Largo Cod_Cliente Numrico Entero Largo Fecha Fecha/Hora

En cada pedido hay un nmero de pedido, el cual es asignado para un determinado cliente en una determinada fecha.

Esta tabla poseer una clave principal (Num_Pedido) y una clave secundaria (Cod_Cliente). La clave principal es nica y no podr existir dos pedidos con el mismo nmero de pedido. Sin embargo, s pueden aparecer varios clientes, ya que un cliente puede realizar mltiples pedidos. Por ltimo, se define la tabla Detalle_Pedidos, la cual se encarga de registrar todos los productos pedidos por el cliente. Su diseo es el que sigue:
Campo Tipo Longitud Num_Pedido Numrico Entero Largo Cod_Producto Numrico Entero Largo Unidades Numrico Entero Largo

Cada registro en esta tabla corresponde a cada producto pedido por el cliente. Por tanto, puede haber mltiples registros para un solo pedido. Por ello, el ndice recae sobre los campos Num_Pedido y Cod_Producto, sin clave principal. En ambos campos no se permiten valores nulos y es obligatorio rellenar dichos campos. Pero sus valores pueden estar duplicados. La relacin de todas las tablas quedara de la siguiente manera:

Datos utilizados
Los datos que se han introducido para ilustrar el ejempl o son los siguientes:
TABLA CLIENTES

Cod_Cliente Nombre Direccion Poblacion Provincia Telefono 1 GERFOSA C/ Almagro, 3 Mstoles Madrid 916474558 2 JUKALI C/ Donoso, 21 Getafe Madrid 916833520 3 SITNA C/ Castillo, 34 Alcorcn Madrid 916124748 TABLA PRODUCTOS Cod_Producto Descripcion PVC PVP Unidades Reservas 1 Monitor 14 15800 25000 40 12 2 Teclado 1500 3500 25 2 3 Raton 500 995 500 247 TABLA PEDIDOS Num_Pedido Cod_Cliente Fecha 1 3 14/06/2000 2 2 14/06/2000 3 2 15/06/2000 4 3 16/06/2000 5 1 16/06/2000 TABLA DETALLE_PEDIDOS Num_Pedido Cod_Producto Unidades 1 2 5 1 3 10 2 1 2 3 1 3 3 3 25 4 3 5 5 1 1 5 2 14 5 3 14

Configuracin de ODBC
Para configurar ODBC es necesario acceder al la barra de inicio de Windows, seleccionar la opcin Configuracin y la subopcin Panel de Control (tambin se puede acceder mediante el icono Mi PC del escritorio). En la ventana que aparece, se hace doble click sobre el icono Fuentes de Datos ODBC (32 bits) , con lo que aparecer la siguiente ventana:

En esta ventana se pueden apreciar que aparecen los controladores de orgenes de datos que tenga instalados la mquina local. Es necesario, pues, que exista el controlador apropiado para el gestor de base de datos a utilizar en la aplicacin. Si la base de datos est gestionada por ejemplo - en Oracle 8.0., es necesario haber instalado previamente el driver de Oracle 8.0. en la mquina local. El driver correspondiente debera aparecer en la primera solapa de esta ventana (DSN de usuario), pero si no es as, se puede comprobar en la solapa Controladores. Para crear un origen de datos ODBC, hay que seleccionar la solapa DSN de Sistema, con lo que se mostrar una lista de orgenes de datos existentes (si es la primera vez, la lista aparecer vaca).

Ahora se hace click sobre el botn Agregar..., en donde aparecer una nueva ventana en donde se selecciona el driver de base de datos apropiado de entre todos los disponibles en la mquina.

En nuestro caso se seleccionar el driver de Microsoft Access Driver (*.mdb) y se hace click sobre el botn Finalizar. En este momento, dependiendo del driver seleccionado, se abre un asistente con el que se establecen los parmetros de localizacin de la base de datos a utilizar, el

nombre de usuario, la contrasea, etc. En el caso de Microsoft Access, la ventana de configuracin es la siguiente:

El campo Nombre del origen de datos representa el nombre identificativo en ODBC, con el cual referirnos a la conexin. Para seleccionar la base de datos a utilizar, se hace click sobre el botn Seleccionar..., en el que se desplegar una caja de dilogo de archivos, en donde navegaremos a la mquina donde se alberga la base de datos (en nuestro caso la mquina local misma), la unidad de disco, el directorio y el nombre del archivo (Almacen.mdb). Si el acceso a esta base de datos se desea o se requiere - realizar mediante un nombre de usuario y una contrasea, se debe especificar estos datos haciendo click sobre el botn Avanzado..., con lo que aparecer una ventana como la siguiente:

Una vez configurado el origen de los datos ODBC, se hace click sobre el botn Aceptar, donde volver a la ventana de Administrador de orgenes de datos ODBC , con el nuevo origen de datos en la lista. Desde esta misma ventana puede cambiarse la configuracin de un determinado origen de datos.

Fases del acceso a bases de datos


Para acceder a una base de datos en Java se puede dividir el proceso en dos niveles bsicos: nivel de controlador y nivel de aplicacin. El nivel de controlador comprende la carga de los drivers necesarios para poder acceder a la base de datos. El nivel de aplicacin implica la creacin de conexin a la base de datos, la creacin de una sentencia de SQL que permita acceder a dicha base de datos y la ejecucin de dicha sentencia, la cual puede o no retornar datos ( resultset o resultado). El nivel de controlador est gestionado por la clase DriverManager, que es la que se encarga de cargar los drivers JDBC. La interface Driver es la encargada de implementar el driver o controlador de la base de datos. El nivel de aplicacin est gestionado por diversas interfaces que se detallan a continuacin. La interfaz Connection es la encargada de establecer la conexin con la base de datos, y hace posible ejecutar sentencias SQL y procedimientos almacenados.

La interfaz Statement se encarga de ejecutar una sentencia SQL esttica y de obtener los posibles registros resultantes. La interfaz PreparedStatement es derivada de la interfaz Statement, y representa a una sentencia SQL precompilada o preparada. Este tipo de sentencias SQL, cuando se refieren a valores de campos, lo hacen mediante el signo interrogacin, el cual ser sustituido por un valor obtenido, de manera dinmica, mediante una variable u objeto. La interfaz CallableStatement desciende, a su vez, de la interfaz PreparedStatement, y se utiliza para ejecutar procedimientos almacenados. El siguiente esquema ilustra estos dos niveles bsicos:

Las fases de acceso a bases de datos se pueden representar en cinco:


1. Cargar driver o controlador JDBC adecuado. 2. Crear la conexin a la base de datos. 3. Crear la sentencia JDBC. 4. Ejecutar la sentencia JDBC. 5. Acceso a los datos retornados (si los hay).

El siguiente esquema ilustra estas cinco fases:

Para el ejemplo propuesto, se desea acceder un resumen de todos los datos almacenados en el almacen. Para ello, se realiza una consulta, la cual d la informacin del nmero de pedido, la fecha del mismo, el cliente que curs el pedido, las unidades, producto, el precio de venta de cada unidad, y el precio total. Esto se consigue con la siguiente sentencia SQL:
SELECT Pedidos.Num_Pedido as NumPedido, Pedidos.Fecha as FechaPedido, Clientes.Nombre as NombreCliente, Detalle_Pedidos.Unidades as Cantidad, Productos.Descripcion as DescProducto, Productos.PVP as Precio, (Cantidad * Precio) as Total FROM Pedidos, Clientes, Detalle_Pedidos, Productos WHERE Pedidos.Cod_Cliente = Clientes.Cod_Cliente and Detalle_Pedidos.Cod_Producto = Productos.Cod_Producto and Detalle_Pedidos.Num_Pedido = Pedidos.Num_Pedido ORDER BY Pedidos.Num_Pedido

Con todos estos conceptos y con las ideas claras del propsito, se puede empezar a esbozar cada una de las fases necesarias para el acceso a la base de datos.

Cargar controlador JDBC

Para cargar de forma explctita un driver o controlador JDBC determinado, se utiliza el mtodo forName de la clase principal Class. Este mtodo retorna un tipo Class, que representa una clase de objeto. Esta clase de objeto es el driver o controlador utilizado para acceder a la base de datos. La clase DriverManager permite cargar drivers, pero intentar siempre cargar todos los drivers apuntados en la propiedad jdbc.drivers. Por ello, es recomendable utilizar el mtodo forName para cargar un solo driver de manera explcita. En nuestro caso, se desea acceder a la base de datos mediante ODBC, por lo que el controlador utilizado es el puente JDBC-ODBC, y se realiza de la siguiente manera:
// Cargar el driver JDBC-ODBC try { Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver"); } catch (Exception e1) { System.out.println ("No se puede cargar el driver JDBC-ODBC"); ... // Tratamiento de la excepcion }

Crear la conexin a la base de datos


Una vez cargado el driver correspondiente, el siguiente paso es crear la conexin a la base de datos. Para ello, es necesario utilizar la interfaz Connection, la cual representa una conexin o sesin de base de datos. Para establecer una conexin, lo primero es declarar un objeto perteneci ente a esta interfaz, tal y como demuestra la siguiente sentencia:
Connection conConexion;

La clase DriverManager es la encargada de gestionar los drivers o controladores de las bases de datos, por lo que tambin permite crear la conexin con la base de datos y retornar un objeto de tipo Connection con dicha conexin, y asignarlo al objeto definido anteriormente. En las siguientes lneas se demuestra cmo hacerlo:
// Crear la conexion con la base de datos try { conConexion = DriverManager.getConnection ("jdbc:odbc:Almacen", "rafinguer", "rafinguer"); ... } catch (Exception e2) { ... // Tratamiento de la excepcion }

En esta ocasin, el mtodo getConnection posee tres parmetros. El primer parmetro especifica la URL de la base de datos, dispuesta en esta sintaxis:
jdbc:odbc:nombre origen de datos

El segundo parmetro especifica el nombre de usuario para acceder a la base de datos. El tercer parmetro especifica el password o contrasea de acceso.

Crear la sentencia JDBC


Una vez creada con xito la conexin con la base de datos, el siguiente paso es crear una sentencia con la que la aplicacin podr enviar peticiones a la base de datos, tales como crear tablas, consultar datos, actualizar o modificar, borrar, etc. Para nuestro ejemplo, se crea un objeto de tipo Statement:
Statement staSentencia;

A continuacin se invoca al mtodo createStatement del objeto Connection que contiene la conexin con la base de datos:
staSentencia = conConexion.createStatement ( );

Como se puede apreciar, el anidamiento de procesos pasan de un objeto a otro. As, el objeto DriverManager crea la conexin delegndola en un objeto de tipo Connection, y este objeto, a su vez, crea una sentencia que delega en un objeto de tipo Statement. Todo esto tiene su sentido: una conexin debe saber qu controlador debe utilizar para acceder a una determinada base de datos, y una sentencia debe saber a qu base de datos debe de atacar, y esta informacin la contiene el objeto Connection.

Ejecutar la sentencia JDBC


Una vez creada la sentencia, se ejecuta dicha sentencia. Para ello, se proporciona, normalmente, una cadena de texto con una sentencia en lenguaje SQL. Dependiendo de la sentencia SQL se utiliza un determinado mtodo. Para nuestro ejemplo, que es una consulta, se utiliza el mtodo executeQuery, el cual ejecuta una sentencia que retorna un conjunto de registros, llamado resultset o resultado. Este resultado es recogido mediante un objeto de tipo Resultset.
strSQL = "select Pedidos.Num_Pedido as NumPedido, "; strSQL = strSQL + "Pedidos.Fecha as FechaPedido, "; strSQL = strSQL + "Clientes.Nombre as NombreCliente, "; strSQL = strSQL + "Detalle_Pedidos.Unidades as Cantidad, "; strSQL = strSQL + "Productos.Descripcion as DescProducto, ";

strSQL = strSQL + "Productos.PVP as Precio, "; strSQL = strSQL + "(Cantidad * Precio) as Total "; strSQL = strSQL + "from Pedidos, Clientes, "; strSQL = strSQL + "Detalle_Pedidos, Productos where "; strSQL = strSQL + "Pedidos.Cod_Cliente = Clientes.Cod_Cliente and "; strSQL = strSQL + "Detalle_Pedidos.Cod_Producto = Productos.Cod_Producto and "; strSQL = strSQL + "Detalle_Pedidos.Num_Pedido = Pedidos.Num_Pedido "; strSQL = strSQL + "order by Pedidos.Num_Pedido"; // Ejecucion de la sentencia y generacion del resultado try { resResultado = staSentencia.executeQuery (strSQL); ... // Tratamiento del resultado de la sentencia } catch (Exception e3) { ... // Tratamiento de la excepcion }

Acceso a los datos retornados


En nuestro ejemplo, la sentencia SQL es una SELECT, la cual selecciona registros de una o varias tablas, retornando registros o filas de datos. Estos registros deben ser capturados por un objeto de tipo Resultset (resultado), el cual se declara previamente:
ResultSet resResultado;

Como se pudo apreciar en la fase anterior, cuando se ejecuta la sentencia JDBC a travs del mtodo executeQuery, ste retorna un objeto de tipo Resultset, el cual se asigna al objeto Resultset declarado:
resResultado = staSentencia.executeQuery (strSQL);

Este objeto contiene todas los registros retornados por la sentencia. Para poder acceder a cada registro y extraer la informacin, la interfaz Resultset provee mtodos para navegar a travs de los registros o para capturar los valores de cada campo en el formato adecuado. No es propsito de este captulo abarcar todas estas caractersticas, pues en captulos posteriores se tratar con ms detalle. El propsito de este ejemplo es acceder a todos los registros retornados y visu alizarlos en la consola, uno a uno, desde el primero hasta el ltimo. Para ello, se ha utilizado el mtodo next, el cual accede al siguiente registro en el Resultset, y retorna el valor true si an no ha llegado al final del Resultset (si todava hay registros). Las siguientes lneas de cdigo ilustran su uso:
// Recorrer el resultado while (resResultado.next ( ))

{ NumPedido = resResultado.getLong ("NumPedido"); Fecha = resResultado.getDate ("FechaPedido"); Nombre = resResultado.getString ("NombreCliente"); Unidades = resResultado.getLong ("Cantidad"); Descripcion = resResultado.getString ("DescProducto"); PVP = resResultado.getDouble ("Precio"); Total = resResultado.getDouble ("Total"); System.out.println ("Num_Pedido : " + NumPedido); System.out.println ("Fecha : " + Fecha); System.out.println ("Cliente : " + Nombre); System.out.println ("Unidades : " + Unidades); System.out.println ("Descripcion: " + Descripci on); System.out.println ("P.V.P. : " + PVP); System.out.println ("Total : " + Total); System.out.println (""); // Linea en blanco (separacion) }

El ejemplo al completo
A continuacin se expone todo el cdigo completo para el ejemplo. Para ello, se crea un fichero de texto llamado EjemploBD1.java, y su contenido es el siguiente:
/**********************************************************/ /* AUTOR: Rafael Hernamperez Martin */ /* FECHA: 17-06-2000 */ /* OBJ : Ejemplo simple de acceso a bases de datos */ /**********************************************************/ import java.sql.*; import java.util.Date; public class EjemploBD1 { public static void main (String args []) { String strSQL = new String ( ); Connection conConexion; Statement staSentencia; ResultSet resResultado; // Campos del registro long NumPedido = 0; Date Fecha = new Date ( ); String Nombre = new String ( ); long Unidades = 0; String Descripcion = new String ( ); double PVP = 0; double Total = 0;

// Cargar el driver JDBC-ODBC try

{ Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver"); } catch (Exception e1) { System.out.println ("No se puede cargar el driver JDBC-ODBC"); System.exit (0); }

// Crear la conexion con la base de datos try { conConexion = DriverManager.getConnection ("jdbc:odbc:Almacen", "rafinguer", "rafinguer"); staSentencia = conConexion.createStatement ( ); strSQL = "select Pedidos.Num_Pedido as NumPedido, "; strSQL = strSQL + "Pedidos.Fecha as FechaPedido, "; strSQL = strSQL + "Clientes.Nombre as NombreCliente, "; strSQL = strSQL + "Detalle_Pedidos.Unidades as Cantidad, "; strSQL = strSQL + "Productos.Descripcion as DescProducto, "; strSQL = strSQL + "Productos.PVP as Precio, "; strSQL = strSQL + "(Cantidad * Precio) as Total "; strSQL = strSQL + "from Pedidos, Clientes, "; strSQL = strSQL + "Detalle_Pedidos, Productos where "; strSQL = strSQL + "Pedidos.Cod_Cliente = Clientes.Cod_Cliente and "; strSQL = strSQL + "Detalle_Pedidos.Cod_Producto = Productos.Cod_Producto and "; strSQL = strSQL + "Detalle_Pedidos.Num_Pedido = Pedidos.Num_Pedido "; strSQL = strSQL + "order by Pedidos.Num_Pedido"; // Ejecucion de la sentencia y generacion del resultado try { resResultado = staSentencia.executeQuery (strSQL); // Recorrer el resultado while (resResultado.next ( )) { NumPedido = resResultado.getLong ("NumPedido"); Fecha = resResultado.getDate ("FechaPedido"); Nombre = resResultado.getString ("NombreCliente"); Unidades = resResultado.getLong ("Cantidad"); Descripcion = resResultado.getString ("DescProducto"); PVP = resResultado.getDouble ("Precio"); Total = resResultado.getDouble ("Total"); System.out.println ("Num_Pedido : " + NumPedido); System.out.println ("Fecha : " + Fecha); System.out.println ("Cliente : " + Nombre); System.out.println ("Unidades : " + Unidades); System.out.println ("Descripcion: " + Descripcion); System.out.println ("P.V.P. : " + PVP); System.out.println ("Total : " + Total); System.out.println (""); // Linea en blanco (separacion) }

} catch (SQLException e4) { System.out.println ("Hubo un error de SQL: " + e4); System.exit (0); } } catch (Exception e2) { System.out.println ("No se pudo realizar la conexion"); System.exit (0); } } }

Una vez grabado el fichero, se compila el cdigo mediante la siguiente sentencia desde la lnea de comandos de la consola de Windows:
javac EjemploBD1.java

Una vez se haya compilado la aplicacin sin ningn error, se pasa a la ejecucin de la aplicacin mediante la siguiente sentencia:
java EjemploBD

En el captulo anterior se contempl un ejemplo muy sencillo de conexin a una base de datos a travs del puente JDBC-ODBC. En este captulo se abordar ms detenidamente esta fase tan fundamental para el acceso a la base de datos.

La carga del driver


La fase previa a la creacin de la conexin es la carga del driver adecuado para utilizar la base de datos. De esto se encarga el mtodo forName de la clase Class, y su sintaxis es la siguiente:
Class.forName ( jdbc.nombreDriver ); Class.forName ( empresa.basedatos.driver );

En el caso de utilizar un driver de tipo JDBC-ODBC, la sentencia a utilizar sera la siguiente:

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

La cadena utilizada posee una sintaxis muy parecida a un URL. La ruta de directorios usual para el acceso a la clase que sirve como driver es la siguiente:
empresa/basedatos/driver.class

Para utilizar otro tipo de controlador habra que remitirse a la documentacin del gestor de base de datos que se est utilizan do. Se podra utilizar la clase DriverManager, que es la clase especializada para gestionar los controladores o drivers, pero el mtodo forName de la clase Class es ms cmodo, ya que con l no es necesario crear una instancia de la clase DriverManager.

La conexin
Una vez cargado el controlador de la base de datos ya es posible establecer una conexin con la base de datos. La clase Connection se encarga de llevar a cabo este contexto, permitiendo ejecutar sentencias SQL y retornar los valores devueltos por las consultas. Tambin posee la capacidad de ejecutar procedimientos almacenados y de acceder a la informacin estructural de las tablas. Para tener una conexin a la base de datos, hay que declarar un objeto de la clase Connection, tal y como ilustra la siguiente lnea:
Connection conexion;

Este objeto ser una referencia al objeto Connection que retornar el mtodo getConnection de la clase DriverManager. La siguiente lnea crea la conexin con la base de datos Almacen:
conConexion = DriverManager.getConnection ("jdbc:odbc:Almacen", "rafinguer", "rafinguer");

El mtodo getConnection est sobrecargado y posee varias sintaxis:


public static Connection getConnection (String url) throws SQLException public static Connection getConnection (String url, String user, String password) throws SQLException public static Connection getConnection (String url, Properties info) throws SQLException

En la primera forma se accede a una base de datos pblica que no tiene restringido el acceso, por lo que se pasa solamente la URL de la base de datos, en el siguiente formato:
jdbc:subprotocolo:nombre

En la segunda forma se accede ya a una base de datos con acceso restringido. Adems de la URL, se pasa tambin el nombre de usuario y la contrasea de acceso. La tercera forma permite suministrar propiedades de conexin. Mediante un objeto de tipo Properties se facilita las propiedades de conexin a la base de datos, mediante la sintaxis tag/valor. Normalmente se pasan los tags user y password. La clase DriverManager permite tambin establecer un lmite de tiempo para intentar establecer la conexin. Para ello dispone del mtodo setLoginTimeout, cuya sintaxis es la que sigue:
public static void setLoginTimeout (int seconds)

Como parmetro se suministra el nmero mximo de segundos para la conexin. Para conocer este tiempo se utiliza el mtodo getLoginTimeout, cuya declaracin es la siguiente:
public static int getLoginTimeout ( )

Mtodos
Class.forName ( jdbc.nombreDriver ); Class.forName ( empresa.basedatos.driver ); public static Connection getConnection (String url) throws SQLException public static Connection getConnection (String url, String user, String password) throws SQLException public static Connection getConnection (String url, Properties info) throws SQLException public static void setLoginTimeout (int seconds) public static int getLoginTimeout ( )

El uso ms habitual de una base de datos es la consulta. Gracias a las consultas se puede acceder a una determinada informacin, en donde pueden verse involucrados la ordenacin, el filtrado de datos o la relacin entre dos o ms tablas. Para ello, se hace uso de la sentencia SELECT del estndar SQL. Para llevar a cabo una consulta se debe crear una sentencia asoci ada al objeto Connection creado. Las sentencias permiten ejecutar acciones tanto de consulta como de actualizacin, eliminacin, insercin, creacin, etc.

Existen tres tipos de sentencias:

y y

Statement: Permiten ejecutar sentencias sin ningn tipo de parmetro (simples o directas). PreparedStatement: Permiten ejecutar sentencias preparadas, con parmetros de entrada. Este tipo de sentencias se utiliza si se va a ejecutar varias veces y si los parmetros son dinmicos en cada ejecucin. CallableStatement: Permiten ejecutar sentencias SQL compiladas (como PreparedStatement), con parmetros de entrada y de salida. Este tipo de sentencias se utiliza para ejecutar procedimientos almacenados.

Este captulo est centrado en el uso de consultas, por lo que se u tilizar como ejemplo una sentencia simple, pues es el tipo de sentencia ms habitual. Los detalles sobre cada tipo de sentencia se irn descubriendo a lo largo de este captulo y los posteriores. La interface Statement es la interface base de las sentencias. De esta interface derivan el resto de sentencias. Para llevar a cabo una consulta a la base de datos es preciso crear un objeto de tipo Statement, que ser una referencia al tipo Statement devuelto por el objeto Connection, y as poder establecer su asociacin. La siguiente lnea realiza esto:
Statement sentencia;

El siguiente paso para crear la sentencia es asociar este objeto al objeto Connection, con el fin de que la sentencia que se ejecuta se realiza sobre una determinada base de datos y no sobre otra. Esto se realiza mediante el mtodo getConnection de la clase Connection. Si anteriormente se cre una conexin mediante un objeto Connection llamado conexion, la forma de crear y asociar la sentencia sera la siguiente:
sentencia = conexion.createStatement ( );

Una vez creada la sentencia y la asociacin con el objeto Connection, el siguiente paso es ejecutar una sentencia. En el caso de ejecutar una consulta, hay que tener en cuenta que este tipo de sentencias debe utilizar la sentencia SELECT de SQL, y que esta sentencia retornar un resultado, que es un conjunto de filas y columnas llamado resultset (resultado). Una forma de saber si la sentencia retorna o no datos es mediante el mtodo execute de la interfaz Statement. Su sintaxis es la siguiente:
public boolean execute (String sql) throws SQLException

Este mtodo ejecuta la sentencia SQL que se pasa como parmetro en forma de cadena o String. Si la sentencia retorna un resultado retornar el valor true. Si la sentencia no devuelve un resultado o la sentencia es de tipo actualizacin retornar el valor false. No obstante, cuando se realiza una aplicacin, el programador sabe qu tipo de sentencia SQL se va a ejecutar en un momento determinado, es decir, se sabe si se va a realizar una consulta o una actualizacin. La forma ms directa es utilizar el mtodo executeQuery, el cual ejecuta una sentencia de consulta y retorna el resultado obtenido. Su sintaxis es la siguiente:
public ResultSet executeQuery (String sql) throws SQLException

Obviamente, primero hay que declarar un objeto de tipo ResultSet, que ser una referencia al objeto ResultSet retornado por el mtodo executeQuery, y as asociar el resultado a la sentencia. La siguiente sentencia crea un objeto de tipo ResultSet:
ResultSet resultado;

La sentencia SQL se puede escribir en una simple cadena, como ilustra la siguiente lnea.
resultado = sentencia.executeQuery ( select * from clientes );

Si la sentencia SQL es extensa, se puede utilizar un objeto de tipo String, en el cual ir concatenando cada trozo de la sentencia SQL, y pasar dicho objeto String al mtodo executeQuery, tal y como muestra las siguientes lneas de cdigo:
strSQL = "select Pedidos.Num_Pedido as NumPedido, "; strSQL = strSQL + "Pedidos.Fecha as FechaPedido, "; strSQL = strSQL + "Clientes.Nombre as NombreCliente, "; strSQL = strSQL + "Detalle_Pedidos.Unidades as Cantidad, "; strSQL = strSQL + "Productos.Descripcion as DescProducto, "; strSQL = strSQL + "Productos.PVP as Precio, "; strSQL = strSQL + "(Cantidad * Precio) as Total "; strSQL = strSQL + "from Pedidos, Clientes, "; strSQL = strSQL + "Detalle_Pedidos, Productos where "; strSQL = strSQL + "Pedidos.Cod_Cliente = Clientes.Cod_Cliente and "; strSQL = strSQL + "Detalle_Pedidos.Cod_Producto = Productos.Cod_Producto and "; strSQL = strSQL + "Detalle_Pedidos.Num_Pedido = Pedidos.Num_Pedido "; strSQL = strSQL + "order by Pedidos.Num_Pedido"; // Ejecucion de la sentencia y generacion del resultado try { resResultado = staSentencia.executeQuery (strSQL); ... // Tratamiento del resultado de la sentencia } catch (Exception e3) {

... // Tratamiento de la excepcion }

Una vez ejecuta la sentencia de consulta hay un resultado que est referenciado por el objeto ResultSet. La forma de acceso a la informacin de este resultado se realiza, en primer lugar, por filas o registros. Cuando se ejecuta la sentencia de consulta s e accede antes del primer registro o fila, y slo se puede acceder a una fila cada vez. Una vez situados en una fila, se puede acceder a cada campo de la fila. El registro actual est apuntado por lo que se denomina un cursor, que es un concepto lgico par a indicar la fila en curso. Para acceder a cada uno de los campos del resultado, hay que tener en cuenta el tipo de dato que posee dicho campo. Cada tipo de dato posee un mtodo getXXX asociado en la interfaz ResultSet. La siguiente tabla ilustra cul es el tipo de mtodo getXXX recomendado a utilizar para cada tipo de dato:
TIPO METODO TINYINT getByte SMALLINT getShort INTEGER getInt BIGINT getLong REAL getFloat FLOAT getDouble DOUBLE getDouble DECIMAL getBigDecimal NUMERIC getBigDecimal BIT getBoolean CHAR getString VARCHAR getString LONGvARCHAR getAsciiStream BINARY getBytes VARBINARY getBytes LONGVARBINARY getBinaryStream DATE getDate TIME getTime TIMESTAMP getTimeStamp

La sintaxis de cada uno de los mtodos getXXX es la siguiente:


byte getByte (int columnIndex) byte getByte (String columnName) short getShort (int columnIndex) short getShort (String columnName) int getInt (int columnIndex) int getInt (String columnName) long getLong (int columnIndex) long getLong (String columnName) float getFloat (int columnIndex) float getFloat (String columnName) double getDouble (int columnIndex) double getDouble (String columnName) BigDecimal getBigDecimal (int columnIndex) BigDecimal getBigDecimal (String columnName)

boolean getBoolean (int columnIndex) boolean getBoolean (String columnName) String getString (int columnIndex) String getString (String columnName) InputStream getAsciiStream (int columnIndex) InputStream getAsciiStrean (String columnName) byte [] getBytes (int columnIndex) byte [] getBytes (String columnName) InputStream getBinaryStream (int columnIndex) InputStream getBinaryStream (String columnName) Date getDate (int columnIndex) Date getDate (String columnName) Time getTime (int columnIndex) Time getTime (String columnName) TimeStamp getTimestamp (int columnIndex) TimeStamp getTimestamp (String columnName)

La sintaxis genrica de los mtodos getXXX es bien sencilla. Se puede pasar el nmero de columna del campo (comenzando desde 1) o el nombre del campo en una cadena. El acceso a cada fila del resultado ha variado bastante de la especificacin de JDBC 1.0. a JDBC 2.0. En la primera, el acceso era bien simple, pues se basaba en un cursor unidireccional y secuencial, que iba desde el primer registro hasta el ltimo. Por ello slo era necesario un mtodo para acceder a cada uno de lo s registros. Este mtodo es next, y permite avanzar el cursor a la siguiente fila del resultset, retornando el valor true si hay ms registros en el resultset, o el valor false si no hay ms registros. La sintaxis de este mtodo es la siguiente:
public boolean next ( ) throws SQLException

En la especificacin JDBC 2.0. es posible ya manipular varios tipos de cursores, en lugar del clsico unidireccional. Gracias a este tipo de cursores se puede acceder de manera directa a una determinada fila, volver haci a atrs, o ir al principio o al final del resultset. Para ello, se han incorporado nuevos mtodos de acceso, los cuales se detallan a continuacin.
public boolean first ( ) throws SQLException public boolean last ( ) throws SQLException public void beforeFirst ( ) throws SQLException public void afterLast ( ) throws SQLException public boolean absolute (int row) throws SQLException public boolean relative (int rows) throws SQLException public void previous ( ) throws SQLException

El mtodo first mueve el cursor o fila actual al primer registro del resultado, retornando el valor true si es vlido, o false en caso contrario. El mtodo last mueve el cursor o fila actual al ltimo registro del resultado, retornando el valor true si es vlido, o false en caso contrario.

El mtodo beforeFirst mueve el cursor o fila actual justo antes del primer registro, mientras que el mtodo afterLast, por el contrario, mueve el cursor justo despus del ltimo registro del resultado. El mtodo absolute mueve el cursor o fila actual directamente al nmero de fila especificado, retornando true si la fila existe, o false en caso contrario. Si el valor pasado es positivo, este valor indica el nmero de filas desde el primer registro, mientras que, si el valor es negativo, este valor indica el nmero de filas desde el ltimo registro. El mtodo relative mueve el cursor o fila actual n filas desde la posicin actual, retornando el valor true si la fila existe o es vlida, o false en caso contrario. Si el valor positivo el avance de filas ser hacia delante, mientras que si es negativo el avance de filas ser hacia atrs. El mtodo previous mueve el cursor o fila actual al registro anterior, retornando el valor true si la fila es vlida o existe, o false en caso contrario. Existen tambin mtodos que permiten comprobar un estado del resultado, y que se pueden consultar antes de acceder a un registro determinado. Estos mtodos son los siguientes:
public boolean isBeforeFirst ( ) throws SQLException public boolean isAfterLast ( ) throws SQLException public boolean isFirst ( ) throws SQLException public boolean isLast ( ) throws SQLException public int getRow ( ) throws SQLException

Los cuatro primeros mtodos retornan el valor true si el cursor actual est antes del primer registro, despus del ltimo registro, en el primer registro o en el ltimo registro, respectivamente, o false en caso contrario. El mtodo getRow retorna el nmero de registro de la fila actual, o bien el valor 0 si no existe registro. Para poder utilizar estos nuevos mtodos es necesario definir un resultado desplazable, lo cual se consigue a la hora de crear la sentencia con una nueva sintaxis de la sentencia createStatement:
public Statement createStatement (int resultSetType, int resultSetConcurrency) throws SQLException

Al utilizar esta sintaxis se genera una sentencia que utilizar un resultset de un tipo y concurrencia determinados, que son las nuevas caractersticas implementadas en JDBC 2.0. Los tipos de resultset vienen dados por las siguientes constantes:

ResultSet.TYPE_FORWARD_ONLY ResultSet.TYPE_SCROLL_INSENSITIVE ResultSet.TYPE_SCROLL_SENSITIVE

En el tipo ResultSet.TYPE_FORWARD_ONLY el cursor slo puede moverse hacia delante. El tipo ResultSet.TYPE_SCROLL_INSENSITIVE es un tipo de resultset en el que el acceso es deplazable pero que no es sensitivo al cambio de otros usuarios. Esto significa que mientras est abierto el resultset, si otro usuario realiza algn cambio en alguno de los datos, stos no se reflejarn (se quedarn en el mismo estado y valor que tenan al abrir el resultset). El tipo ResultSet.TYPE_SCROLL_SENSITIVE es como el anterior, pero el resultset es sensitivo al cambio de otros usuarios. Esto significa que mientras est abierto el resultset, si otro usuario realiza algn cambio en alguno de los datos, stos ser reflejarn en nuestro resultset. La concurrencia al resultset viene dada por las siguientes constantes:
ResultSet.CONCUR_READ_ONLY ResultSet.CONCUR_UPDATABLE

En el primer caso, la concurrencia es de slo lectura, mientras que en la segunda es posible realizar operaciones de actualizacin. As, si se desea acceder a los registros de la tabla Clientes, en modo de slo lectura y que pueda desplazarse en el resultset, habra que crear la sentencia de la siguiente manera:
sentencia = conexion.createStatement ( ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);

El modo estndar, si no se especifica nada en la creacin de la sentencia, es del ti po TYPE_FORWARD_ONLY y de concurrencia CONCUR_READ_ONLY, es decir, un resultset no desplazable (el cursor slo se mueve hacia delante) y de slo lectura, tal y como hereda de la especificacin JDBC 1.0. Cuando se crea un cursor que no es de tipo TYPE_FORDWARD_ONLY, se puede acceder libremente a los registros (es desplazable), gracias a los mtodos vistos anteriormente. Las sentencias pueden limitarse en cuanto al tiempo de ejecucin. El mtodo setQueryTimeout de la interfaz Statement establece el nmero mximo de segundos que necesita una sentencia para su ejecucin. La sintaxis de este mtodo es la que sigue:
public void setQueryTimeout (int seconds) throws SQLException

Para conocer dicho tiempo se utiliza el siguiente mtodo:

public int getQueryTimeout ( ) throws SQLException

Como ha podido apreciarse a lo largo de todo este captulo, no hay un mtodo que indique el nmero de filas que posee el resultado. Se puede recurrir a la picarda y hacer lo siguiente: acceder al ltimo registro (mtodo last) y obtener el nmero de fila actual (mtodo getRow).

Mtodos (Connection)
conexion.createStatement ( ); public Statement createStatement (int resultSetType, int resultSetConcurrency) throws SQLException

Mtodos (Statement)
public boolean execute (String sql) throws SQLException public ResultSet executeQuery (String sql) throws SQLException public void setQueryTimeout (int seconds) throws SQLException public int getQueryTimeout ( ) throws SQLException

Mtodos (ResultSet)
byte getByte (int columnIndex) byte getByte (String columnName) short getShort (int columnIndex) short getShort (String columnName) int getInt (int columnIndex) int getInt (String columnName) long getLong (int columnIndex) long getLong (String columnName) float getFloat (int columnIndex) float getFloat (String columnName) double getDouble (int columnIndex) double getDouble (String columnName) BigDecimal getBigDecimal (int columnIndex) BigDecimal getBigDecimal (String colum nName) boolean getBoolean (int columnIndex) boolean getBoolean (String columnName) String getString (int columnIndex) String getString (String columnName) InputStream getAsciiStream (int columnIndex) InputStream getAsciiStrean (String columnName) byte [] getBytes (int columnIndex) byte [] getBytes (String columnName) InputStream getBinaryStream (int columnIndex) InputStream getBinaryStream (String columnName)

Date getDate (int columnIndex) Date getDate (String columnName) Time getTime (int columnIndex) Time getTime (String columnName) TimeStamp getTimestamp (int columnIndex) TimeStamp getTimestamp (String columnName) public boolean next ( ) throws SQLException public boolean first ( ) throws SQLException public boolean last ( ) throws SQLException public void beforeFirst ( ) throws SQLException public void afterLast ( ) throws SQLException public boolean absolute (int row) throws SQLException public boolean relative (int rows) throws SQLException public void previous ( ) throws SQLException public boolean isBeforeFirst ( ) throws SQLException public boolean isAfterLast ( ) throws SQLException public boolean isFirst ( ) throws SQLException public boolean isLast ( ) throws SQLException public int getRow ( ) throws SQLException

Constantes
ResultSet.TYPE_FORWARD_ONLY ResultSet.TYPE_SCROLL_INSENSITIVE ResultSet.TYPE_SCROLL_SENSITIVE ResultSet.CONCUR_READ_ONLY ResultSet.CONCUR_UPDATABLE

También podría gustarte