====== Conexión a bases de datos ======
La librería Java Database Connectivity (JDBC) es una API que proporciona acceso a bases de datos desde Java. Consta de dos paquetes:
java.sql
javax.sql
{{:bloque4:jdbc_arquitecture.png?300 |}}
Para poder trabajar con un sistema gestor de bases de datos determinado, necesitamos un controlador (Driver) que haga de **intermediario** entre la tecnología JDBC y la base de datos.
Por lo tanto, solo tenemos que descargar el controlador o driver concreto para dicha plataforma.
A la hora de usar cualquier SGDB que use SQL las sentencias son siempre las mismas, solo cambia el driver de conexión.
----
===== Añadir el conector JDBC a nuestro proyecto =====
Para conectarnos con MySQL desde Java necesitamos el driver JDBC para MySql. Lo podemos descargar desde la web de Mysql. https://www.mysql.com/products/connector/
Debemos descargar el conector para JDBC (Java Database Connectivity). En la sección descargas indicaremos nuestro //ssoo// (//platform independent//) y descargaremos un fichero comprimido que contiene el fichero Jar del conector.
Para usar otro SGBD debemos buscar el driver conector en su soporte web.
{{ :bloque4:libs-folder.png?200|}}
Una vez que lo tenemos descargado debemos añadirlo al classpath de nuestro proyecto. Para ello crearemos como siempre una carpeta (p.e. **libs**) y copiamos y pegamos dentro de ella el archivo Jar que acabamos de descargar.
Click derecho sobre nuestro proyecto en el explorador de proyectos de **eclipse -> Build Path -> Configure Build Path**.
En la ventana que se abre seleccionamos Libraries y pulsamos sobre Add JARs. Y añadimos el fichero jar que está en nuestra carpeta //libs//.
{{ :bloque4:jdbc-connector.png?400 | }}
===== Conexión con la Base de Datos MySQL =====
__Siempre usaremos las clases del paquete__ ''java.sql''.
Para realizar una conexión con la base de datos necesitamos:
* Obtener una instancia de la Clase ''Connection''. Representa la conexion con la base de datos.
* La obtenemos a partir del método estático ''getConnection()'' de la clase ''DriverManager''.
''getConnection'' espera unas cadenas que indiquen todos los parámetros de conexión con la base de datos.
Connection conexionBD = null;
String servidor = "jdbc:mysql://localhost:3306/";
String bbdd = "reservas";
String user = "root";
String password = "";
conexionBD = DriverManager.getConnection(servidor + bbdd, user, password);
* ''user'' es el nombre de usuario de conexion a la base de datos MySql
* ''password'' representa la contraseña, que estará en blanco si no la hemos definido en MySql.
* ''servidor'' representa el tipo de driver, y el servidor-puerto al que nos queremos conectar.
* ''bbdd'' representa la base de datos sobre la que queremos trabajar. Es un parámetro opcional.
A partir del objeto ''Connection'' obtenido, realizaremos todas las operaciones contra la base de datos.
==== Cerrar conexión ====
El método ''close()'' de la clase ''Connection'', cierra la conexión con la base de datos.
conexionBD.close();
==== Conexión con otros SGBD ====
(p.e. PostgreSql, MariaDb)
El proceso es el mismo, pero varía el tipo de servidor de bases de datos y también el driver (JAR) que debemos descargar del soporte oficial:
String servidor = "jdbc:postgresql://localhost:5432/";
String servidor = "jdbc:mariadb://localhost:3306/";
===== Consultas sobre la base de datos =====
Las operaciones CRUD responden a un acrónimo usado en bases de datos que hace referencia a las operaciones básicas (create, read, update, delete). En SQL todas las consultas se asocian a algún tipo de operación de las anteriores.
Podremos tratar las consultas desde un enfoque muy simple:
* **Consultas que no devuelven datos** (INSERT, DELETE, UPDATE): llamadas también consultas de acción.
* **Consultas que devuelven datos** (SELECT): llamadas también de selección, porque seleccionamos los datos a mostrar.
==== Preparación de consultas ====
Para preparar una consulta para su ejecución necesitamos usar la clase ''PreparedStatement''. Su sintaxis de uso es bastante sencilla, ya que el método ''preparedStatement(String consulta)'' de la clase ''Connection'' nos devuelve un objeto de este tipo:
String consulta = "INSERT INTO usuarios(nombre, apellidos) VALUES ('Juan','Sola')";
//A partir del objeto Connection obtenemos una consulta preparada
PreparedStatement sentencia = conexion.prepareStatement(consulta);
...
String consulta = "DELETE FROM usuarios WHERE nombre = 'Juan'";
PreparedStatement sentencia = conexion.prepareStatement(consulta);
==== Ejecución de consultas ====
La clase ''PreparedStatement'' contiene principalmente 3 métodos de ejecución de consultas dependiendo del tipo de consulta a ejecutar:
* **Consultas de acción** (INSERT, UPDATE, DELETE)
Usaremos el método ''executeUpdate()'', que devuelve un ''int'' con el número de registros afectados. Ese valor podemos usarlo o no.
//Ejecuto la consulta
sentencia.executeUpdate();
//Ejecuto la consulta y obtengo el número de registros afectados (si lo necesitara)
int numeroRegistros = sentencia.executeUpdate();
* **Consultas de selección** (SELECT, funciones, procedimientos que muestren datos)
Si la consulta devuelve registros, usaremos el método ''executeQuery()'', que devuelve un tipo de datos ''ResultSet'' con el conjunto de registros obtenidos:
ResultSet resultado = sentencia.executeQuery();
Una vez que tenemos el objeto ''ResultSet'' con el conjunto de registros obtenidos, podemos recorrer el conjunto de registros ResultSet con un bucle //while//.
while(resultado.next()){
System.out.println(resultado.getString(1) + ", " + resultado.getString(2));
}
De esta forma mostraríamos por pantalla las columnas 1 y 2 de cada registro obtenido. También podemos usar ''getXXX()'' si queremos obtener el tipo de datos concreto (p.e. getInt(1), getDouble(3), getDate(4)).
**Las columnas están numeradas empezando en el nº 1**.
* **Ejecutar cualquier tipo de consulta**
El método boolean ''execute()'' se usa cuando la consulta puede devolver ninguno, uno o varios conjuntos de registros ''ResultSet''. Esto se hace para tener un mismo método que permite ejecutar cualquier consulta (acción o selección).
boolean estado = sentencia.execute();
Si el método devuelve true, quiere decir que el primer resultado obtenido es un ResultSet (Consulta Select). Podemos acceder a dicho conjunto de registros con el método ''getResultSet()''.
boolean estado = sentencia.execute();
//Si devuelve true es que he obtenido registros
if(estado){
ResultSet resultado = sentencia.getResultSet();
//procesamos el resultado
}
Si devuelve false indica que la consulta es del tipo (INSERT, UPDATE o DELETE), o que no ha devuelto resultados. Podemos acceder al número de registros afectados mediante el método ''getUpdateCount()'' que devuelve ''int''.
==== Parametrizar una consulta ====
La clase ''PreparedStament'' almacena una consulta precompilada a la que podemos añadirle los valores de sus parámetros (caracter ''?'') posteriormente:
String consulta = "INSERT INTO productos(nombre, cantidad, precio) VALUES (?, ?, ?)";
PreparedStatement sentencia = conexion.prepareStatement(consulta);
//Despues de precompilar la consulta, damos valores a sus parámetros
sentencia.setString(1, "miel"); //La primera interrogación
sentencia.setInt(2, 57); //La segunda
sentencia.setFloat(3, 4.34F); //Y la tercera
//Finalmente ejecuto la consulta
sentencia.executeUpdate();
En la consulta SQL puedo indicar mediante el caracter ''?'', los parámetros a los que posteriormente voy a dar valor. La consulta : //"INSERT INTO productos(nombre, cantidad, precio) VALUES (?, ?, ?)"// tiene 3 parámetros.
Posteriormente mediante el objeto PreparedStatement puedo dar valor a esos parámetros indicando el tipo de valor, y el número de parámetro al que le doy el valor (1, 2 ó 3, ya que hay 3 interrogaciones).
==== Obtener el número de columnas de una consulta ====
Si en algún caso al realizar una consulta no sabemos cuántas columnas nos va a devolver, podemos usar el método ''getMetaData()'' que devuelve un objeto del tipo ''ResultSetMetaData''.
Existen dos formas de usar dicho método. Se puede invocar desde un objeto de tipo ''ResultSet'' obtenido de la ejecución de una consulta:
…
ResultSet resultado = sentencia.executeQuery();
ResultSetMetaData resultadoMetaData = resultado.getMetaData();
int numeroColumnas = resultadoMetaData.getColumnCount();
También puedo obtenerlo antes de ejecutar la consulta llamando al método desde un objeto de tipo ''PreparedStatement'':
…
PreparedStatement sentencia = conexión.preparedStatement(consulta);
ResultSetMetaData resultadoMetaData = sentencia.getMetaData();
int numeroColumnas = resultadoMetaData.getColumnCount();
===== Anexo: Ejemplos =====
==== Seleccionar datos ====
String sentenciaSql = "SELECT nombre, precio FROM productos";
PreparedStatement sentencia = null;
try {
sentencia = conexion.prepareStatement(sentenciaSql);
ResultSet resultado = sentencia.executeQuery();
//Mostramos los datos
while(resultado.next()){
System.out.prinln(resultado.getStrig(1) + " - " + resultado.getString(2));
}
} catch (SQLException sqle) {
sqle.printStackTrace();
} finally {
if (sentencia != null)
try {
//Debemos cerrar solo cuando hemos leido los datos.
//Si cerramos antes de recorrer el ResultSet, perdemos los datos.
sentencia.close();
} catch (SQLException sqle) {
sqle.printStackTrace();
}
}
==== Insertar datos ====
String sentenciaSql = "INSERT INTO productos (nombre, precio) VALUES (?, ?)";
PreparedStatement sentencia = null;
try {
sentencia = conexion.prepareStatement(sentenciaSql);
sentencia.setString(1, nombreProducto);
sentencia.setFloat(2, precioProducto);
sentencia.executeUpdate();
} catch (SQLException sqle) {
sqle.printStackTrace();
} finally {
//Nos aseguramos de cerrar los recursos abiertos
if (sentencia != null)
try {
sentencia.close();
} catch (SQLException sqle) {
sqle.printStackTrace();
}
}
==== Modificar datos ====
String sentenciaSql = "UPDATE productos SET nombre = ?, precio = ? " +
"WHERE nombre = ?";
PreparedStatement sentencia = null;
try {
sentencia = conexion.prepareStatement(sentenciaSql);
sentencia.setString(1, nuevoNombreProducto);
sentencia.setFloat(2, precioProducto);
sentencia.setString(3, nombreProducto);
sentencia.executeUpdate();
} catch (SQLException sqle) {
sqle.printStackTrace();
} finally {
//Nos aseguramos de cerrar los recursos abiertos
if (sentencia != null)
try {
sentencia.close();
} catch (SQLException sqle) {
sqle.printStackTrace();
}
}
==== Eliminar datos ====
String sentenciaSql = "DELETE FROM productos WHERE nombre = ?";
PreparedStatement sentencia = null;
try {
sentencia = conexion.prepareStatement(sentenciaSql);
sentencia.setString(1, nombreProducto);
sentencia.executeUpdate();
} catch (SQLException sqle) {
sqle.printStackTrace();
} finally {
//Nos aseguramos de cerrar los recursos abiertos
if (sentencia != null)
try {
sentencia.close();
} catch (SQLException sqle) {
sqle.printStackTrace();
}
}
----
(c) {{date> %Y}} Fernando Valdeón