Vous êtes sur la page 1sur 9

TUTORIAL JDBC CON APLICACIONES DE EJEMPLO

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.

Para usar JDBC hay que seguir los siguientes pasos:

1. INCLUIR EL JAR CON EL DRIVER DE LA BASE DE


DATOS
El primer paso es obtener el driver de la base de datos que vamos a utilizar, buscamos en google “MySQL jdbc
driver”, “Oracle jdbc driver” o el que queramos y descargamos el jar y lo incluimos es nuestro proyecto. Para este
ejemplo voy a usar MySQL, puedes descargarlo desde aquí, lo que queremos es el .jar por lo que tienes que elegir
Plataform Independent y descargar el zip o el tar.gz, y aunque vienen unos cuantos archivos y carpetas el único que
nos interesa es mysql-connector-java-5.1.26-bin.jar que lo tenemos que añadir a nuestro proyecto.
2. CARGAR EL DRIVER
Ya tenemos el jar con el driver, pero hay que cargarlo para que se pueda hacer uso de el en nuestra aplicación. En
nuestro caso como vamos a usar MySQL la instrucción es la siguiente y como puede lanzar una excepción pues se
mete dentro de un try-catch.

try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException ex) {
log.error("No se encontro el Driver MySQL para JDBC.");
}

3.ESTABLECER UNA CONEXIÓN CON LA BASE DE DATOS


El siguiente paso es establecer una conexión con la base de datos. Hay dos opciones para obtener una conexión la
primera es crear una nueva conexión con la base de datos que será la que veremos en este ejemplo y la otra es
obtener la conexión de un pool de conexiones creado previamente que es una opción más eficiente porque no hace
falta estar abriendo y cerrando conexiones continuamente ya que simplemente se cogen las conexiones de pool
cuando se necesitan y se dejan en el pool cuando ya no se necesitan para que puedan volver a usarse. En un articulo
previo, Como crear un pool de conexiones en Tomcat, puedes ver un ejemplo de como usar un pool de conexiones
con dicho servidor pero para un ejemplo de una aplicación de escritorio como la que vamos a ver supuestamente
vamos a tener pocas llamadas a base de datos por lo que no se va a notar mucha diferencia.

//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.

Vamos a ver como hacer una consulta de tipo Statement:

// Creamos el Statement para poder hacer consultas


Statement st = cn.createStatement();

// La consulta es un String con código SQL


String sql1 = "SELECT * FROM cuentas";

// Ejecuta una consulta que devuelve resultados


ResultSet rs = st.executeQuery(sql1);
while (rs.next()) {
System.out.println (rs.getString ("propietario") + " " + rs.getString (2)+ " " +
rs.getInt(saldo));
}

String sql2 = "UPDATE cuentas SET saldo = saldo - "


+ cantidad + " WHERE codigo = '"
+ codigo + "' AND saldo >= "
+ cantidad;

// Ejecuta una consulta de tipo insert, update o delete


st.executeUpdate(sql2);

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;

String sqlBusqueda = "SELECT codigo FROM cuentas WHERE codigo=?";


pstBuscarCodigo = cn.prepareStatement(sqlBusqueda);
pstBuscarCodigo.setString(1, codigo);
ResultSet rs = pstBuscarCodigo.executeQuery();
if (!rs.next){...}

String sqlNuevaCuenta = "INSERT INTO cuentas VALUES (?,?,?,?)";


pstInsertarCuenta = cn.prepareStatement(sqlNuevaCuenta);
pstInsertarCuenta.setString(1, codigo);
pstInsertarCuenta.setString(2, nombre);
pstInsertarCuenta.setString(3, email);
pstInsertarCuenta.setDouble(4, saldo);
pstInsertarCuenta.executeUpdate();

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.

rs.close(); // Cierra el resulset


st.close(); // Cierra el statement
cn.close(); // Cierra la conexión

6. EJEMPLOS
Una vez visto más o menos como funciona vamos a ver unos ejemplo de como usar JDBC.

La base de datos para los ejemplos será la siguiente:

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 {

private Logger log = Logger.getLogger(CreaCuenta.class);


private Ventana v;
private Connection cn;
private PreparedStatement pstBuscarCodigo;
private PreparedStatement pstInsertarCuenta;

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", "");

String sqlBusqueda = "SELECT codigo FROM cuentas WHERE codigo=?";


pstBuscarCodigo = cn.prepareStatement(sqlBusqueda);

String sqlNuevaCuenta = "INSERT INTO cuentas VALUES (?,?,?,?)";


pstInsertarCuenta = cn.prepareStatement(sqlNuevaCuenta);

} catch (SQLException ex) {


log.error("Error " + ex.getErrorCode() + ": " + ex.getMessage());
} catch (ClassNotFoundException ex) {
log.error("No se encontro el Driver MySQL para JDBC.");
}
}

