acceso al nivel de datos desde la lógica del negocio usando un sgbd relacional.

arquitectura física en 2 niveles: cliente gordo/servidor flaco

1. Objetivos: 

2.Tareas a realizar:

Este laboratorio es una continuación del anterior y consiste en crear una nueva clase que implemente el nivel de lógica del negocio y que acceda a una BD que contenga los nombres de usuarios y passwords con permiso de acceso al sistema, para ello llevaremos a cabo los siguentes pasos:

 1.- Crear un proyecto con las clases del laboratorio anterior: Lanzador, LanzadorFrame, Presentacion, e InterfaceLogicaNegocio. En realidad, se puede comenzar usando la solución que se encuentra aquí:  SOLUCIÓN LABORATORIO 2

 2. Manejo de ODBC local. Crear una BD Access (cuentas.mdb) que contenga una tabla “cuenta” con atributos “usuario” (del tipo texto), “password” (del tipo texto) y  numIntentosFallidos” (del tipo numérico) y añadir algunas tuplas de ejemplo.

 2.1. Definir una fuente de datos ODBC llamada  BDPasswLabLog3N y asociadla a la BD anterior.  Para ello:

    a)  En Windows 2000: hay que acceder al Panel de control => Herramientas administrativas => Orígenes de datos ODBC

    b)  En Windows XP: hay que acceder al Panel de control => Rendimiento y Mantenimiento => Herramientas administrativas => Orígenes de datos ODBC

    c)  O simplemente ejecutando el comando: odbcad32 (Menú Inicio del Sistema Operativo => Ejecutar => odbcad32)

2.2. Dentro del Administrador de ODBC hay que definir el nombre de la fuente de datos BDPasswLabLog3N y seleccionar la BD cuentas.mdb.

Dentro de la pestaña DNS del usuario (User DNS) pinchar en Agregar (Add) y seleccionar "Microsoft Access Driver". Esto hará que surja un ventana en la cual pondremos como nombre del origen de datos BDPasswLabLog3N y mediante el botón Seleccionar buscaremos la BD cuentas.

   

3.- Añadir una nueva lógica de negocio llamada EntradaSistemaDB que implemente la interface InterfaceLogicaNegocio,la cual se encargara de gestionar la BD. Compilad la nueva clase y llevad a cabo los siguientes pasos compilando en cada paso:

Nota: Si en el paso anterior tenéis problema de compilación recordad implementar el método hacerLogin.

    public class EntradaSistemaDB implements InterfaceLogicaNegocio {
        public EntradaSistemaDB() {}
        public boolean hacerLogin(String u, String p) {
        return true;}
    }

3.1. Inicilizar los driver. La clase que se encarga de inicializar o cargar los drivers es  DriverManager perteneciente al paquete Java.sql.*. La carga (Class.forName) dependera del controlador.

Nota: Al compilar vereis que es necesario atrapara una excepción. Si no lo podeis solucionar presetad atención a la primera línea de la excepción.

    import java.sql.*;

    public EntradaSistemaDB() {
        try {
            Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
        } catch (ClassNotFoundException e) { System.out.println("Clase no encontrada");
        } catch (Exception ex) { ex.printStackTrace(); }
    }


3.2. Crear una conexión. Luego de inicializar los drivers, es necesario crear una conexion para ello se utiliza el DriverManager para obtener un objeto del tipo conexión, Connection.

 

Puente JDBC/ODBC --> Connection c =  DriverManager.getConnection("jdbc:odbc:NombreBD");
JDBC                        -->  Connection c = DriverManager.getConnection(String d, String u, String c);

                                         // d = identifica la BD, jdbc:subprotocolo//servidor:puerto/BDs

                                         // u = identifica el usuario

                                         // c = identifica la clave

                                        // "jdbc:oracle:thin:@g010128.si.ehu.es:1521:ISO", "ISO", "ISO"

                                        // "jdbc:mysql://jipla0.gi.ehu.es/ISO","ISO","ISO"

 

     Connection c = DriverManager.getConnection("jdbc:odbc:BDPasswLabLog3N");


3.3. Crear una sentencia. Una vez que se tiene un objeto del tipo Connection, se pueden crear sentencias (statements).

// c es un objeto Connection
Statement s = c.createStatement();      

// Ejecuta una sentencia SQL INSERT, UPDATE o DELETE
int i = s.executeUpdate(String sql);    

// Ejecuta una sentencia SQL SELECT y devuelve el resultado en un objeto ResultSet

ResultSet r = s.executeQuery(String sql);

