Manual JDBC
Manual JDBC
Manual JDBC
4
1.1¿Qué es JDBC?................................................................................................................ 4
1.1.1 ¿Qué hace JDBC?......................................................................................................5
1.1.2 JDBC es un API de bajo nivel y una base para API’s de alto nivel......................... 5
1.1.3 JDBC frente a ODBC y otros API’s.........................................................................6
1.1.5 SQL Conforme......................................................................................................... 8
1.2 Productos JDBC............................................................................................................ 9
1.2.1 JavaSoft Framework...............................................................................................10
1.2.2 Tipos de drivers JDBC........................................................................................... 11
2. CONEXIÓN....................................................................................................................... 12
2.1 Vista Preliminar.......................................................................................................... 12
2.1.1 Apertura de una conexión.......................................................................................12
2.1.2 Uso general de URL’s............................................................................................ 12
2.1.3 JDBC y URL’s....................................................................................................... 13
2.1.4 El subprotocolo “odbc”.......................................................................................... 15
2.1.5 Registro de subprotocolos...................................................................................... 15
2.1.6 Envío de Sentencias SQL....................................................................................... 16
2.1.7 Transacciones......................................................................................................... 17
2.1.8 Niveles de aislamiento de transacciones................................................................ 18
3. LA CLASE DriverManager............................................................................................... 19
3.1 Vista preliminar...........................................................................................................19
3.1.1 Mantenimiento de la lista de drivers disponibles................................................... 19
3.1.2 Establecer una conexión.........................................................................................20
4. LA CLASE Statement........................................................................................................ 22
4.1 Vista Preliminar.......................................................................................................... 22
4.1.1 Creación de objetos Statement............................................................................... 22
4.1.2 Ejecución de sentencias usando objetos Statement................................................ 23
4.1.3 Realización de Statement....................................................................................... 24
4.1.4 Cerrar objetos Statement........................................................................................ 24
4.1.5 Sintaxis de Escape SQL en objetos Statement....................................................... 24
4.1.6 Uso del método execute......................................................................................... 27
5. LA CLASE ResultSet......................................................................................................... 30
5.1 Vista Preliminar.......................................................................................................... 30
5.1.1 Filas y Cursores...................................................................................................... 30
5.1.2 Columnas................................................................................................................31
5.1.3 Tipos de datos y conversiones................................................................................32
Unicode.......................................................................................................................... 35
Unicode es un esquema de codificación de caracteres que utiliza 2 bytes por cada
carácter. ISO (International Standards Organization) define un número dentro del
intervalo 0 a 65.535 (216 – 1) por cada carácter y símbolo de cada idioma (más algunos
2
espacios vacíos para futuras ampliaciones). En todas las versiones de 32 bits de
Windows, el Modelo de objetos componentes (COM), que es la base de las tecnologías
OLE y ActiveX, utiliza Unicode. Unicode es totalmente compatible con Windows NT.
Aunque Unicode y DBCS tienen caracteres de doble byte, los esquemas de codificación
son completamente diferentes. ........................................................................................ 35
5.1.5 Valores resultado NULL........................................................................................ 36
5.1.6 Result sets opcionales o múltiples..........................................................................36
6. LA CLASE PreparedStatement......................................................................................... 37
6.1 Vista Preliminar.......................................................................................................... 37
6.1.1 Creación de objetos PreparedStatement................................................................. 37
6.1.2 Pasar parámetros IN.............................................................................................. 38
6.1.4 Usar setObject........................................................................................................ 39
6.1.5 Envío de JDBC NULL como un parámetro IN...................................................... 40
6.1.6 Envio de parámetros IN muy grandes.................................................................... 40
7. LA CLASE CallableStatement........................................................................................... 41
7.1 Vista Preliminar.......................................................................................................... 41
7.1.1 Crear objetos CallableStatement............................................................................ 42
7.1.2 Parámetros IN y OUT.............................................................................................42
7.1.3 Parámetros INOUT.................................................................................................43
7.1.4 Recuperar parámetros OUT después de resultados................................................ 44
7.1.5 Recuperar valores NULL en parámetros OUT.......................................................44
9. EJEMPLO DE CODIGO....................................................................................................45
3
JDBC.
1. INTRODUCCION
Drivers JDBC
Para usar JDBC con un sistema gestor de base de datos en particular, es necesario
disponer del driver JDBC apropiado que haga de intermediario entre ésta y JDBC.
Dependiendo de varios factores, este driver puede estar escrito en Java puro, o ser
una mezcla de Java y métodos nativos JNI (Java Native Interface).
4
JDBC expande las posibilidades de Java. Por ejemplo, con Java y JDBC API, es
posible publicar una página web que contenga un applet que usa información
obtenida de una base de datos remota. O una empresa puede usar JDBC para
conectar a todos sus empleados (incluso si usan un conglomerado de máquinas
Windows, Macintosh y UNIX) a una base de datos interna vía intranet. Con cada
vez más y más programadores desarrollando en lenguaje Java, la necesidad de
acceso fácil a base de datos desde Java continúa creciendo.
1.1.2 JDBC es un API de bajo nivel y una base para API’s de alto nivel.
JDBC es una interfase de bajo nivel, lo que quiere decir que se usa para ‘invocar’ o
llamar a comandos SQL directamente. En esta función trabaja muy bien y es más
fácil de usar que otros API’s de conexión a bases de datos, pero está diseñado de
forma que también sea la base sobre la cual construir interfaces y herramientas de
alto nivel. Una interfase de alto nivel es ‘amigable’, usa un API mas entendible o
más conveniente que luego se traduce en la interfase de bajo nivel tal como JDBC.
5
1.1.3 JDBC frente a ODBC y otros API’s
La respuesta es que se puede usar ODBC desde Java, pero es preferible hacerlo
con la ayuda de JDBC mediante el puente JDBC-ODBC. La pregunta es ahora ¿por
qué necesito JDBC?. Hay varias respuestas a estas preguntas:
1.- ODBC no es apropiado para su uso directo con Java porque usa una interface
C. Las llamadas desde Java a código nativo C tienen un número de
inconvenientes en la seguridad, la implementación, la robustez y en la
portabilidad automática de las aplicaciones.
2.- Una traducción literal del API C de ODBC en el API Java podría no ser
deseable. Por ejemplo, Java no tiene punteros, y ODBC hace un uso copioso
de ellos, incluyendo el notoriamente propenso a errores “void * “. Se puede
pensar en JDBC como un ODBC traducido a una interfase orientada a objeto
que es el natural para programadores Java.
4. Un API Java como JDBC es necesario en orden a permitir una solución Java
“pura”. Cuando se usa ODBC, el gestor de drivers de ODBC y los drivers
deben instalarse manualmente en cada máquina cliente. Como el driver JDBC
esta completamente escrito en Java, el código JDBC es automáticamente
instalable, portable y seguro en todas las plataformas Java.
6
1.1.4 Modelos en dos y tres pisos.
El API JDBC soporta los modelos en dos y tres pisos de acceso a base de datos.
JDBC
Servidor de BD.
DBMS
Hasta ahora, este nivel intermedio ha sido escrito en lenguajes como C ó C++, que
ofrecen un rendimiento más rápido. De cualquier modo, con la introducción de
compiladores optimizadores que traducen el bytecode en código máquina eficiente,
se está convirtiendo en práctico desarrollar este nivel intermedio en Java.
7
Esta es una gran ventaja al hacer posible aprovechar las características de
robustez, multiproceso y seguridad de Java.
JDBC
Servidor de BD.
DBMS
SQL es el lenguaje estándar para el acceso a las bases de datos relacionales. Una
de las áreas de dificultad es que aunque muchas DBMS’s (Data Base Management
Systems) usan un formato estándar de SQL para la funcionalidad básica, estos no
conforman la sintaxis más recientemente definidas o semánticas para
funcionalidades más avanzadas. Por ejemplo, no todas las bases de datos soportan
procedimientos almacenados o joins de salida, y aquellas que lo hacen no son
consistentes con otras. Es de esperar que la porción de SQL que es
verdaderamente estándar se expandirá para incluir más y más funcionalidad.
Entretanto, de cualquier modo, el API de JDBC debe soportar SQL tal como es.
Una forma en que el API JDBC trata este problema es permitir que cualquier
cadena de búsqueda se pase al driver subyacente del DBMS. Esto quiere decir que
una aplicación es libre de usar la sentencia SQL tal como quiera, pero se corre el
riesgo de recibir un error en el DBMS. De hecho una consulta de una aplicación
incluso no tiene por que ser SQL, o puede ser una derivación especializada de SQL
8
diseñada para especificas DBMS (para consultas a imágenes o documentos por
ejemplo).
Una segunda forma en que JDBC trata este problema es proveer cláusulas de
escape al estilo de ODBC , que se discutirán en el 4.1.5. ”Sintaxis de escape en
Sentencias Objetos”.
La sintaxis de escape provee una sintaxis JDBC estándar para varias de las áreas
más comunes de divergencia SQL. Por ejemplo, ahí escapes para literales de fecha
o procedimientos almacenados.
Existen una serie de productos basados en JDBC que ya han sido desarrollados.
Por supuesto la información de este apartado será rápidamente obsoleta.
http://java.sun.com/products/jdbc
9
1.2.1 JavaSoft Framework
• El puente JDBC-ODBC
La suite de testeo JDBXC suministra seguridad y confianza en los drivers JDBC que
se ejecutarán en el programa. Solo los drivers que pasan el test pueden ser
designados JDBC COMPLIANT.
El puente JDBC-ODBC permite a los drivers ODBC usarse como drivers JDBC. Fue
implementado como una forma de llegar rápidamente al fondo de JDBC y para
proveer de acceso a los DBMS menos populares si no existen drivers JDBC para
ellos.
10
1.2.2 Tipos de drivers JDBC
Los drivers que son susceptibles de clasificarse en una de estas cuatro categorías.
2.- driver Java parcialmente Nativo. Este tipo de driver convierte llamadas JDBC en
llamadas del API cliente para Oracle, Sybase, Informix, DB2 y otros DBMS. Nótese
que como el driver puente, este estilo de driver requiere que cierto código binario
sea cargado en cada máquina cliente.
3.- driver Java nativo JDBC-Net. Este driver traduce llamadas JDBC al protocolo de
red independiente del DBMS que después es traducido en el protocolo DBMS por el
servidor. Este middleware en el servidor de red es capaz de conectar a los clientes
puros Java a muchas bases de datos diferentes. El protocolo específico usado
dependerá del vendedor. En general esta es la alternativa más flexible.
4.- driver puro Java y nativo-protocolo.. Este tipo de driver convierte llamadas JDBC
en el protocolo de la red usado por DBMS directamente. Esto permite llamadas
directas desde la máquina cliente al servidor DBMS y es la solución más práctica
para accesos en intranets. Dado que muchos de estos protocolos son propietarios,
los fabricantes de bases de datos serán los principales suministradores.
Esperamos que las alternativas 3 y 4 sean las formas preferidas de acceder a las
bases de datos desde JDBC. Las categorías 1 y 2 son soluciones interinas cuando
no están disponibles drivers directos puros Java.
11
2. CONEXIÓN
Un objeto Connection representa una conexión con una base de datos. Una
sesión de conexión incluye las sentencias SQL que se ejecutan y los resultados que
son devueltos después de la conexión. Una única aplicación puede tener una o más
conexiones con una única base de datos, o puede tener varias conexiones con
varias bases de datos diferentes.
Dado que URL’s causan a menudo cierta confusión, daremos primero una breve
explicación de URL en general y luego entraremos en una discusión sobre URL’s de
JDBC.
12
Una URL (Uniform Resource Locator) da información para localizar un recurso en
Internet. Puede pensarse en ella como una dirección.
La primera parte de una URL especifica el protocolo usado para acceder a la
información y va siempre seguida por dos puntos. Algunos protocolos comunes son
ftp, que especifica “file transfer protocol” y http que especifica “hypertext transfer
protocol”. Si el protocolo es “file” indica que el recurso está en un sistema de
ficheros local mejor que en Internet : veamos unos ejemplos:
ftp://javasoft.com/docs/JDK-1_apidocs.zip
http://java.sun.com/products/jdk/CurrentRelease
file:/home/haroldw/docs/books/tutorial/summary.html
El resto de la URL, todo después de los dos puntos, da información sobre donde se
encuentra la fuente de los datos. Si el protocolo es file, el resto de la URL es el path
al fichero. Para los protocolos ftp y http, el resto de la URL identifica el host y
puede opcionalmente dar un path más específico al sitio. Por ejemplo, el siguiente
es la URL para la home page de JavaSoft. Esta URL identifica solo al host:
http://java.sun.com
Una URL JDBC suministra una forma de identificar una base de datos para que el
driver apropiado pueda reconocerla y establecer la conexión con ella. Los
desarrolladores de drivers son los que determinan actualmente que JDBC URL
identifica su driver particular. Los usuarios no tienen por que preocuparse sobre
como se forma una URL JDBC; ellos simplemente usan la URL suministrada con el
driver que usan. El rol de JDBC es recomendar algunas convenciones a los
fabricantes de drivers para su uso.
Dado que los JDBC URL se usan con varios tipos de drivers, las convenciones son
necesariamente muy flexibles. Primero, permiten a diferentes drivers usar diferentes
esquemas para nombrar las bases de datos. EL subprotocolo odbc, por ejemplo,
permite que las URL contengan valores de atributos (pero no los requieren).
Segundo, las URL’s JDBC permiten a los desarrolladores de drivers codificar toda la
información de la conexión dentro de ella. Esto hace posible, por ejemplo, para un
applet que quiera hablar con una base de datos dada el abrir la conexión sin
necesitar que el usuario realice ninguna tarea de administración de sistemas.
Tercero, las URL’s JDBC permiten un nivel de indirección. Esto quiere decir que la
URL JDBC puede referirse a una base de datos lógica o un host lógico que se
traduce dinámicamente al nombre actual por el sistema de nombramiento de la red.
13
Esto permite a los administradores de sistemas evitar dar especificaciones de sus
hosts como parte del nombre JDBC. Hay una variedad de servicios de
nomenclatura de red diferentes (tales como DNS, NIS y DCE), y no hay restricción
acerca de cual usar.
La sintaxis para las URL’s JDBC que se muestra a continuación tiene tres partes
separadas por dos puntos:
jdbc:<subprotocol>:<subname>
jdbc:dcenaming:accounts-payable
14
subnombre y debería seguir a la convención estándar de nomenclatura de URL.
//hostname:port/subsubname
15
2.1.6 Envío de Sentencias SQL
Una vez que la conexión se haya establecido, se usa para pasar sentencias SQL a
la base de datos subyacente. JDBC no pone ninguna restricción sobre los tipos de
sentencias que pueden enviarse: esto da un alto grado de flexibilidad, permitiendo
el uso de sentencias específicas de la base de datos o incluso sentencias no SQL.
Se requiere de cualquier modo, que el usuario sea responsable de asegurarse que
la base de datos subyacente sea capaz de procesar las sentencias SQL que le
están siendo enviadas y soportar las consecuencias si no es así. Por ejemplo, una
aplicación que intenta enviar una llamada a un procedimiento almacenado a una
DBMS que no soporta procedimientos almacenados no tendrá éxito y generará una
excepción. JDBC requiere que un driver cumpla al menos ANSI SQL-2 Entry Level
para ser designado JDBC COMPLIANT. Esto significa que los usuarios pueden
contar al menos con este nivel de funcionalidad.
JDBC suministra tres clases para el envío de sentencias SQL y tres métodos en la
interfaz Connection para crear instancias de estas tres clases. Estas clases y
métodos son los siguientes:
16
El método createStatement se usa para:
2.1.7 Transacciones
Una transacción consiste en una o más sentencias que han sido ejecutadas,
completas y, o bien se ha hecho commit o bien roll-back. Cuando se llama al
método commit o rollback , la transacción actúal finaliza y comienza otra.
Una conexión nueva se abre por defecto en modo auto-commit, y esto significa que
cuando se completa se llama automáticamente al método commit. En este caso,
cada sentencia es ‘commitada’ individualmente, por tanto una transacción se
compone de una única sentencia. Si el modo auto-commit es desactivado, la
transacción no terminará hasta que se llame al método commit o al método
rollback explícitamente, por lo tanto incluirá todas las sentencias que han sido
ejecutadas desde la última invocación a uno de los métodos commit o rollback. En
este segundo caso, todas las sentencias de la transacción son “commitadas’ o
deshechas en grupo.
El método commit hace permanente cualquier cambio que una sentencia SQL
realiza en la base de datos, y libera cualquier bloqueo mantenido por la transacción.
El método rollback descarta estos cambios.
17
2.1.8 Niveles de aislamiento de transacciones
con.setTransactionIsolation(TRANSACTION_READ_UNCOMMITTED);
El nivel de aislamiento más alto, el que más cuidado toma para evitar conflictos. La
interfase Connection define cinco niveles de aislamiento con el nivel más bajo que
especifica que no soporte transacciones hasta el más alto que especifica que
mientras una transacción esté abierta ninguna otra transacción puede realizar
cambios en los datos leídos por esa transacción. Normalmente, el nivel de
transacción más alto es el más lento en la ejecución de la aplicación.(debido a que
se incrementan los bloqueos y se disminuye la concurrencia de los usuarios). El
desarrollador debe balancear la necesidad de rendimiento con la necesidad de la
consistencia de los datos al tomar la decisión del nivel de aislamiento a usar. Por
supuesto, el nivel de aislamiento que pueda soportarse depende de las
posibilidades de la base de datos subyacente.
18
3. LA CLASE DriverManager
La clase DriverManager mantiene una lista de clases disponibles que han sido
registrados mediante el método DriverManager.registerDriver. Todas las
clases Driver deben escribirse con una sección estática que cree una instancia de
la clase y luego la registre en la clase DriverManager cuando se cargue. Además
el usuario normalmente no debería llamar a DriverManager.registerDriver
directamente; debería llamarse automáticamente por el driver cuando esté se
carga,. Una clase Driver se carga, y por tanto se registra, de dos formas
diferentes:
Class.forName("acme.db.Driver");
19
2 Mediante la adición del driver a la propiedad jdbc.drivers de java.lang.System-
Esta es una lista de nombres de clases de drivers, separadas por dos puntos,
que es la que carga la clase DriverManager. Cuando la clase DriverManager
se inicializa, mira en la propiedad jdbc.drivers, y si el usuario ha introducido
uno o más drivers, la clase DriverManager intenta cargarlos. El siguiente
código ilutsra como un programador debería introducir estas tres clases en
~/.hotjava/properties
jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.test.ourDriver;
Notese que en esta segunda manera de cargar los drivers es necesario una
preparación del entorno que es persistente. Si existe alguna duda sobre esto es
preferible y más seguro llamar al método Class.forName para cargar
explicitamente cada driver. Este es también el método que se usa para traer un
driver particular puesto que una vez que la clase DriverManager ha sido
inicializada no chequeará la lista de propiedades jdbc.drivers.
Una vez que la clase Driver ha sido cargada y registrada con la clase
DriverManager, se está en condiciones de establecer la conexión con la base de
datos. La solicitud de la conexión se realiza mediante una llamada al método
DriverManager.getConnection, y DriverManager testea los drivers regsitrados para
ver si puede establecer la conexión.
A veces puede darse el caso de que más de un driver JDBC pueda establecer la
conexión para una URL dada. Por ejemplo, cuando conectamos con una base de
datos remota, podría ser posible usar un driver puente JDBC-ODBC, o un driver
JDBC de protocolo genérico de red, o un driver suministrado por el vendedor.
20
En tales casos, el orden en que los driver son testeados es significante porque
DriverManager usará el primer driver que encuentre que pueda conectar con
éxito a la base de datos.
Testea los drivers mediante la llamada al método Driver.connect cada uno por
turno, pasándole como argumento la URL que el usuario ha pasado originalmente al
método DriverManager.getConnection. El primer driver que reconozca la
URL realiza la conexión.
Una primera ojeda puede parecer insuficiente, pero son necesarias solo unas pocas
llamadas a procedimientos y comparaciones de cadena por conexión puesto que no
es probable que haya docenas de drivers se carguen concurrentemente.
21
4. LA CLASE Statement
Una vez establecida la conexión con una base de datos particular, esta conexión
puede usarse para enviar sentencias SQL. Un objeto Statement se crea mediante
el método de Connection createStatement, como podemos ver en el siguiente
fragmento de código.
Connection con = DriverManager.getConnection(url, "sunny", "");
Statement stmt = con.createStatement();
22
4.1.2 Ejecución de sentencias usando objetos Statement.
Todos los métodos que ejecutan sentencias cierran los objetos Resultset abiertos
como resultado de las llamadas a Statement. Esto quiere decir que es necesario
completar el proceso con el actual objeto Resulset antes de reejecutar una
sentencia Statement.
23
4.1.3 Realización de Statement
Cuando una conexión está en modo auto-commit, las sentencias ejecutadas son
‘comitadas’ o rechazadas cuando se completan. Un sentencia se considera
completa cuando ha sido ejecutada y se han devuelto todos los resultados. Pare el
método executeQuery, que devuelve un único result set, la sentencia se completa
cuando todas las filas del objeto ResultSet se han devuelto. Para el método
executeUpdate, un sentencia se completa cuando se ejecuta. En los raros casos
en que se llama al método execute, de cualquier modo, no se completa hasta que
los result sets o update counts que se generan han sido devueltos.
Los objetos Statement pueden contener sentencias SQL que usen sintaxis de
escape SQL. La sintaxis de escape señala al driver que el código que lleva debe ser
tratado diferentemente. El driver buscará por cualquier sintaxis de escape y lo
traducirá en código que entiende la base de datos en particular. Esto hace que la
sintaxis de escape sea independiente de la DBMS y permite al programador usar
características que de otro modo no estarían disponibles.
Una clausula de escape se enmarca entre llaves y tiene una palabra clave:
{keyword . . . parameters . . . }
24
La palabra clave (keyword) indica el tipo de clausula de escape, según se muestra:
Los caracteres “%” y “_” trabajan como wildcards en la clausula SQL LIKE
(“%” significa cero o más caracteres y “_” significa exactamente un carácter”.
En orden a interpretarlos literalmente, pueden estar precedidos por un
backslash (‘\’), que es un carácter de escape especial en cadenas. Se puede
especificar un carácter que se use como carácter de escape por la inclusión
de la sintaxis siguiente al final de la consulta.
{escape 'escape-character'}
{fn user()};
Las funciones escalares pueden estar soportadas por diferentes DBMS con
ligeras diferencias de sintaxis, y pueden no estar disponibles en todos los
drivers. Varios métodos de DatabaseMetaData nos listarán las funciones
que están soportadas. Por ejemplo, el método getNumericFunctions devuelve
una lista de los nombres de las funciones numéricas separadas por comas, el
método getStringFunction nos devuelve los nombres de las funciones de
cadena, y así varías más.
25
• d, t y ts para literales de fecha y tiempo
Las DBMS difieren en la sintaxis que usan para los literales de fecha, tiempo
y timestamp. JDBC soporta un formato estándar ISO para estos literales y
usa una clausula de escape que el driver debe traducir a la representación
del DBMS.
{t `hh:mm:ss'}
{ts `yyyy-mm-dd hh:mm:ss.f . . .'}
La parte fraccional de los segundos (.f . . .) del TIMESTAMP puede
omitirse.
Los argumentos de entrada pueden ser bien literales, bien parámetros. Ver la
sección 7 “CallableStatement” de esta guía.
26
• oj para joins de salida
Las Outer joins son una característica avanzada, y solo puede chequearse la
gramática SQL mediente una explicación de ella. JDBC provee tres métodos
de DatabaseMetaData para determinar que tipos de outer joins soporta un
driver: supportsOuterJoins, supportsFullOuterJoins, y
supportsLimitedOuterJoins.
El método execute debería usarse solamente cuando es posible que una sentencia
nos devuelva más de un objeto Resultset., mas de un update count o una
combinación de ambos. Estas múltiples posibilidades para resultados, aunque
raras, son posibles cuando se ejecutan ciertos procedimientos almacenados o por
la ejecución dinámica de una string SQL desconocida (esto es, desconocida para el
programador de la aplicación en tiempo de compilación). Por ejemplo, una usuario
podría ejecutar un procedimiento almacenado (usando una objeto
CallableStatement y este procedimiento podría ejecutar una actualización,
después una select, luego una actualización, después una select y así.
Normalmente, alguien que usa un procedimiento almacenado sabrá que se le va a
devolver.
27
Después de usar el método execute para ejecutar el procedimiento, se debe
llamar al método getResultSet para conseguir el primer result set y después los
métodos apropiados getXXX para recuperar los valores de él. Para conseguir el
segundo result set, se necesita llamar al método getMoreResults y y despues a
getResultSet de nuevo. Si se sabe que el procedimiento devuelve dos upadte
counts, se llama primero al método getUpdateCount, seguido de
getMoreResults y de nuevo getUpdateCount.
Aquellos casos en los que no se conoce que devolverá se nos presenta una
situación más compleja. El método execute devuelve true si el resultado es un
objeto ResultSet y false si es un int Java. Si devuelve un int, esto quiere
decir que el resultado o bien es un update count o que la sentencia que ha
ejecutado es un comando DDL. Lo primero que hay que hacer después de llamar
execute es llmar o bien a getResultSet o getUpdateCount. Al método
getResultSet se le llama para conseguir el primero de los dos o más objetos
ResultSet y al método getUpdateCount para conseguir el primero de dos o más
update counts.
28
((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1))
ResultSet rs = stmt.getResultSet;
if (rs != null) {
. . . // use metadata to get info about result set columns
while (rs.next()) {
. . . // process results
stmt.getMoreResults();
continue;
}
break; // there are no more results
29
5. LA CLASE ResultSet
Un ResultSet contiene todas las filas que satisfacen las condiciones de una
sentencia SQL y proporciona el acceso a los datos de estas filas mediante un
conjunto de métodos get que permiten el acceso a las diferentes columnas de la
filas. El método ResultSet.next se usa para moverse a la siguiente fila del result
set, convirtiendo a ésta en la fila actúal.
El formato general de un result set es una tabla con cabeceras de columna y los
valores correspondientes devueltos por la ‘query’. Por ejemplo, si la ‘query’ es
SELECT a, b, c FROM Table1, el resultado tendrá una forma semejante a :
a b c
-------- --------- --------
12345 Cupertino CA
83472 Redmond WA
83492 Boston MA
30
En SQL, el cursor resultado para una tabla tiene nombre. Si una base de datos
permite upadtes posicionados o deletes posicionados, el nombre del cursor es
necesario y debe ser proporcionado como un parámetro del comando update o
delete. El nombre del cursor puede obtenerse mediante una llamada al método
getCursorName.
No todas las bases de datos soportan updates o deletes posicionados. Los métodos
DatabaseMetaData.supportsPositionedDelete y
DatabaseMetaData.supportsPositionedUpdate nos permiten descubrir si
estas operaciones están soportadas en una conexión dada. Cuando lo están, el
driver o la DBMS deben asegurarse que las filas seleccionadas están
apropiadamente bloquedas y por tanto que estas operaciones no provoquen
actualizaciones anomalas ni otros problemas de concurrencia.
5.1.2 Columnas
Los métodos getXXX suministran los medios para recuperar los valores de las
columnas de la fila actúal. Dentro de cada fila, los valores de las columnas pueden
recuperarse en cualquier orden, pero para asegurar la máxima portabilidad,
deberían extraerse las columnas de izquierda a derecha y leer los valores de las
columnas una única vez.
La opción de usar el nombre de columna fue provista para que el usuario que
especifica nombres de columnas en una ‘query’ pueda usar esos nombres como
argumentos de los métodos getXXX. Si, por otro lado, la sentencia select no
especifica nombres de columnas (tal como en “select * from table1” o en
casos donde una columna es derivada), deben usarse los números de columna. En
estas situaciones , no hay forma de que el usuario sepa con seguridad cuales son
los nombres de las columnas.
31
En algunos casos, es posible para una query SQL devolver un result set con más de
una columna con el mismo nombre. Si se usa el nombre de columna como
argumento en un método getXXX, éste devolverá el valor de la primera columna
que coincida con el nombre. Por eso, si hay múltiples columnas con el mismo
nombre, se necesita usar un índice de columna para asegurarse que se recupera el
valor de la columna correcta. Esto puede ser ligeramente más eficiente que usar los
números de columna.
Para los métodos getXXX, el driver JDBC intenta convertir los datos subyacentes a
tipos de datos Java . Por ejemplo, si el método getXXX es getString y los tipos de
datos de la base de datos en la base subyacente es VARCHAR, el driver JDBC
convertirá VARCHAR en String de Java. El valor devuelto por getString será un
objeto Java de tipo String.
La siguiente tabla muestra que tipos JDBC está permitido devolver para un método
getXXX y que tipos JDBC (tipos genéricos de SQL) se recomiendan para
recuperarlos. Una x indica un método getXXX legal para un tipo de dato particular.
Por ejemplo, cualquier método getXXX excepto getBytes o getBinaryStream puede
usarse para recuperar valores de tipo LONGVARCHAR, pero se recomienda usar
getAsciiStream o getUnicodeStream, dependiendo de que tipo de dato se devuelve.
El método getObject devolverá cualquier tipo de dato como un Object Java y es util
cuando los tipos de datos de la DBMS subyacente son abstractos específicos de
ésta o cuando una aplicación genérica necesita aceptar cualquier tipo de datos.
Una “x” indica que el método getXXX puede legalmente usarse para recuperar el
tipo JDBC dado.
Una “X” indica que el método getXXX es el recomendado para recuperar el tipo de
dato dado.
32
5.1.4 Uso de Streams valores muy grandes de filas
T S I B R F D D N B C V L B V L D T T
I M N I E L O E U I H A O I A O A I I
N A T G A O U C M T A R N N R N T M M
Y L E L A B I E R C G A B G E E E
I L G N T L M R H V R I V S
N I E T E A I A A Y N A T
T N R L C R R A R A
T C R B M
H Y I P
A N
R A
R
Y
getByte Xx x x x x x x x x x x x
getShort x X x x x x x x x x x x x
getInt x x X x x x x x x x x x x
getLong x x x Xx x x x x x x x x
getFloat x x x x Xx x x x x x x x
getDouble x x x x x XX x x x x x x
getBigDecimal x x x x x x x X X x x x x
getBoolean x x x x x x x x x Xx x x
getString x x x x x x x x x x XXx x x x x x x
getBytes XXx
getDate x x x X x
33
getTime x x x X x
getTimestamp x x x x X
getAsciiStream x x Xx x x
getUnicodeStream x x Xx x x
getBinaryStream x x X
getObject x x x x x x x x x x x x x x x x x x x
34
De todos modos, puede ser conveniente recuperar datos muy grandes en ‘pedazos’
de tamaño fijo. Esto se hace mediante la clase ResultSet que devuelve ‘streams’
java.io.Input desde los cuales los datos pueden ser leidos en ‘pedazos’. Nòtese que
estas corrientes deben ser accedidas inmediatamente porque se cierran
automáticamente con la llamada al siguiente método getXXX de ResultSet. (Este
comportamiento está impuesto por las restricciones de implementación de acceso a
grandes blob).
El API JDBC tiene tres métodos diferentes para recuperar streams, cada uno con
un valor diferente de retorno.
Notar que esto difiere de las corrientes Java que devuelven bytes sn tipo y pueden
(por ejemplo) usarse para ambos caracteres ASCII y Unicode.
1
Unicode
Unicode es un esquema de codificación de caracteres que utiliza 2 bytes por cada carácter. ISO
(International Standards Organization) define un número dentro del intervalo 0 a 65.535 (216 – 1) por
cada carácter y símbolo de cada idioma (más algunos espacios vacíos para futuras ampliaciones). En
todas las versiones de 32 bits de Windows, el Modelo de objetos componentes (COM), que es la
base de las tecnologías OLE y ActiveX, utiliza Unicode. Unicode es totalmente compatible con
35
output.write(buff, 0, size);
}
}
Para determinar si un valor resultado dado es JDBC NULL, primero debe intentarse
leer la columna y usar el método ResultSet.wasNull para descubrir si el valor
devuelto es JDBC NULL
• Un valor null de Java para aquellos métodos getXXX que devuelven objetos
Java (tales como getString, getBigDecimal, getBytes, getDate,
getTime, getTimestamp, getAsciiStream, getUnicodeStream,
getBinaryStream, getObject).
36
No se necesita hacer nada para cerrar un ResultSet. Se cierra automáticamente
cuando por la Statement que la crea cuando se cierra esta, y se reutiliza cuando
cuando se recupera el próximo resultado de una secuencia de múltiples resultados.
6. LA CLASE PreparedStatement
37
El objeto pstmt contiene ahora la sentencia “UPDATE table4 SET m= ? WHERE
x = ? “, que ya ha sido enviada a la base de datos y ha sido preparada para su
ejecución.
Una vez que el valor ha sido fijado para una sentencia dada, puede usarse para
múltiples ejecuciones de esa sentencia hasta que se limpie mediante el método
ClearParameters.
En el modo por defecto para una conexión (modo auto-commit activo), cada
sentencia es conmitada o rechazada automáticamente cuando se completa.
38
6.1.3 Conformidad de tipos de datos en parámetros IN
El XXX en los métodos setXXX son tipos Java. Estos son los tipos implícitos de
JDBC (tipos genéricos SQL) porque el driver mapeará el tipo Java en su
correspondiente tipo JDBC (ver la sección 8 para más información sobre el mapeo
de tipos), y envían ese tipo JDBC a la base de datos. El siguiente ejemplo fija el
segundo parámetro del objeto PreparedStatement pstmt a 44 con un tipo Java
short.
pstmt.setShort(2, 44);
39
La capacidad de el método setObject para aceptar cualquier objeto Java permite
a una aplicación ser genérica y aceptar entradas para un parámetro en tiempo de
ejecución. En esta situación el tipo de entrada no es conocido cuando la aplicación
es compilada. Mediante el uso de setObject, la aplicación puede aceptar
cualquier tipo de objeto Java como entrada al tipo JDBC esperado por la base de
datos. La tabla de la sección 8.6.5 muestra todas las posibles conversiones qiue
setObject puede realizar.
También se enviará un JDBC NULL a la base de datos cuando un valor Java null
se pasa mediante un método setXXX (si los acepta los objetos Java como
argumentos). El método setObject, en cualquier caso, puede tomar un valor null
únicamente si se ha especificado el tipo JDBC.
40
java.io.File file = new java.io.File("/tmp/data");
int fileLength = file.length();
java.io.InputStream fin = new java.io.FileInputStream(file);
java.sql.PreparedStatement pstmt = con.prepareStatement(
"UPDATE Table5 SET stuff = ? WHERE index = 4");
pstmt.setBinaryStream (1, fin, fileLength);
pstmt.executeUpdate();
7. LA CLASE CallableStatement
41
procedimientos almacenados y el método getProcedures devolverá una
descripción de los procedimientos almacenados disponibles.
donde los encajes ‘?’ son parámetros IN, OUT ó INOUT dependiendo del
procedimiento getTestData.
42
JDBC (por tanto coincide con el tipo con el tipo JDBC que la base de datos
devolverá) y getXXX ‘casts’ este a un tipo Java.
Para ilustrar esto, el siguiente ejemplo registra los parámetros OUT, ejecuta el
procedimiento almacenado llamado por cstmt y recupera los valores devueltos en
los parámetros OUT. El método getByte recupera un byte Java de el primer
parámetro, y getBigDecimal recupera un objeto BigDecimal (con tres dígitos
después del punto decimal) del segundo parámetro OUT:
CallableStatement cstmt = con.prepareCall(
"{call getTestData(?, ?)}");
cstmt.registerOutParameter(1, java.sql.Types.TINYINT);
cstmt.registerOutParameter(2, java.sql.Types.DECIMAL, 3);
cstmt.executeQuery();
byte x = cstmt.getByte(1);
java.math.BigDecimal n = cstmt.getBigDecimal(2, 3);
Son parámetros que suminstran entradas así como aceptan salidas. Estos
requieren llamar a los métodos apropiados setXXX (heredados de
PreparedStatement) además de llamar al método registerOutParameter.
Los métodos setXXX fijan los valores como parámetros de entrada y
registerOutParameter registra sus tipos JDBC como parámetros de salida. El
método setXXX suminstra un valor Java que el driver convierte en un valor JDBC
antes de enviarlo a la base de datos.
43
CallableStatement cstmt = con.prepareCall(
"{call reviseTotal(?)}");
cstmt.setByte(1, 25);
cstmt.registerOutParameter(1, java.sql.Types.TINYINT);
cstmt.executeUpdate();
byte x = cstmt.getByte(1);
Después de hecho esto, los valores de los parámetros OUT pueden ser
recuperados mediante los métodos CallableStatement.getXXX.
El valor devuelto en un parámetro OUT puede ser JDBC NULL. Cuando esto ocurre,
le valor JDBC NULL se convertirá de forma que el valor devuelto por el método
getXXX sea null, 0 o false dependiendo del tipo del método getXXX usado.Como
con los objetos ResultSet, la única manera de saber si un valor de 0 o false fue
originalmente NULL JDBC es testear el método wasNull, que devuelve true si el
último valor leído por un método getXXX fue JDBC NULL y false en caso contrario.
La sección 5 contiene más información al respecto.
44
9. EJEMPLO DE CODIGO
// The following code can be used as a template. Simply
// substitute the appropriate url, login, and password, and then
substitute the
// SQL statement you want to send to the database.
//-------------------------------------------------------------------
---------
//
// Module: SimpleSelect.java
//
// Description: Test program for ODBC API interface. This java
application
// will connect to a JDBC driver, issue a select statement
// and display all result columns and rows
//
// Product: JDBC to ODBC Bridge
//
// Author: Karl Moss
//
// Date: February, 1996
//
// Copyright: 1990-1996 INTERSOLV, Inc.
// This software contains confidential and proprietary
// information of INTERSOLV, Inc.
//-------------------------------------------------------------------
---------
import java.net.URL;
import java.sql.*;
class SimpleSelect {
try {
Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");
DriverManager.setLogStream(System.out);
45
// If we were unable to connect, an exception
// would have been thrown. So, if we get here,
// we are successfully connected to the URL
dispResultSet (rs);
rs.close();
stmt.close();
con.close();
}
catch (SQLException ex) {
46
System.out.println ("\n*** SQLException caught ***\n");
ex.printStackTrace ();
}
}
//-------------------------------------------------------------------
// checkForWarning
// Checks for and displays warnings. Returns true if a warning
// existed
//-------------------------------------------------------------------
if (warn != null) {
System.out.println ("\n *** Warning ***\n");
rc = true;
while (warn != null) {
System.out.println ("SQLState: " +
warn.getSQLState ());
System.out.println ("Message: " +
warn.getMessage ());
System.out.println ("Vendor: " +
warn.getErrorCode ());
System.out.println ("");
warn = warn.getNextWarning ();
}
}
return rc;
}
47
//-------------------------------------------------------------------
// dispResultSet
// Displays all columns and rows in the given result set
//-------------------------------------------------------------------
48