2. Conexion y uso de bases de datos en lenguaje Java
2. Conexion y uso de bases de datos en lenguaje Java Dataprix 6 November, 2009 - 16:15El acceso a bases de datos desde Java se realiza mediante el estándar JDBC (Java data base conectivity), que permite un acceso uniforme a las bases de datos independientemente del SGBD. De esta manera, las aplicaciones escritas en Java no necesitan conocer las especificaciones de un SGBD en particular, basta con comprender el funcionamiento de JDBC. Cada SGBD que quiera utilizarse con JDBC debe contar con un adaptador o controlador.
La estructura de JDBC se puede expresar gráficamente como sigue:
Hay drivers para la mayoría de SGBD, tanto de software libre como de código abierto. Además, hay drivers para trabajar con otros tipos de datos (hojas de cálculo, ficheros de texto, etc.) como si fueran SGBD sobre los que podemos realizar consultas SQL.
Para usar la API JDBC con un SGBD en particular, necesitaremos el driver concreto del motor de base de datos, que media entre la tecnología JDBC y la base de datos. Dependiendo de múltiples factores, el driver puede estar escrito completamente en Java, o bien haber usado métodos JNI (Java native interface) para interactuar con otros lenguajes o sistemas.
La última versión de desarrollo de la API JDBC proporciona también un puente para conectarse a SGBD que dispongan de drivers ODBC (open database connectivity). Este estándar es muy común sobre todo en entornos Microsoft y sólo debería usarse si no disponemos del driver nativo para nuestro SGBD.
En el caso concreto de MySQL y PostgreSQL, no tendremos ningún problema en encontrar los drivers JDBC:
• MySQL Connector/J: es el driver oficial para MySQL y se distribuye bajo licencia GPL. Es un driver nativo escrito completamente en Java.
• JDBC para PostgreSQL: es el driver oficial para PostgreSQL y se distribuye bajo licencia BSD. Es un driver nativo escrito completamente en Java.
Tanto uno como otro, en su distribución en formato binario, consisten en un fichero .jar (Java archive) que debemos situar en el CLASSPATH de nuestro programa para poder incluir sus clases.
Java incluye la posibilidad de cargar clases de forma dinámica. Éste es el caso de los controladores de bases de datos: antes de realizar cualquier interacción con las clases de JDBC, es preciso registrar el controlador. Esta tarea se realiza con el siguiente código:
String controlador = "com.mysql.jdbc.Driver" Class.forName(controlador).newInstance();
o bien:
Class.forName("org.postgresql.Driver");
A partir de este momento, JDBC está capacitado para interactuar con MySQL o PostgreSQL.
2.1. Acceder al SGBD con JDBC
2.1. Acceder al SGBD con JDBC Dataprix 9 November, 2009 - 10:46La interfaz JDBC está definida en la librería java.sql. Vamos a importar a nuestra aplicación Java todas las clases definidas en ella.
import java.sql.*;
Puesto que JDBC puede realizar conexiones con múltiples SGDB, la clase DriverManager configura los detalles de la interacción con cada uno en particular. Esta clase es la responsable de realizar la conexión, entregando un objeto de la clase Connection.
String url="jdbc:mysql://localhost/demo";
String usuario="yo"
String contasenia="contraseña"
Connection conexion = DriverManager.getConnection (url,usuario,contrasenia);
El destino de la conexión se especifica mediante un URL de JDBC con la sintaxis siguiente:
jdbc::
La parte protocolo_sgdb de la URL especifica el tipo de SGBD con el que se realizará la conexión, la clase DriverManager cargará el módulo correspondiente a tal efecto.
El subnombre tiene una sintaxis específica para cada SGDB que tanto para MySQL como para PostgreSQL es //servidor/base_de_datos.
Las sentencias en JDBC también son objetos que deberemos crear a partir de una conexión:
Statement sentenciaSQL = conexion.createStatement();
La variable res contiene el resultado de la ejecución de la sentencia, y proporciona un cursor que permite leer las filas una a una. |
Al ejecutar una sentencia, el SGBD entrega unos resultados que JDBC también representa en forma de objeto, en este caso de la clase ResultSet:
ResultSet res = sentencia.executeQuery("SELECT * FROM tabla");
Para acceder a los datos de cada columna de la hoja de resultados, la clase ResultSet dispone de varios métodos según el tipo de la información de la columna:
getArray() getInt()
getClob() getBoolean()
getString() getLong()
getAsciiStream() getByte()
getDate() getObject()
getTime() getObject()
getBigDecimal() getBytes()
getDouble() getBytes()
getTimestamp() getRef()
getBinaryStream() getRef()
getFloat() getCraracterStream()
getURL() &
import java.sql.*;
// Atención, no debe importarse com.mysql.jdbc ya que se carga dinámicamente!!
public static void main(String[] args) {
// Cargamos el driver JDBC para MySQL
String controlador = "com.mysql.jdbc.Driver"
Class.forName(controlador).newInstance();
// Conectamos con la BD
String url="jdbc:mysql://localhost/uoc";
String usuario="yo"
String contasenia="contraseña"
Connection conexion = DriverManager.getConnection (url,usuario,contrasenia);
// Creamos una sentencia SQL
Statement sentenciaSQL = conexion.createStatement();
// Ejecutamos la sentencia
ResultSet res = sentencia.executeQuery("SELECT * FROM tabla");
// Iteramos sobre la hoja de resultados
while (res.next()) {
// Obtenemos el campo "nombre" en forma de String
System.out.println(res.getString("nombre") );
}
// Finalmente, se liberan los recursos utilizados.
res.close();
sentencia.close();
conexion.close();
}
En el ejemplo anterior no se ha previsto nada para tratar con los errores que puedan producirse, porque en Java el tratamiento de errores se realiza a través de Exceptions. En JDBC se han previsto excepciones para los errores que pueden producirse a lo largo de todo el uso de la API: conexión, ejecución de la sentencia, etc.
Revisemos el ejemplo, utilizando excepciones para tratar los errores.
import java.sql.*;
// Atención, no debe importarse com.mysql.jdbc ya que se carga
// dinámicamente!!
public static void main(String[] args) {
try {
// Cargamos el driver JDBC para MySQL
String controlador = "com.mysql.jdbc.Driver"
Class.forName(controlador).newInstance();
} catch (Exception e) {
System.err.println("No puedo cargar el controlador de MySQL ...");
e.printStackTrace();
}
try {
// Conectamos con la BD
String url="jdbc:mysql://localhost/uoc";
String usuario="yo"
String contasenia="contraseña"
Connection conexion = DriverManager.getConnection (url,usuario,contrasenia);
// Creamos una sentencia SQL
Statement sentenciaSQL = conexion.createStatement();
// Ejecutamos la sentencia
ResultSet res = sentencia.executeQuery("SELECT * FROM tabla");
// Iteramos sobre la hoja de resultados
while (res.next()) {
// Obtenemos el campo "nombre" en forma de String
System.out.println(res.getString("nombre") );
}
// Finalmente, se liberan los recursos utilizados. res.close();
sentencia.close();
conexion.close();
} catch (SQLException e) {
System.out.println("Excepción del SQL: " + e.getMessage());
System.out.println("Estado del SQL: " + e.getSQLState());
System.out.println("Error del Proveedor: " + e.getErrorCode());
}
}
Errores |
Todos los errores de JDBC |
Mientras que la operación executeQuery() de la clase Statement devuelve un objeto ResultSet, la operación executeUpdate()sólo devuelve su éxito o fracaso. Las sentencias SQL que se utilizan con executeUpdate() son insert, update, o delete, porque no devuelven ningún resultado.
public void Insertar_persona(String nombre, direccion,
telefono){ Statement sentencia = conexion.CreateStatement();
sentencia.executeUpdate( "insert into personas values("
+ nombre + ","
+ domicilio + ","
+ telefono + ")" );
}
2.2. Sentencias preparadas
2.2. Sentencias preparadas Dataprix 17 November, 2009 - 10:39Las sentencias preparadas de JDBC permiten la “precompilación” del código SQL antes de ser ejecutado, permitiendo consultas o actualizaciones más eficientes. En el momento de compilar la sentencia SQL, se analiza cuál es la estrategia adecuada según las tablas, las columnas, los índices y las condiciones de búsqueda implicados. Este proceso, obviamente, consume tiempo de procesador, pero al realizar la compilación una sola vez, se logra mejorar el rendimiento en siguientes consultas iguales con valores diferentes.
Otra ventaja de las sentencias preparadas es que permiten la parametrización: la sentencia SQL se escribe una vez, indicando las posiciones de los datos que van a cambiar y, cada vez que se utilice, le proporcionaremos los argumentos necesarios que serán sustituidos en los lugares correspondientes. Los parámetros se especifican con el carácter ‘?’.
public class Actualizacion{
private PreparedStatement sentencia;
public void prepararInsercion(){
String sql = "insert into personas values ( ?, ? ,? )";
sentencia = conexion.prepareStatement(sql);
}
public void insertarPersona(String nombre, dirección, telefono)
{
sentencia.setString(1, nombre);
sentencia.setString(2, direccion);
sentencia.setString(3, telefono);
sentencia.executeUpdate();
}
}
Al utilizar esta clase, obviamente, deberemos llamar primero al método que prepara la inserción, y posteriormente llamar tantas veces como sea necesario al método insertarPersona.
Se definen tres parámetros en la sentencia SQL, a los cuales se hace referencia mediante números enteros consecutivos:
String sql = "insert into personas values ( ?, ? ,? )";
Ejemplo | ||
|
La clase PreparedStatement incluye un conjunto de operaciones de la forma setXXXX(), donde XXXX es el tipo de dato para los campos de la tabla. Una de esas operaciones es precisamente setString() que inserta la variable en un campo de tipo cadena.
2.3. Transacciones
2.3. Transacciones Carlos 25 November, 2009 - 10:38La API JDBC incluye soporte para transacciones, de forma que se pueda deshacer un conjunto de operaciones relacionadas en caso necesario. Este compor- tamiento es responsabilidad de la clase Connection.
Por omisión, cada sentencia se ejecuta en el momento en que se solicita y no se puede deshacer. Podemos cambiar este comportamiento con la operación siguiente:
conexion.setAutoCommit(false);
Después de esta operación, es necesario llamar a commit() para que todas las sentencias SQL pendientes se hagan definitivas:
sentencia.executeUpdate(...);
...
sentencia.executeUpdate(...);
...
conexion.commit(); // Se hacen permanentes las dos actualizaciones anteriores
En caso contrario, desharemos todas las actualizaciones después del último commit():
sentencia.executeUpdate(...);
...
sentencia.executeUpdate(...);
...
sentencia.executeUpdate(...);
...
conexion.rollback(); // Cancela las tres últimas actualizaciones