Académique Documents
Professionnel Documents
Culture Documents
JDBC (Java Database Connectivity) es un API de Java que nos permite conectarnos con bases de datos y realizar
operaciones sobre ellas utilizando instrucciones SQL desde una aplicación Java. Con JDBC tenemos una interfaz
para conectarnos con una base de datos sin tener que preocuparnos de si es una base de datos MySQl, Oracle,
SQLServer o cualquier otro tipo de base de datos. El único cambio que habría que hacer para cambiar el tipo de
base de datos de una aplicación sería cambiar el driver especifico de la base de datos en cuestión.
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException ex) {
log.error("No se encontro el Driver MySQL para JDBC.");
}
//Connection cn = DriverManager.getConnection("jdbc:mysql://servidor_bd:puerto/nombre_bd",
"usuario", "contraseña");
Connection cn = DriverManager.getConnection("jdbc:mysql://localhost:3306/cuentas", "root", "");
Para obtener una conexión tenemos que hacer uso del método getConnection de DriverManager y le tenemos que
pasar como parámetros, la base de datos, el usuario y la contraseña. Si en lugar de usar mysql se usa otra base de
datos el formato de la url de la base de datos cambia, por ejemplo para oracle el formato es
jdbc:oracle:<tipo_driver>:@<<base_datos>.
4. REALIZAR UNA CONSULTA
Una vez obtenida la conexión ya podemos hacer consultas a la base de datos. Hay tres métodos para ejecutar una
consulta de tipo Statement que es una consulta normal, PreparedStatement con la que se puede crear una consulta
que se precompila en la base de datos y como es de esperar se pueden establecer los distintos datos porque aunque
la estructura de la consulta será la misma lo lógico es que los datos de la consulta sean distintos y finalmente están
las CallableStatements que sirven para ejecutar procedimientos almacenados.
Como se puede ver el primer paso es crear un statement sobre el que luego podemos hacer las consultas que
queramos, una vez creado para hacer nuestra consulta tenemos los métodos execute (para hacer cualquier consulta,
pero su valor de devolución es boolean y devuelve true si lo que devuelve es un resulset y falso en caso contrario),
exeteQuery (para hacer select) y executeUpdate (para hacer insert, update y delate). Y finalmente si se ejecuta una
query podemos obtener el resultado mediante ResultSet, para obtener los valores de las columnas resulset tiene un
get para cada tipo de datos que se puede llamar pasándole como parámetro el nombre de la columna de base de
datos o el numero de la columna según se prefiera.
Como se puede ver el código para hacer una consulta no es muy complicado pero es muy propenso a errores porque
hay que estar creando cadenas de texto concatenadas con variables java y hay que recordar que si el dato es de
tipo texto hay que anidar comillas y entre unas cosas y otras es fácil cometer algún error. Para facilitar el trabajo con
bases de datos hay alternativas a trabajar directamente con JDBC como JPA o Hibernate, pero estamos hablando
de JDBC…
Si usamos PreparedStatement además de que son más eficientes nos facilitan la tarea de escribir las consultas SQL
porque en lugar de tener que estar concatenando las distintas variables con el código SQL, lo que se hace es sustituir
en este las variables por ? y posteriormente se introducen los datos concretos mediante setters. En el siguiente
código puedes ver como se crean unas PreparedStatement.
PreparedStatement pstBuscarCodigo;
PreparedStatement pstInsertarCuenta;
5. CERRAR LA CONEXIÓN
Después de hacer las consultas que se necesite se debe de cerrar la conexión para liberar los recursos, también se
pueden cerrar el resulset y el statement de forma manual pero cerrando la conexión se cierran los otros dos porque
están creados a partir de la conexión, del mismo modo al cerrar el statement también se liberan los recursos del
resulset.
6. EJEMPLOS
Una vez visto más o menos como funciona vamos a ver unos ejemplo de como usar JDBC.
En este primer ejemplo se crea la conexión y los prepareStatements en el constructor por lo que luego podemos
usarlo sin tener que crear una nueva conexión para insertar un nuevo dato, pero no cerramos nunca la conexión
porque si la cerrásemos como se esta creando en el constructor ya no se podría volver a usar.
Este primer ejemplo consiste en una aplicación que permite almacenar una nueva cuenta en la base de datos siempre
y cuando el código de la cuenta no este ya en la base de datos. En este ejemplo se usan PreparedStatements.
Una de las cosas que podemos ver en el ejemplo y que siempre se deben de hacer es declarar la conexión, los
statements y los resultsets fuera de los bloques try catch, porque puede pasar que como en este ejemplo la conexión
se cree en un sitio y luego se use en otro (lo normal sería tener una clase especifica para crear la conexión con la
base de datos) y aunque no fuese así lo normal es cerrar la conexión en un bloque finally porque aunque en este no
cerramos la conexión lo normal y recomendable es hacerlo, sin ir más lejos en los siguientes ejemplos si se cierra la
conexión en el finally porque la conexión hay que cerrarla tanto si la ejecución a sido correcta como si se produce
alguna excepción.
package ejemplo1;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.*;
import javax.swing.JLabel;
import org.apache.log4j.Logger;
/**
*
* @author Ivan Salas Corrales <http://programandoointentandolo.com/>
*/
public class CreaCuenta implements ActionListener {
public CreaCuenta(Ventana v) {
this.v = v;
try {
// Driver para conectar con MySQL
Class.forName("com.mysql.jdbc.Driver");
// Conexion con la base de datos
cn = DriverManager.getConnection("jdbc:mysql://localhost:3306/cuentas", "root", "");
@Override
public void actionPerformed(ActionEvent e) {
try {
pstBuscarCodigo.setString(1, codigo);
// Si el codigo no esta en la bd se añade la cuenta
ResultSet rs = pstBuscarCodigo.executeQuery();
if (!rs.next()) {
pstInsertarCuenta.setString(1, codigo);
pstInsertarCuenta.setString(2, nombre);
pstInsertarCuenta.setString(3, email);
pstInsertarCuenta.setDouble(4, saldo);
pstInsertarCuenta.executeUpdate();
lbMensaje.setText("Cuenta agregada correctamente");
} else {
lbMensaje.setText("El codigo indicado ya esta en la base de datos");
}
} catch (SQLException ex) {
log.error("Error " + ex.getErrorCode() + ": " + ex.getMessage());
}
}
}
Como supongo que te habrás dado cuenta el código anterior es el de una clase que implementa ActionListener por
lo que como te podrás imaginar que es parte de una aplicación Swing, pero como eso no aporta nada al
entendimiento de JDBC no lo pongo aunque al final puedes descargarte el proyecto con los ejemplos y ver y probar
los ejemplos concretos.
La aplicación del segundo ejemplo sirve para hacer transferencias entre las cuentas. Con este ejemplo vamos a
aprovechar para ver como crear transacciones y deshacerlas si falla alguna de las operaciones. En este ejemplo se
usan statement para ver lo tedioso que puede ser.
Cuando usamos JDBC por defecto cuando hacemos un executeXXX ya sea con una statement o una
preparedstatement la operación se realiza inmediatamente pero hay ocasiones en las que este no será el
comportamiento deseado como es el caso de este ejemplo, porque la operación que global que queremos hacer es
sacar el dinero de una cuenta y meterlo en otra, pero si usamos el funcionamiento estándar si se extrae el dinero de
la primera cuenta y luego hay un error al insertar el dinero en la segunda cuenta el dinero habrá desaparecido y esto
no es un comportamiento aceptable por lo que lo lógico sería que si falla la segunda operación se deshaga la primera
y todo quede como antes de empezar la operación.
package ejemplo2;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Date;
import org.apache.log4j.Logger;
/**
*
* @author Ivan Salas Corrales <http://programandoointentandolo.com/>
*/
public class Transferir implements ActionListener {
public Transferir(Transferencias t) {
this.t = t;
}
@Override
public void actionPerformed(ActionEvent e) {
Connection cn = null;
try {
// Driver para conectar con MySQL
Class.forName("com.mysql.jdbc.Driver");
Statement st = cn.createStatement();
// Ejecuta la transaccion
cn.commit();
t.setLbResultado("Transeferencia completada.");
} else {
// Deshace los cambios hechos dentro de la transaccion
cn.rollback();
t.setLbResultado("Error: La transeferencia no se ha completado.");
}
Para poder controlar cuando se hacen las transacciones para poder englobar las operaciones que nosotros
queramos dentro de la misma transacción lo primero es indicar que no queremos que las operaciones se hagan
automáticamente (cn.setAutoCommit(false)) y luego se ejecutan las operaciones como se hace normalmente y
cuando se han hecho todas se hace el commit o si ha fallado algo se hace un rollback para deshacer las operaciones.
Lo normal es comprobar que se han hecho correctamente las operaciones y entonces hacer el commit o el rollback
si han fallado, pero también se debería de hacer un rollback en el catch porque si se produce alguna excepción será
que algo a fallado por lo que habrá que deshacerlo por si acaso queda la base de datos en algún estado inconsistente
(Que no se realice correctamente un executeXXX no quiere decir que se tenga que lanzar una excepción porque por
ejemplo si se intenta transferir más saldo del disponible no se produce ninguna excepción pero el update
correspondiente no produce ningún cambio en la base de datos por lo que hay que controlarlo).
Y finalmente en ultimo ejemplo vamos a hacer una sencilla aplicación que muestra la lista de operaciones realizadas
sobre una cuenta y para ver algo nuevo los datos de la consulta se obtienen de dos tablas distintas usando un join,
aunque esto es cosa del sql y para la parte java no hay nada distinto y vamos coger los datos del resulset obtenidos
de la consulta para mostrarlos en una tabla.
package ejemplo3;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.*;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableModel;
import org.apache.log4j.Logger;
/**
*
* @author Ivan Salas Corrales <http://programandoointentandolo.com/>
*/
public class MuestraMovimientos implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
String tipo;
String cantidad;
String fecha;
try {
// Driver para conectar con MySQL
Class.forName("com.mysql.jdbc.Driver");
psBusqueda.setString(1, tfCuenta.getText());
ResultSet listaMovimientos = psBusqueda.executeQuery();