@Override
public void actionPerformed(ActionEvent e) {

String codigo = v.getTextCodigo().getText();


String nombre = v.getTextNombre().getText();
String email = v.getTextEmail().getText();
double saldo = (double) v.getTextSaldo().getValue();
JLabel lbMensaje = v.getLbMensaje();

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 {

private Logger log = Logger.getLogger(Transferir.class);


private Transferencias t;

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");

cn = DriverManager.getConnection("jdbc:mysql://localhost:3306/cuentas", "root", "");

// Obtenemos la fecha actual


Date date = new Date();
Date fecha = new java.sql.Date(date.getTime());

String sqlCuenta1 = "UPDATE cuentas SET saldo = saldo - "


+ t.getTfCantidad().getValue() + " WHERE codigo = '" +
t.getTfCuenta1().getText()
+ "' AND saldo >= " + t.getTfCantidad().getValue();

String sqlMovimiento1 = "INSERT INTO movimientos SET codigo = '"


+ t.getTfCuenta1().getText() + "', tipo = '1', cantidad= 0-"
+ t.getTfCantidad().getValue() + ", fecha= '" + fecha + "'";

String sqlCuenta2 = "UPDATE cuentas SET saldo = saldo + "


+ t.getTfCantidad().getValue() + " WHERE codigo = '" +
t.getTfCuenta2().getText() + "'";

String sqlMovimiento2 = "INSERT INTO movimientos SET codigo = '"


+ t.getTfCuenta2().getText() + "', tipo = '1', cantidad= "
+ t.getTfCantidad().getValue() + ", fecha= '" + fecha + "'";

Statement st = cn.createStatement();

// Indica que las operaciones no se validan automaticamente


cn.setAutoCommit(false);

// Si se hacen ambas actualizaciones se hace la transaccion y sino se deshace


if ((st.executeUpdate(sqlCuenta1) != 0) && (st.executeUpdate(sqlCuenta2) != 0)
&&(st.executeUpdate(sqlMovimiento1) != 0) && (st.executeUpdate(sqlMovimiento2) !=
0)) {

// 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.");
}

} catch (SQLException ex) {


log.error("Error " + ex.getErrorCode() + ": " + ex.getMessage());
try {
// Deshace los cambios hechos dentro de la transaccion
cn.rollback();
t.setLbResultado("Error: La transeferencia no se ha completado.");
} catch (SQLException ex1) {
log.error("Error " + ex1.getErrorCode() + ": " + ex1.getMessage());
}
} catch (ClassNotFoundException ex) {
log.error("No se encontro el Driver MySQL para JDBC.");
} finally {
try {
cn.close();
} catch (SQLException ex) {
log.error("Error " + ex.getErrorCode() + ": " + ex.getMessage());
}
}
}
}

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 {

private Logger log = Logger.getLogger(MuestraMovimientos.class);


private DefaultTableModel dtm;
private Connection cn;
private JTextField tfCuenta;

public MuestraMovimientos(DefaultTableModel dtm, JTextField tfCuenta) {


this.dtm = dtm;
this.tfCuenta = tfCuenta;
}

@Override
public void actionPerformed(ActionEvent e) {
String tipo;
String cantidad;
String fecha;

// Se borra el contenido de la tabla


int i = 0;
while (dtm.getRowCount() > 0) {
dtm.removeRow(i);
}

try {
// Driver para conectar con MySQL
Class.forName("com.mysql.jdbc.Driver");

cn = DriverManager.getConnection("jdbc:mysql://localhost:3306/cuentas", "root", "");

String sqlBusqueda = "SELECT tm.tipo, m.cantidad, m.fecha FROM movimientos AS m INNER


JOIN tipos_movimiento AS tm ON tm.id=m.tipo WHERE m.codigo=?";

PreparedStatement psBusqueda = cn.prepareStatement(sqlBusqueda);

psBusqueda.setString(1, tfCuenta.getText());
ResultSet listaMovimientos = psBusqueda.executeQuery();

// Se ponene en la tabla los valores obtenidos de la consulta


while (listaMovimientos.next()) {
tipo = listaMovimientos.getString("tipo");
cantidad = listaMovimientos.getString("cantidad");
fecha = listaMovimientos.getString("fecha");
dtm.addRow(new String[]{tipo, cantidad, fecha});
}

// Cierro el ResultSet y el Statement aunque al cerrar el Statement ya se cierra el


ResulSet
listaMovimientos.close();
psBusqueda.close();

} catch (SQLException ex) {


log.error("Error " + ex.getErrorCode() + ": " + ex.getMessage());
} catch (ClassNotFoundException ex) {
log.error("No se encontro el Driver MySQL para JDBC.");
} finally {
try {
// Cierra la conexion con la base de datos
cn.close();
} catch (SQLException ex) {
log.error("Error " + ex.getErrorCode() + ": " + ex.getMessage());
}
}
}
}

Vous aimerez peut-être aussi