Cread una sentencia que recupere la primera tupla de la tabla y compilad

     ResultSet r = s.executeQuery("Select * from cuenta

            where usuario="+"invitado"+ "and password="+"hola");

Nota: Recordad que los String se concatenan con el signo +, es decir, "usuario=" + "invitado"

Moverse por el ResultSet. Cada una de estas sentencias puede devolver cero o más resultados, los mismos son devueltos como objetos del tipo ResultSet. Mediante ResultSet.next() nos podemos mover al «siguiente» elemento del resultado, o bien sobre el primero si todavía no se ha utilizado. La función next() devuelve true o false si el elemento existe, de forma que se puede iterar mediante la sentencia while ( resultado.next()) de este modo se puede tener acceso a todos los elementos.

Para tener acceso a la diferentes columnas se utilizan los métodos getXXX(). El acceso se puede hacer por el nombre de la columna o bien mediante su ubicación relativa. Además de getString() están disponibles getBoolean(), getByte(), getDouble(), getFloat(), getInt(), getLong(), getNumeric(), getObject(), getShort(), getDate(), getTime() y getUnicodeStream(), cada uno de los cuales devuelve la columna en el formato correspondiente, si es posible.

3.5. Probar a recuperar la primera tupa y luego la segunda. ¿Que sucede?. Hay que reescribir el código, es posible parametrizar las sentencias SQL. Un ejemplo de ello se observa a continuación.

        PreparedStatement s =  c.prepareStatement(“Select nombre From Persona

                                  Where ciudad =? and edad=?”);
        s.setString(1, ”San Luis”); // pone San Luis en el primer parámetro
        s.setInt(2, 25); // pone 25 en el segundo parámetro
        ResultSet r = s.executeQuery();


Implemetad el método hacerLogin para que consulte en la base de datos si existe o no el usuario y password pasado por referencia. Compilad y atrapad las excepciones.

    public boolean hacerLogin(String u, String p) {
        try {
            PreparedStatement s;
            s=c.prepareStatement("select * from cuenta where usuario=? and password=?");
            s.setString(1,u);
            s.setString(2,p);
            ResultSet r=s.executeQuery();
            return r.next();
        } catch (SQLException e) {

            System.out.println("Error en el SQL: "+e.toString()); return false;
        } catch (Exception ex) {

            System.out.println("Error: "+ex.toString()); return false;}
    }

3.6. Cerrar la conexión. Después de haber trabajado con una sentencia o una conexión es recomendable cerrarla mediante sentencia.close() o conexión.close().

Cread un método que cierre la conexión, compilad y atrapad las excepciones.

    public void cerrarConexion() {
        try {
            c.close();
        } catch (SQLException e) { e.printStackTrace();
        } catch (Exception ex) { ex.printStackTrace(); }
    }

4. Ejecutar la lógica del negocio creada

4.1. Comprobar que dentro de vuestro código se utiliza una fuente de datos ODBC llamada BDPasswLabLog3N.

4.2 Ejecutar la aplicación asignando la nueva lógica del negocio. Intentadlo pero si teneis algun problema, aqui teneis la Solución del laboratorio hasta este paso.

 

5.- Manejo de ODBC Remoto. En esta sección dividiremos nuestra aplicación en dos niveles: 1) colocando en un ordenador remoto la base de datos (Servidor Flaco) y 2) colocando nuestra aplicación en un ordenador local (Cliente Gordo). Por lo tanto, el cliente tendrá el nivel de  presentación (la clase Presentacion) y la lógica del negocio (la clase EntradaSistemaBD) y el servidor tendrá la tabla CUENTA (usuario, password, numIntentosFallidos).

 

El manejo ODBC Remoto es totalmente trasparente al usuario y de hecho no hace falta CAMBIAR NI RECOMPILAR EL CÓDIGO DE LA PRESENTACIÓN, NI EL DE LA LÓGICA DEL NEGOCIO. Solo es necesario que el servidor se encuentre  accesible a través de la red, y remplazar o redefinir la conexion ODBC (BDPasswLabLog3N). De esta forma la aplicación buscará la BD en el servidor y no en nuestro ordenador, para ello se ha dejado una base de datos a disposición con las siguientes caracteristicas:

     - el servidor docente JIPL00 (Este es el ordenador que hay que buscar en la red)

     - el mismo contiene un recurso compartido llamado DOCENCIA (Este es el recurso compartido que hay que “Conectar a unidad de red”)

     - y dentro del directorio ISO se encuentra la base de datos

