JDBC
JDBC es un conjunto de clases e interfaces Java para la ejecución de sentencias SQL
Es la parte CLI de Java (Call-Level Interface)
Fue desarrollado conjuntamente por JavaSoft, Sybase, Informix e IBM entre otros
JDBC permite la manipulación de cualquier base de datos SQL
No es necesario hacer un programa específico para manipular Oracle, Sybase, etc…
Nuestro programa puede manipular cualquier base de datos
Uniendo Java con JDBC obtenemos programas que se pueden ejecutar en cualquier plataforma y que pueden manipular cualquier base de datos
Las clases e interfaces JDBC se encuentran dentro del paquete java.sql
Proceso de trabajo con JDBC
El proceso de trabajo en JDBC consiste en los siguientes pasos:
-
Conectarse a la base de datos (mediante DriverManager)
-
Emitir sentencias SQL (mediante Statement, PreparedStatement, CallableStatement)
-
Procesamiento de los resultados (ResultSet)
Driver JDBC
Cada base de datos concreta implementa la interface JDBC de una manera particular, al igual que las clases auxiliares y de utilidad que sean necesarias para esa base de datos
Por eso son necesarios los drivers de bases de datos
Una misma aplicación podrá conectar con distintas bases de datos simplemente cambiando ese driver (no debemos confundir el driver de la base de datos con la clase Driver, pues son cosas distintas)
El interface Driver especifica los métodos que todo driver JDBC debe implementar, para ello se pueden cargar los drivers de dos maneras:
-
Cuando se inicia la clase Driver, el DriverManager consulta la propiedad jdbc.drivers
Dicha propiedad contiene una lista de drivers (las clases) que deben ser cargadas
Para ello deberá ejecutarse un comando como este (para usar ODBC) en la línea de comandos:
-
Si se desea introducir un nuevo driver después de que el DriverManager se halla inicializado se deberá usar el método forname de la clase Class
Para ello deberemos incluir en nuestro código la siguiente instrucción (para usar ODBC):
Es aconsejable que se use de forma static en nuestra aplicación (porque sólo se cargará el driver una vez, al cargar la clase)
Base de datos | Driver |
---|---|
ODBC | sun.jdbc.odbc.JdbcOdbcDriver |
Oracle | oracle.jdbc.driver.OracleDriver |
SQLServer | com.microsoft.jdbc.sqlserver.SQLServerDriver |
MySQL | com.mysql.jdbcDriver |
Tipos de driver:
- Puente JDBC-ODBC
Traduce JDBC a ODBC y lo retransmite al driver ODBC de la máquina
Es el driver ODBC el que realmente se comunica con la base de datos
Está incluido en el JDK pero no incluye JDBC2
Inconvenientes:
-
Es útil para realizar pruebas, pero es lento en producción
-
Es necesario el driver ODBC en el cliente (menor portabilidad)
-
- Driver JDBC sobre driver nativo de la BD
Retransmite JDBC al driver nativo instalado en la máquina
El driver nativo es el que realmente se comunica con al base de datos
Inconveniente:
- necesidad de driver nativo (menor portabilidad)
- Driver Java sobre red
Traduce las llamadas JDBC a un protocolo de red independiente de la plataforma que contacta con el servidor
El servidor traduce esas peticiones al protocolo concreto de cada base de datos
Se usa un middleware en el servidor de red que es capaz de conectar a los clientes puros Java a muchas bases de datos diferentes
Ventaja:
-
es rápido, independiente de la plataforma y no requiere de instalación en el cliente
-
- Driver puro Java con protocolo nativo
Traduce las llamadas JDBC al protocolo específico de la base de datos contactando directamente con ella
Obtener conexiones
Mediante el driver concreto realizaremos las conexiones, pero las solicitudes de conexión deben ser realizadas mediante el DriverManager
Una vez que la clase Driver ha sido cargada y registrada, el DriverManager puede establecer conexiones con la base de datos mediante estos dos pasos:
-
Se llama al método DriverManager.getConnection(url, user, pass); y se obtiene un objeto de tipo Connection
-
El DriverManager prueba los drivers registrados para ver si puede establecer la conexión y si no fue posible, lanza una SQLException
Una misma aplicación, puede tener varias conexiones a la misma base de datos o varias conexiones a otras bases de datos (hasta el máximo permitido por la base de datos)
Los parámetros que admite getConnection son la url (que es un subprotocolo que utiliza la base de datos para realizar la conexión), el user (el nombre de usuario que va a conectarse) y el pass (la contraseña que utiliza el usuario para conectarse)
El parametro url tiene el siguiente formato:
El subprotocolo es particular de cada base de datos y lo utiliza el DriverManager para buscar el driver adecuado para manipularla
El subnombre depende del subprotocolo concreto
El driver será el encargado de interpretarlo y le ayudará a localizar la base de datos
Nuestra aplicación no trabajará con el driver concreto, sino que lo hará el DriverManager
De esta manera las aplicaciones pueden trabajar con el objeto Connection sin preocuparse por el tipo de base de datos con la cual estemos trabajando (no hay que hacer modificaciones en el código, solo cambiar el parámetro url)
La conexión debe cerrarse siempre al finalizar, porque sino se consumen recursos de forma innecesaria, aunque se pueden reutilizar conexiones, no es aconsejable, es mejor usar siempre una conexión nueva
En el ejemplo se ha utilizado un esqueleto de conexión para Oracle
Para utilizar la conexión debemos crear un objeto de tipo Connection, el cual representa una sesión abierta con la base de datos
El objeto provee de un contexto con el que poder emitir sentencias SQL y obtener resultados
El objeto Connection debe inicializarse inicialmente a null, así podremos comprobar si hubo conexión porque incluiremos un bloque try para gestionar errores, el cuál lanzará SQLException
Para cerrar la conexión debe hacerse mediante el método close(), dentro de un finaly (el cual también tendrá su bloque try que lanza SQLException)
El interface Statement
La conexión nos permite crear objetos Statement mediante el método createStatemen()
Los objetos Statement permiten la ejecución de sentencias SQL y la obtención de resultados (mediante los objetos ResultSet)
Los métodos para ejecutar SQL aceptan String como parámetro (JDBC es CLI), los cuales pueden componerse dinámicamente en función de valores contenidos en variables
Para componer el String SQL habrá que concatenar los distintos fragmentos SQL (los estáticos y las variables)
Hay tres formas de ejecutar sentencias SQL:
-
Mediante el método executeQuery(<sql>) para consultas mediante la sentencia SELECT que produce túplas como resultado (devuelve un objeto de tipo ResultSet)
-
Es como una tabla que almacena el resultado de la consulta
-
Tiene una serie de métodos de consulta
-
Es como un cursor (en terminología PL / SQL)
-
-
Mediante el método executeUpdate(<sql>) para actualizaciones mediante las sentencias INSERT, DELETE, UPDATE, así como comandos DDL (CREATE, DROP, ALTER TABLE, ADD) y bloques PL / SQL (entre los bloques begin y end)
-
Devuelve un entero que indica el número de filas modificadas por el comando
-
Con los comandos DDL devuelve 0
-
-
Mediante el método execute(<sql>) que ejecuta cualquier sentencia SQL
-
Si se utilizó un QUERY, devolverá True
-
El ResultSet se puede obtener mediante el método getResultSet()
-
-
Si se utilizó un UPDATE, devolverá False
-
El total de filas modificadas se pueden obtener mediante el método getUpdateCount()
-
-
Después de procesar el resultado, se debe cerrar el Statement mediante el método close()
Este método cierra también el ResultSet asociado, de todas formas, Sun recomendaba cerrar el ResultSet de forma explícita para evitar errores no deseados
Un mismo Statement puede reutilizarse en una misma conexión para ejecutar distintas sentencias
El interface ResultSet
El objeto ResultSet actúa como un cursor dentro de los resultados
La primera vez que se lee apunta encima del primer resultado, pero no lo lee, por eso hay que leer la primera fila avanzando mediante el método next()
El método next() devuelve True si pudo avanzar o False si no pudo
Para obtener los valores del ResultSet se utilizan los métodos get que tienen el siguiente formato get<tipo>(<columna>)
Para nombrar las columnas podemos o hacerlo por su nombre o por un indice, el cual empieza en 1, su numeración viene dada por el orden en que fue introducida en el SELECT
Hay que prestar atención a las fechas, porque se guardan como java.sql.Date, no como java.util.Date como se podría esperar
Conviene cerrar el ResultSet aunque se cierra implícitamente al cerrar o reutilizar el Statement que lo creó
Cuando se ha leído un null de SQL usando uno de los métodos get<tipo>, éste devuelve:
-
Un valor null de Java para aquellos métodos que devuelven objetos Java (getString(), getBigDecimal(), getDate(), getTime(), getTimestamp(), getObject(), etc)
-
Un valor 0 para aquellos métodos que devuelven tipos numéricos (getByte(), getShort(), getInt(), getLong(), getFloat(), getDouble() )
-
Un valor False para el método getBoolean()
Para determinar si un valor dado era null, primero debe intentar leerse la columna y usar el método de ResulSet wasNull() para saber si fue null
Devolverá True si lo era, False en caso contrario
El interface PreparedStatement
Cada vez que se lanza un Statement la base de datos debe interpretarla y calcular un plan de consulta
Pero al usar PreparedStatement, se puede ejecutar múltiples veces, obteniendo un aumento de rendimiento al tener la consulta ya analizada y optimizada
No todas las bases de datos soportan PreparedStatement, hay que leer la documentación de la misma para saber si se pueden usar
Los objetos PreparedStatement derivan del Statement obtenido de la conexión, mediante el método prepareStatement(<sql>);
Un PreparedStatement sirve para lanzar instrucciones SQL precompiladas, podremos parametrizar una o más entradas mediante el operador ?, cuyos valores podrán ser modificados en distintas ejecuciones de la instrucción
Parametrizar las entradas es útil cuando desconocemos los valores de ciertos tipos de datos SQL en la base de datos de destino,
El valor parametrizado obtiene el valor correcto del driver de la base de datos, por lo que no deberemos preocuparnos por los tipos
Antes de que la instrucción SQL pueda ser ejecutada deberemos asignarle un valor a los parámetros de entrada
Para asignar valores usaremos los métodos set<tipo>(columna, valor); siendo el tipo compatible con el del parámetro
Para ejecutarlo se usan los métodos execute de Statement
En el ejemplo se ha utilizado un PreparedStatement para Oracle