Acceso A Datos - Tema 2
Acceso A Datos - Tema 2
Acceso A Datos - Tema 2
Existen distintas formas de conectar a gestores de bases de datos, como por ejemplo:
ODBC es poco adecuado para ser usando desde Java, porque se basa en llamadas en
lenguaje C (lo que implica uso de punteros, que no existen en Java), y la conversin
puede suponer riesgos de seguridad y de falta robustez. Por eso, en Java se suele
emplear una alternativa totalmente basada en objetos, llamada JDBC (Java Database
Connectivity).
En la mayora de casos, se deber instalar un driver que permita acceder desde el lenguaje o
herramienta que se est utilizando al gestor de bases de datos deseados. En el mundo Java es
habitual llamar tambin conector a estos drivers.
Para alguna base de datos minoritaria en la que no existiera un driver JDBC pero s uno ODBC,
sera posible emplear un puente ODBC-JDBC (JDBC-to-ODBC bridge).
(Nota: Si empleas VirtualBox o algn otro sistema de virtualizacin para tus pruebas con
PostgreSQL, es recomendable que no te limites al disco duro virtual de 8 GB que posiblemente
te propondr VirtualBox, o se quedar al lmite de su capacidad, lo que puede suponer
problemas inesperados de ejecucin; es preferible reservar al menos 9 o 10 GB).
http://www.postgresql.org/download
Pero desde esa misma pgina se puede descargar tambin el instalador interactivo:
En el momento de escribir este texto, la ltima versin estable es la 9.5.3.2, siendo todava
beta la 9.6, por lo que sera razonable escoger la 9.5.
chmod +x postgresql-9.5.3-2-linux-x64-run
sudo su
./postgresql-9.5.3-2-linux-x64-run
Que en primer lugar nos preguntar el directorio de instalacin (y que podemos dejar con su
valor por defecto);
Y, dado que se trata de una arquitectura cliente-servidor, deberemos elegir un puerto a travs
del que se establecer esa comunicacin (se nos propone el 5432, y es razonable dejarlo as);
Y comenzar la instalacin en s.
Y al final se nos preguntar si queremos lanzar Stack Builder para instalar herramientas
adicionales. Ya que vamos a conectar a PostgreSQL desde Java, ser razonable decir que s.
Y luego indicaremos los extras que deseamos, y que en principio sern slo un driver de
base de datos, el que usa el estndar JDBC:
Y comenzar la instalacin en s:
Se nos preguntar a qu servidor conectar (se nos propone localhost, que es el correcto, al
estar trabajando desde el propio equipo que acta de servidor), base de datos (postgres es
la nica que existe por ahora), puerto (5432 es el habitual, si no lo hemos cambiado), usuario
(por ahora slo existe el administrador, postgres) y contrasea (la que hayamos escogido
para el administrador).
Vamos a crear una nueva base de datos muy sencilla, que llamaremos datos1, para hacer las
primeras pruebas:
Al entrar, nos aparecer un servidor de PostgreSQL al que podremos conectar usando el botn
derecho del ratn:
Y se nos mostrar que en ese servidor hay dos bases de datos: la que crea el propio sistema
(postgres) y la que hemos creado nosotros desde psql (datos1).
Tras hacer clic en nuestra base de datos, podremos pulsar el botn SQL para dar rdenes en
este lenguaje:
Y podramos usar tambin la orden SELECT para comprobar los datos existentes:
En la barra de herramientas, junto al botn SQL, tendremos otro que permitir ver los datos en
el objeto escogido:
Eso s, como es una primera tabla tan sencilla que no tiene ni siquiera una clave primaria se
nos mostrar un aviso de que podremos ver los datos pero no editarlos:
Ejercicios propuestos
2.2.1.1: Elige otra distribucin de Linux ms amigable (por ejemplo, Linux Mint) e instala
PostgreSQL en ella.
2.2.1.2: Crea una base de datos llamada pruebas1. Crea en ella una tabla programas para
guardar una coleccin de software. En una primera aproximacin guardaremos slo tres
campos: un cdigo, un nombre y el nombre del dispositivo de copia de seguridad en el que
est el respaldo de ese programa. Introduce dos datos de ejemplo y comprueba que se han
almacenado correctamente.
La nica diferencia es que PostgreSQL, psql y pgAdmin estarn todos accesibles fcilmente
desde el men de Inicio.
2.2.2.1: Instala PostgreSQL en Windows, ya sea en mquina real o en mquina virtual (hacerlo
en mquina virtual har que el manejo de PostgreSQL sea ms lento pero que no lo sea el
equipo anfitrin cuando no ests empleando PostgreSQL, por lo que puede ser una opcin ms
recomendable en un ordenador moderno).
2.2.2.2: Crea una base de datos llamada pruebas1. Crea en ella una tabla programas para
guardar una coleccin de software. En una primera aproximacin guardaremos slo tres
campos: un cdigo, un nombre y el nombre del dispositivo de copia de seguridad en el que
est el respaldo de ese programa. Introduce dos datos de ejemplo y comprueba que se han
almacenado correctamente.
2.2.3.1: Crea una base de datos llamada BDTareas, que usaremos para anotar tareas. En ella
en principio habr una nica tabla llamada tareas, con dos campos: el cdigo de la tarea
(numrico, que ser clave primaria) y la descripcin de la tarea. Aade dos tareas: la 1 ser
Repasar acceso a datos y la 2 ser Instalar NetBeans.
2.2.3.2: Modifica la tabla para aadir dos campos (al final de los ya existentes): uno ser la
fecha prevista para la tarea y otro ser si est terminada o no.
2.2.3.3: Modifica la primera tarea para que tenga fecha de maana y no est terminada, y la
segunda para que tenga fecha de pasado maana y tampoco est terminada. Aade dos tareas
ms.
2.2.3.8: Muestra el cdigo de la tarea ms antigua (menor cdigo) que contenga la palabra
NetBeans.
2.2.3.9: Muestra la descripcin y fecha de las tareas cuyo cdigo es inferior al cdigo de la
tarea ms antigua (menor cdigo) que contenga la palabra NetBeans.
2.2.3.10: Crea una nueva tabla categorias. Cada categora tendr dos campos: un cdigo
(texto, clave primaria) y la descripcin. Aade la categora E, Estudios y la categora O,
2.2.3.11: Muestra la descripcin de todas las tareas, junto con la descripcin de la categora a
la que pertenecen, para todas las categoras de las que se haya indicado descripcin.
2.2.3.12: Muestra la descripcin de todas las tareas, junto con la descripcin de la categora a
la que pertenecen o NULL en caso de que no se haya indicado esa categora.
2.2.3.13: Muestra la descripcin de todas las tareas, junto con la descripcin de la categora a
la que pertenecen o (Ninguna) en caso de que no se haya indicado esa categora.
2.2.3.13: Muestra la descripcin de todas las tareas, junto con la descripcin de la categora a
la que pertenecen, para todas las tareas (puede que alguna categora aparezca como NULL) y
para todas las categoras (y puede que alguna tarea aparezca como NULL, si esa categora no
tiene tareas asignadas).
Ya como aplicacin real, los pasos necesarios para conectar y obtener un bloque de
informacin son los siguientes:
Usar Class.forName para cargar el driver que se haya escogido. En nuestro caso ser:
Class.forName("org.postgresql.Driver");
Establecer una conexin, indicando la URL del servidor, el usuario que conecta y la
contrasea. Ser algo como:
Si cada fila del resultado est formada por varios campos, podemos obtener el valor de
cada uno de ellos con rs.getString( x ), donde x puede ser un nmero (la posicin de
esa columna en la consulta, comenzando con 1) o un nombre de campo (indiferente
de maysculas o minsculas):
rs.close();
con.close();
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
while (rs.next()) {
System.out.println(rs.getString(1) + "\t " + rs.getString(2));
}
rs.close();
con.close();
}
}
(En un caso real, es habitual que en vez de localhost se indique la direccin IP del servidor,
que no tiene por qu coincidir con el cliente, por ejemplo 192.168.1.2).
Nombre E-mail
-----------------------------------------
Persona Dos pers2@correo.com
Persona Uno pers1@correo.com
Desde NetBeans tambin se podr crear con facilidad proyectos que accedan a bases de datos.
Podramos descargar NetBeans desde su pgina oficial, o bien usar el Centro de Software
Ubuntu:
Incluso en una distribucin relativamente poco amigable para usuarios avanzados como es el
Ubuntu bsico, es fcil que podamos instalar NetBeans en un par de clics:
Y entonces comenzar la descarga en segundo plano, que podremos notar por la barra de
progreso que mostrar el icono de NetBeans en el panel izquierdo de la pantalla:
Un fuente como el anterior debera no mostrar errores en el momento de teclear (salvo quiz
la necesidad de incluirlo dentro de un package):
La forma de aadir una biblioteca es pulsar el botn derecho sobre el proyecto para acceder a
sus Propiedades (Properties):
Si no fuera el caso, o como alternativa, se puede pulsar el botn Add JAR/folder en vez de
Add library, para no aadir desde una biblioteca del sistema, sino desde un fichero
descargado:
En el caso de Windows, basta con descargar NetBeans (en su versin para JavaSE) desde su
pgina oficial, instalarlo. La forma de crear un proyecto y de aadir bibliotecas es la misma que
en Linux.
Ejercicios propuestos
2.3.1: Comprueba desde Java que los dos datos que has introducido en el apartado 2.2.2 son
accesibles.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
con.close();
}
}
Ejercicios propuestos
2.4.1: Crea desde Java una nueva tabla dentro de la base de datos prueba1, que permita
guardar los datos sobre cada dispositivo de copia de seguridad: cdigo, nombre y situacin.
2.4.2: Desde Java, aade a la tabla de programas un nuevo campo, que indicar su tamao,
medido en KB.
import java.sql.Connection;
import java.sql.DriverManager;
Datos insertados: 2
Y si ahora volvemos a lanzar el programa, que mostraba todos los datos, deberan mostrase 4:
Nombre E-mail
-----------------------------------------
Persona Cuatro pers4@correo.com
Persona Dos pers2@correo.com
Persona Tres pers3@correo.com
Persona Uno pers1@correo.com
(La otra orden habitual que forma parte del DML es SELECT, que, como ya sabemos, s
devuelve en general un conjunto de datos, por lo que se lanza con .executeQuery).
Ejercicios propuestos
2.5.1: Crea desde Java un programa que aada tres filas en la tabla de dispositivos de copia de
seguridad. Comprueba el valor devuelto, para asegurarte de que todas se han insertado
correctamente.
A este tipo de funciones se les suele llamar tambin procedimientos almacenados (Stored
Procedures) y se pueden llamar luego desde otros lenguajes en el equipo cliente. De hecho, es
una de las formas de trabajar que se suelen recomendar para evitar ataques de inyeccin de
SQL.
Como ejemplo de funcin que reciba un parmetro y que devuelva un valor, podemos crear
una que diga la cantidad de datos que hay en la tabla personas cuyo nombre sigue un cierto
patrn:
select CantidadPersonas('Per%');
cantidadpersonas
4
select CantidadPersonas('N%');
la respuesta sera
cantidadpersonas
0
Ahora es posible crear un programa en Java que llame a esa funcin, que le pase parmetros y
que obtenga sus resultados. Para ello, emplearemos un CallableStatement:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.CallableStatement;
import java.sql.Types;
Class.forName("org.postgresql.Driver");
cStmt.execute();
int resultado = cStmt.getInt(1);
System.out.println("Resultado: " + resultado);
con.close();
}
}
Class.forName("org.postgresql.Driver");
Statement s = c.createStatement();
String consulta = "select CantidadTotalPersonas()";
ResultSet rs = s.executeQuery(consulta);
rs.next();
System.out.println("Resultado: " + rs.getInt(1));
c.close();
}
}
De hecho, esta funcin es tan sencilla que no necesita declarar variables ni usar ninguna
caracterstica de PL/pgSQL, por lo que se podra usar SQL simple (aunque en eso caso, se nos
avisara de que el resultado no debe ser un INTEGER sino un BIGINT) y el delimitador no
necesitara ser algo que comience y acabe con $, sino que bastara con una comilla simple:
Si una funcin tiene que devolver dos o ms valores, una primera solucin (antinatural) puede
ser hacerlo en forma de vector (array), un tipo de datos que veremos con ms detalle en el
siguiente tema, pero esto hara que acceder a esos datos no fuera tan sencillo:
Si hubiera que realizar operaciones sobre cada datos, podra ser ms interesante usar
PL/pgSQL en vez de SQL, as:
Se podra acceder desde SQL de igual forma que en la funcin anterior, mientras que un
programa Java que recorriese el conjunto resultado y lo mostrase no podra usar
CallableStatement, sino PreparedStatment, que es una clase de ms bajo nivel, ms
dependiente del gestor de base de datos concreto, pero a cambio s permite devolver un
conjunto de datos (con el inconveniente adicional de que los datos se reciben como un nico
string):
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.PreparedStatement;
import java.sql.Types;
Class.forName("org.postgresql.Driver");
pStmt.setString(1, "%t%");
ResultSet rs = pStmt.executeQuery();
while (rs.next()) {
System.out.println("Detalles: " + rs.getString(1));
}
rs.close();
con.close();
}
}
Ejercicios propuestos:
2.6.1: Crea una funcin en PL/pgSQL, que dado el cdigo de un dispositivo de copia de
seguridad, devuelva su nombre.
2.6.2: Crea un programa en Java que pida al usuario el cdigo de un dispositivo de copia de
seguridad y muestre su nombre, usando la funcin anterior.
2.6.3: Crea una funcin en PL/pgSQL, que dada una ubicacin, devuelva la cantidad de
dispositivos de copia de seguridad que hay almacenados en ella.
2.6.4: Crea un programa en Java que lea los nombres de todas las ubicaciones, los almacene en
una lista y luego compruebe la cantidad de dispositivos que hay almacenados en cada una,
usando la funcin anterior, para poder mostrar aquellas ubicaciones que no estn vacas.
Una alternativa un poco ms amigable es usar un bloque try-catch para atrapar el error,
informar al usuario y permitir que el programa prosiga a pesar del problema.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
try {
Class.forName("org.postgresql.Driver");
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
try {
Class.forName("org.postgresql.Driver");
De hecho, incluso una SQLException es algo demasiado genrico, de modo que en un caso
real se podran analizar los valores de getMessage() y de getErrorCode() para saber el tipo
de error concreto que ha existido (en general, esto puede ser preferible a usar varios bloques
try-catch en un fragmento de cdigo tan pequeo, para separar la lgica de programa de la
lgica de la gestin de errores, que es lo que pretende el uso de try-catch.
2.7.1: Crea un programa que pida al usuario datos de programas y los aada a la base de datos.
En caso de que se introduzca un cdigo repetido, el programa debe comportarse de forma
amigable: no interrumpir la ejecucin, sino pedir un nuevo cdigo y guardar los datos
(supondremos que el usuario sabe qu datos va a introducir pero se est inventando los
cdigos, de modo que el problema no se tratar de que ha tratado de volver a introducir una
ficha que ya estaba, sino que es una ficha nueva, en la que por error ha reutilizado un cdigo
que ya haba empleado, pero el resto de datos s son correctos).
A nivel de tamao del software a probar, tambin se suele distinguir entre pruebas de
unidad (por ejemplo, para una clase concreta), de integracin (cuando se unen varias clases
para dar lugar a un programa) y de sistema (para cuando todo el conjunto del software ya est
en funcionamiento). Tambin se suele hablar de pruebas de aceptacin del usuario, cuando
comprueba que el software cumple con todos los requisitos que se haban especificado.
Partiremos de una clase cuyo planteamiento no es correcto. Ser una clase Comparador, con
un nico mtodo MayorDe3, que devuelva el mayor de tres nmeros enteros que se le
pasen como parmetros:
class Comparador {
public static int mayorDe3(int a, int b, int c) {
int resultado = a;
if ((b > a) && (b > c)) resultado = b;
if ((c > a) && (c > b)) resultado = c;
return resultado;
}
}
Con JUnit, cada prueba ser tpicamente una funcin void, precedida por @Test y con una
llamada a assertEquals que permita verificar si el resultado de una cierta llamada a una
funcin es igual a un cierto valor:
En el caso de que utilicemos NetBeans, el primer paso ser crear la clase que queremos
probar. Despus se deber aadir al proyecto la biblioteca JUnit:
Se nos propondr crear una clase con el mismo nombre que la nuestra, pero terminado en
Test. Se nos propondr generar una serie de cdigo para nosotros. Al tratarse de una nica
clase, la propia documentacin de NetBeans dice que se puede desmarcar las opciones de
generar un Test initializer y un Test finalizer:
package comparador;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
public ComparadorTest() {
}
@BeforeClass
public static void setUpClass() {
}
@AfterClass
public static void tearDownClass() {
}
/**
* Test of main method, of class Comparador.
*/
@Test
public void testMain() {
System.out.println("main");
String[] args = null;
Comparador.main(args);
/**
* Test of mayorDe3 method, of class Comparador.
*/
@Test
public void testMayorDe3() {
System.out.println("mayorDe3");
int a = 0;
int b = 0;
int c = 0;
int expResult = 0;
int result = Comparador.mayorDe3(a, b, c);
assertEquals(expResult, result);
// TODO review the generated test code and remove the default call to
fail.
fail("The test case is a prototype.");
}
En nuestro caso, que no existe un main que probar, podemos eliminar el mtodo
testMain. Adems, en testMayorDe3 podemos dar un poco ms de sentido al cuerpo del
mtodo, dando valores a a, b, y c, as como el resultado esperado a expResult, y
eliminar la orden fail de ejemplo que apareca al final para recordarnos que por ahora slo se
trataba de un prototipo:
/**
* Test of mayorDe3 method, of class Comparador.
*/
@Test
public void testMayorDe3() {
System.out.println("mayorDe3");
int a = 3;
int b = 2;
int c = 1;
int expResult = 3;
int result = Comparador.mayorDe3(a, b, c);
assertEquals(expResult, result);
}
Para lanzar las pruebas, bastar ir al men Run y escoger la opcin Test Project:
Si todo va bien, la ventana inferior derecha de la pantalla mostrar que los tests se han pasado
correctamente:
En un caso general, no bastar con un nico test, sino que deberemos preparar bastantes. Por
ejemplo, para esta funcin mayorDe3 se podra hacer una batera como:
@Test
public void testMayorDe3a() {
System.out.println("Decreciente");
assertEquals(Comparador.mayorDe3(3, 2, 1), 3);
}
@Test
public void testMayorDe3b() {
System.out.println("Creciente");
assertEquals(Comparador.mayorDe3(1, 2, 3), 3);
}
@Test
public void testMayorDe3c() {
System.out.println("MayorCentro");
assertEquals(Comparador.mayorDe3(2, 3, 1), 3);
@Test
public void testMayorDe3d() {
System.out.println("Negativos");
assertEquals(Comparador.mayorDe3(-1, -2, -3), -1);
}
@Test
public void testMayorDe3e() {
System.out.println("Dos mayores, uno menor");
assertEquals(Comparador.mayorDe3(3, 3, 0), 3);
}
@Test
public void testMayorDe3f() {
System.out.println("Uno menor, dos mayores");
assertEquals(Comparador.mayorDe3(0, 3, 3), 3);
}
@Test
public void testMayorDe3g() {
System.out.println("Tres iguales");
assertEquals(Comparador.mayorDe3(3, 3, 3), 3);
}
Esta batera, ms detallada, s mostrara que hay casos en los que nuestro algoritmo no se
comporta bien:
En un caso general, antes de liberar cada nueva versin de un programa, se debera volver a
lanzar las pruebas automatizadas, para descubrir nuevos bugs que los cambios hubieran
podido introducir. Esto es lo que se conoce como realizar pruebas de regresin.
Ejercicios propuestos:
2.8.1: Crea dos funciones: una que devolver la primera solucin de una ecuacin de segundo
grado (- b + Raz(b*b - 4*a*c) / 2a ) y otra que devolver la segunda solucin de una ecuacin
de segundo grado (- b - Raz(b*b - 4*a*c) / 2a ) Elige una forma de tratar los casos en los que
hay solucin nica o en los que no existe solucin. Crea una batera de pruebas que te permita
tener unas mnimas garantas de que las funciones se comportan correctamente. Por ejemplo,