a) Hay que buscar el ordenador JIPL00 en la red (Menú Inicio S.O. => Buscar => Archivos o Carpetas … => Buscar Equipo => Nombre equipo: JIPL00

        Nota: si no lo encuentra o hay problemas para entrar, intentad buscarlo añadiendo el dominio: jipl00.gi.ehu.es

 b) Hacer doble click en el equipo JIPL00 para encontrar los recursos compartidos. Conectar el recurso compartido “Docencia” como una unidad de red del ordenador local. Para ello, hacer click con el botón derecho sobre el recurso compartido y seleccionar la opción “Conectar a unidad de red”. Aceptar el nombre de la unidad de red propuesta.

 c) Reconfigurar la fuente de datos ODBC anterior seleccionando la BD remota en la unidad de red creada anteriormente. La base de datos “passwords.mdb” se encuentra en el directorio “ISO”. Si teneis algun problema recurid el punto 2 de este laboratorio.

 

Probad la aplicación distibuida.

6.- Hasta el momento no hemos hecho uso del atributo “numIntentosFallidos” de la tabla “cuenta. Modificar la clase del negocio para incrementar en uno el número de intentos fallidos al introducir incorrectamente el password de un usuario e inicializa a cero el mismo  cuando se entra correctamente en el sistema. Si teneis algún problemar recurir al punto 3.3 de este laboratorio.

    Statement st;

    st = c.createStatement();

    boolean res = r.next();
    if (res) st.executeUpdate("update cuenta set

            numIntentosFallidos=0 where usuario='"+u+"'");
    else st.executeUpdate("update cuenta set

            numIntentosFallidos=numIntentosFallidos+1 where usuario='"+u+"'");
    return res;

7. Manejo de JDBC Remoto y librerías disponibles. Hasta el momento hemos trabajado con ODBC ahora lo haremos con JDBC, para ello probaremos conectarnos a otro sistema de gestión de BD como es ORACLE, para ello debemos cargar el controlador adecuado recuerda el punto 3.1 y 3.2 de este laboratorio. La maquina a la cual nos conectaremos sera:

    - Máquina: g010128.si.ehu.es

    - Puerto: 1521

    - BD: ISO

    - Usuario/Password: ISO

Probad a ejecutar la aplicación que sucede? Nos da una excepción porque no es posible cargar o encontrar la clase JDBC. Este error no nos aparecia en el caso de ODBC porque sun proporciona las clases necesarias para  realizar el puente. Pero para el caso de JDBC es necesario cargarlo uno mismo. Para ello hay que realizar los siguientes pasos:

1. Debemo pinchar con el botón derecho sobre el proyecto, esto hara que aparezca una lista desplegable.

2. Seleccionar la última opción de la lista "Project Setting"

3. Bucar "Oracle JDCB"  dentro de Configurations --> Development --> Libraries

4. Seleccionarla y pinchar en la flecha de la derecha de forma tal que aparezca en el cuadro adyacente.

5. Ejecutad la aplicación y comprobad su funcionamiento.

    Class.forName("oracle.jdbc.driver.OracleDriver");
    c = DriverManager.getConnection(

        "jdbc:oracle:thin:@g010128.si.ehu.es:1521:ISO","ISO","ISO");

9. Manejo de JDBC Remoto y librerías no disponibles. Para conectarnos al servidor Oracle fue necesario agregar la libreria "ORACLE JDBC" disponible en JDeveloper ya que ambas herramientas (BD Oracle y JDeveloper) pertenecen a la misma compañia, pero que sucede si deseamos usar librerias externas como puede ser una BD de libre distribución del tipo Mysql, será necesario bajarnos los driver o librería necesarias para gestionar las mismas desde Java.

Probaremos concectarnos al servidor Mysql que se encuentra en:

    - Máquina: jipla.si.ehu.es

    - BD: ISO

    - Usuario/Password: ISO

 

     Class.forName("com.mysql.jdbc.Driver");
     c = DriverManager.getConnection(

        "jdbc:mysql://jipla0.gi.ehu.es/ISO","ISO","ISO");

Probad a ejecutar la aplicación que sucede? Nos da una excepción porque no es posible cargar o encontrar la clase JDBC. Para ello repetiremos los pasos 1, 2 y 3 del punto anterior

1. Debemo pinchar con el botón derecho sobre el proyecto, esto hara que aparezca una lista desplegable.

2. Seleccionar la última opción de la lista "Project Setting"

3. Bucar "Oracle JDCB"  dentro de Configurations --> Development --> Libraries

4. Pinchar en new y colocar como nombre de la libreria: Mysql y buscar las librerias en JIPL00/Docencia/ISO/mysql-connector-java-3.0.17-ga-bin.jar

Nota: Si teneis el siguiente problema (table or view that not exist) al ejecutar la aplicación anteponed al nombre de la tabla (cuenta) lo siguiente: "jimena." de forma tal que  la tabla se llame "jimena.cuenta"

8. Parametrizacion del manejo de conexiones. Utilizar un archivo de configuración para no tener que recompilar la aplicación cada vez que cambiamos de BD. Para ello crea una clase llamada PropiedadesDelSistema.

8.1. Leer los atributos del sistema. Por lo general, un programa contiene un conjunto de atributos del sistema como es la dirección del servidor, el puerto de conexión, el sistema operativo. A veces, también tiene sus propios atributos configurables llamados atributos del programa que permiten al usuario configurar aspectos de una aplicación. Estos valores se suelen llamar preferencias de usuario. Los atributos del sistema son mantenidos por la maquina virtual Java mediante la clases System más concretamente por el método getProperty(String property). Agrega el siguiente código a la nueva clase.

    import java.util.Properties;

    Properties props = System.getProperties();

    System.out.println(props);

8.2. Crea una función principal y ejecuta el programa. Como resultado veremos una lista de todas las claves/valores (key/values). La clave es la “palabra” y el valor es la “definición” de dicha palabra. Cada Property key es un atributo de configuración del programa, y su correspondiente Property value es el valor de dicha propiedad.

    java.runtime.name=Java(TM) 2 Runtime Environment Standard Edition,

    sun.boot.library.path=D:\JDeveloper903i\jdk\jre\bin,

    java.vm.version=9.0.3.738 cdov, ....

8.3. Cargar atributos del programa. Los atributos del programa se gestionan con la clase  java.util.Properties. La cual nos permite: 1) Cargar pares key/values desde un fichero; 2) Obtener un valor a partir de una clave; 3) Listar todas las claves y sus valores; 4) Hacer un recorrido por las claves; 5) Salvar las claves a un fichero.

Lo primero que debemos hacer para cargar los atributos del programa es hacer un archivo de configuración, llamad al archivo "ArchivoDeConfiguracion.txt", el cual tendra el siguiente aspecto

    # esto es un comentario y no sera leido

    sistema_operativo = Windows

    bd_utilizada = Oracle

Las claves están separadas de sus valores por el símbolo “=”.  A la hora de ser cargado por el sistema no se tienen en cuenta ni los espacios ni líneas en blanco. Las líneas que empiezan por “#” se utilizan para hacer comentarios.

8.4. Cambiad la clase EntradaSistemaDB para que tome en consideración el archivos de configuración. Implementad el método getSO y getDB para obtener los datos cargados en tiempo de ejecucion.

          FileInputStream f = new FileInputStream("c:\\lab2\\ArchivoDeConfiguracion.txt");
    props.load(f);
    System.setProperties(props);
    s_sist_op = props.getProperty("sistema_operativo");
    s_db = props.getProperty("bd_utilizada");

Nota: Al compilar nos encontraremso con que JDevoloper no encuentra la clase FileInputStream. Navega en la siguiente pagina (http://java.sun.com/j2se/1.3/docs/api/) y busca el paquete a incorporar y atrapa las excepciones.

import java.io.FileInputStream;

public class PropiedadesDelSistema {
private String s_sist_op;
private String s_db;
public PropiedadesDelSistema() {
try {
    Properties props = System.getProperties();
    FileInputStream f = new FileInputStream("
c:\\lab2\\ArchivoDeConfiguracion.txt");
    props.load(f);
    System.setProperties(props);
    s_sist_op = props.getProperty("sistema_operativo");
    s_db = props.getProperty("bd_utilizada");
    } catch (Exception ex) { ex.printStackTrace(); }
}
public String getSO () { return s_sist_op; }
public String getDB () { return s_db; }
 

public EntradaSistemaDB() {
 try {
   PropiedadesDelSistema prop = new PropiedadesDelSistema();
   if (prop.getSO().equals("Windows")) {
     if (prop.getDB().equals("ACCESS")) {
        Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
        c = DriverManager.getConnection("jdbc:odbc:BDPasswLabLog3N");
    } else if (prop.getDB().equals("ORACLE")) {
        Class.forName("oracle.jdbc.driver.OracleDriver");
        c = DriverManager.getConnection(

           "jdbc:oracle:thin:@g010128.si.ehu.es:1521:ISO","ISO","ISO");
    } else if (prop.getDB().equals("MYSQL")) {
        Class.forName("com.mysql.jdbc.Driver");
        c = DriverManager.getConnection(

            "jdbc:mysql://jipla0.gi.ehu.es/ISO","ISO","ISO");
   }
  }
...

8.4. Ejecutar la aplicación cambiando en el archivo de configuración la BD utilizada como se observa no es necesario recompilar la aplicacion, ni realizar cambios, solo basta con parametrizar el sistema.

Una vez que acabemos el laboratorio hay que eliminar del disco duro la carpeta de trabajo con los proyectos .jpr, los fuentes .java, las clases .class, y el workspace .jws