Tutorial de Java

Modelos

Anterior | Siguiente

Modelo Relacional de Objetos

Este modelo intenta fundir la orientación a objetos con el modelo de base de datos relacional. Como muchos de los lenguajes de programación actuales, como Java, son orientados a objetos, una estrecha integración entre los dos podría proporcionar una relativamente sencilla abstracción a los desarrolladores que programan en lenguajes orientados a objetos y que también necesitan programar en SQL. Esta integración, además, debería casi eliminar la necesidad de una constante traslación entre las tablas de la base de datos y las estructuras del lenguaje orientado a objetos, que es una tarea muy ardua.

A continuación se muestra un ejemplo muy simple para presentar la base del Modelo. Supóngase que se crea la siguiente Tabla en una base de datos:

Con una relativa facilidad, se puede mapear esta tabla en un objeto Java; que, tal como se muestra en el siguiente trozo de código:

class Empleado {
  int Clave;
  String Nombre;
  String Apellido;
  String Telefono;
  int Num_Empleado;

  Clave = Num_Empleado;
  }

Para recuperar esta tabla desde la base de datos a Java, simplemente se asignarían las columnas respectivas al objeto Empleado que se crearía previamente a la recuperación de cada fila, tal como se muestra a continuación:

Empleado objEmpleado = new Empleado();
objEmpleado.Nombre = resultset.getString( "nombre" );
objEmpleado.Apellido = resultset.getString( "apellido" );
objEmpleado.Telefono = resultset.getString( "telefono" );
objEmpleado.Num_Empleado = resultset.getInt( "num_empleado" );

Con una base de datos más grande, incluso con enlaces entre las tablas, el número de problemas se dispara, incluyendo la escalabilidad debida a los múltiples JOINs en el modelo de datos y los enlaces cruzados entre las claves de la tablas. Pero, afortunadamente, ya hay productos disponibles que permiten crear este tipo de puentes entre los modelos relacional y orientado a objetos; es más, hay varias de estas soluciones que están siendo desarrolladas para trabajar específicamente con Java.

Uno de los ejemplos, para Linux, de este tipo de herramientas es la base de datos PostGres, que es un sistema de base de datos relacional que unse las estructuras clásicas de estos sistemas con los conceptos de programación orientada a objetos, es decir, se trataría de una base de datos objeto-relacional. En PostGres, por ejemplo, las tablas de denominan clases, las filas se denominan instancias y las columnas se denominan atributos. Además, un concepto que aparece en PostGres y que viene claramente de la programación orientada a objetos es la Herencia, de forma que cuando se crea una nueva clase heredada de otra, la clase creada adquiere todas las características de la clase de la que proviene, más las características que se definan en la nueva clase. Por poner un ejemplo, si se tiene una tabla creada con la sentencia que se indica a continuación:

CREATE TABLA tabla1 (
  campo1 text
  campo2 int )

A continuación, se puede crear una segunda tabla con la sentencia SQL a continuación definida:

CREATE TABLA tabla2 (
  campo3 int )
INHERITS( tabla1 )

Como resultado de esta sentencia SQL, la nueva tabla tendrá los atributos campo1, campo2 y campo3.

Modelo de Conexión

La conexión a bases de datos relacionales a través de JDBC realmente es muy simple, tal como ya se ha podido comprobar. No obstante, en este apartado es proporcionar, o intentarlo al menos, una plantilla que se pueda reutilizar y personalizar para aprender a manipular bases de datos con Java. Una vez que el ejemplo sea comprendido por el lector, no habrá problemas a la hora de saber qué hacer y cómo hacerlo. Y cuando se necesite más información, la documentación que proporciona JavaSoft sobre JDBC la tiene.

De todas las cosas que hay que tener en mente, la conexión de Java con la base de datos relacional es la primera de las preocupaciones. En el ejemplo java2102.java se muestra cómo se hace, y establece la conexión. También contiene varios métodos que procesan sentencias SQL habituales de una forma simple y segura: la conexiones son abiertas y cerradas con cada sentencia SQL. Si el lector está construyendo aplicaciones en las que se prevean grandes flujos de transacciones, tendrá que establecer una estrategia más adecuada; por ejemplo, si se pretenden realizar actualizaciones sobre un registro una vez que se haya accedido a él, probablemente sea mejor mantener abierta la conexión con la base de datos hasta que hayan concluido todas las actualizaciones. Como la conexión a una base de datos es un objeto, se puede mantener en una variable tanto tiempo como se necesite; con lo cual, la aplicación será capaz de procesar las actualizaciones mucho más rápidamente, pero corriendo el riesgo de que otros usuarios tengan bloqueado el acceso hasta que las conexiones que estén bloqueadas se concluyan.

import java.io.*;
import java.net.*;
import java.util.*;
import java.sql.*;

public class java2102 extends Thread {
  public static final int PUERTO = 6700;
  ServerSocket socketEscucha;
    
  public java2102() {
    try {
      socketEscucha = new ServerSocket( PUERTO );            
    } catch( IOException e ) {
      System.err.println( e );
    }
    this.start();
  }

  public void run() {
    try {
      while( true ) {
        Socket socketCliente = socketEscucha.accept();
        SQLConexion c = new SQLConexion( socketCliente );
      }
    } catch( IOException e ) {
      System.err.println( e );
    }
  }

  public static void main( String[] argv ) {
    new java2102();
  }
}

class SQLConexion extends Thread {
  protected Socket cliente;
  protected BufferedReader in;
  protected PrintStream out;
  protected String consulta;

  public SQLConexion( Socket socketCliente ) {
    cliente = socketCliente;
    try {
      in = 
     new BufferedReader( new InputStreamReader( cliente.getInputStream() ) );
      out = new PrintStream( cliente.getOutputStream() );
    } catch( IOException e ) {
      System.err.println( e );
      try {
        cliente.close();
      } catch( IOException e2 ) {};
         return;
    }
    this.start();
  }

  public void run() {
    try {
      consulta = in.readLine();
      System.out.println( "Lee la consulta <" + consulta + ">" );
      ejecutaSQL();
    } catch( IOException e ) {}
    finally {
      try {
        cliente.close();
      } catch( IOException e ) {};
    }
  }

  public void ejecutaSQL() {
    Connection conexion;   // Objeto de conexión a la base de datos
    Statement sentencia;   // Objeto con la sentencia SQL
    ResultSet resultado;   // Objeto con el resultado de la consulta SQL
    ResultSetMetaData resultadoMeta;
    boolean mas;           // Indicador de si hay más filas
    String driver = "jdbc:odbc:Tutorial"; 
    String usuario = ""; 
    String clave = "";
    String registro;
    int numCols, i;

    try {
      Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver" );
      conexion = DriverManager.getConnection( driver,usuario,clave );

      sentencia = conexion.createStatement();
      resultado = sentencia.executeQuery( consulta );

      mas = resultado.next(); 
      if( !mas ) {
        out.println( "No hay mas filas." ); 
        return;
      }

      resultadoMeta = resultado.getMetaData();
      numCols = resultadoMeta.getColumnCount();
      System.out.println( numCols + " columnas en el resultado.");
      while( mas ) {
        // Se construye la cadena de respuesta
        registro = "";
        for( i=1; i <= numCols; i++ ) {
          registro = registro.concat( resultado.getString(i)+"  " );
        }
        out.println( registro );
        System.out.println( registro );
        mas = resultado.next();
      }

      resultado.close(); 
      sentencia.close();
      conexion.commit(); 
      conexion.close();
    } catch( Exception e ) {
      System.out.println( e.toString() );
    }
  }
}

El ejemplo, evidentemente, asume que hay una base de datos disponible para usar y su esquema es muy simple, ya que utiliza la base de datos del primer ejemplo del capítulo, con solamente tres campos. Mucho del desarrollo de este capítulo ha sido desarrollado en Linux utilizando la base de datos PostGres, y luego, exactamente el mismo código, solamente cambiando la conexión a la base de datos, ejecutado utilizando Microsoft Access, para capturar la ejecución con el API del último JDK.

La parte cliente del ejemplo anterior, es la que se ha codificado en el ejemplo java2103.java, implementado como applet, que permite introducir una consulta en el campo de texto, que será enviada al servidor implementado en el ejemplo anterior, que a su vez, enviará la consulta a la base de datos y devolverá el resultado al applet, que mostrará la información resultante de su consulta en la parte inferior del applet. En estos dos ejemplos, se muestran los fundamentos de la interacción con bases de datos, de una forma un poco más complicada, de tal modo que no desde el mismo programa se ataca a la base de datos; esto proporcionará al lector una visión más amplia de la capacidad y potencia que se encuentra bajo la conjunción de Java y las Bases de Datos.

import java.io.*;
import java.net.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;

public class java2103 extends Applet {
  static final int puerto = 6700;
  String cadConsulta = "No hay consulta todavia"; 
  String cadResultado = "No hay resultados";
  Button boton;
  TextArea texto;
  List lista;

  public void init() {
    setLayout( new GridLayout( 5,1 ) );
    texto = new TextArea( 20,40 );
    lista = new List();
    boton = new Button( "Ejecutar Consulta" );
    boton.addActionListener( new MiActionListener() );
    
    add( new Label( "Escribir la consulta aqui..." ) );
    add( texto );
    add( boton );
    add( new Label( "y examinar los resultados aqui" ) );
    add( lista );

    resize( 800,800 );
    }

  void abreSocket() {
    Socket s = null;
    try {
      s = new Socket( getCodeBase().getHost(),puerto );
      BufferedReader sinstream = 
        new BufferedReader(new InputStreamReader(s.getInputStream()));
      PrintStream soutstream = new PrintStream( s.getOutputStream() );

      soutstream.println( texto.getText() );
      lista.removeAll();
      cadResultado = sinstream.readLine();
      while( cadResultado != null ) {
        lista.add( cadResultado );
        cadResultado = sinstream.readLine();
        }
    } catch( IOException e ) { 
      System.err.println( e );
    } finally {
    try { 
      if( s != null ) 
        s.close();}
    catch( IOException e ) {}
    }
    }

  class MiActionListener implements ActionListener {
    public void actionPerformed( ActionEvent evt ) {
      abreSocket();
    }
  }
}

Una vez comprendidas las ideas básicas que se presentaban en los ejemplos anteriores, el lector podrá atreverse ya a escribir programas que manejen esquemas de bases de datos mucho más complicados. No obstante, si el lector tiene una buena base en el conocimiento de bases de datos relacionales, estará satisfecho con esta simplicidad.

El ejemplo java2104.java, es un simple interfaz para atacar a cualquier tipo de base de datos. Solamente se ha codificado el driver JDBC a utilizar, pero a través de la ventana que se presenta, se puede acceder a cualquier base de datos y cualquier tabla. La figura muestra la ventana, una vez que se ha accedido a la base de datos que se ha estado utilizando.

Y el código completo del ejemplo, que se ha intentado comentar profusamente para que la comprensión sea sencilla es el que se reproduce a continuación.

import java.net.URL;
import java.awt.*;
import java.sql.*;
import java.awt.event.*;

public class java2104 extends Frame implements MouseListener {
  // Se utiliza el itnerfaz MouseListener para poder capturar
  // los piques de ratón

  // Estos son los objetos que se van a utilizar en la aplicación
  Button botConexion = new Button( "  Conexión a la Base de Datos  " );
  Button botConsulta = new Button( " Ejecutar Consulta " );

  TextField txfConsulta = new TextField( 40 );
  TextArea txaSalida = new TextArea( 10,75 );
  TextField txfUsuario = new TextField( 40 );
  TextField txfClave = new TextField( 40 );
  TextField txfUrl = new TextField( 40 );
  String strUrl = "";
  String strUsuario = "";
  String strClave = "";
  // El objeto Connection es parte del API de JDBC, y debe ser lo
  // primero que se obtenga, ya que representa la conexión efectiva
  // con la Base de Datos
  Connection con;

  public static void main( String args[] ) {
    java2104 ventana = new java2104();
    
    // Se recoge el evento de cierre de la ventana
    ventana.addWindowListener( new WindowAdapter() {
      public void windowClosing( WindowEvent evt ) {
        System.exit( 0 );
      }
    } );
    
    ventana.setSize( 450,300 );
    ventana.setTitle( "Tutorial de Java, JDBC" );
    ventana.pack();
    ventana.setVisible( true );
  }

  // Constructor de la clase, que es el que construye el interfaz
  // que se va a mostrar en la ventana
  public java2104() {
    // Se hacen todos los campos de texto editables para que se
    // puedan introducir datos, y no se permite que se escriba en
    // el área de texto que se va a utilizar como salida de los
    // resultados de las acciones del usuario y las respuestas que
    // se obtengan de la base de datos a las consultas que se
    // realicen
    txfConsulta.setEditable( true );
    txfUsuario.setEditable( true );
    txfUrl.setEditable( true );
    txaSalida.setEditable( false );

    // Se va a utilizar el GridBagLayout, que aunque complicado
    // en su uso, tiene la flexibilidad que se necesita en este
    // caso concreto
    GridBagLayout gridbag = new GridBagLayout();
    GridBagConstraints gbCon = new GridBagConstraints();
    // Lo fijamos como el layout a utilizar
    setLayout( gridbag );

    // Se fija el color y la fuente de caracteres a usar
    setFont( new Font( "Helvetica",Font.PLAIN,12 ) );
    setBackground( Color.orange );

    // No se han fijado los setConstraints para el Label, para que
    // se asuman los de defecto. El campo de texto txfUsuario es
    // el último componente en su fila, a través de gbCon, y
    // luego se añade al interfaz de usuario
    gbCon.weightx = 1.0;
    gbCon.weighty = 0.0;
    gbCon.anchor = GridBagConstraints.CENTER;
    gbCon.fill = GridBagConstraints.NONE;
    gbCon.gridwidth = GridBagConstraints.REMAINDER;
    add( new Label( "Usuario" ) );
    gridbag.setConstraints( txfUsuario,gbCon );
    add( txfUsuario );
    add( new Label( "Clave de Acceso" ) );
    gridbag.setConstraints( txfClave,gbCon );
    add( txfClave );
    add( new Label( "URL de la Base de Datos" ) );
    gridbag.setConstraints( txfUrl,gbCon );
    add( txfUrl );
    // Ahora viene la fila en que está el botón de Conexión a la
    // base de datos, fijamos los constraints para que eso sea así
    // y lo añadimos
    gridbag.setConstraints( botConexion,gbCon );
    add( botConexion );

    // Ahora registramos el botón para que reciba los eventos del
    // raton a través del interfaz MouseListener
    botConexion.addMouseListener( this );

    // Ahora viene la zona que permite introducir el texto de la
    // consulta que se quiere realizar y el botón que va a permitir
    // su envío al driver JDBC
    add( new Label( "Consulta SQL" ) );
    gridbag.setConstraints( txfConsulta,gbCon );
    add( txfConsulta );
    gridbag.setConstraints( botConsulta,gbCon );
    add( botConsulta );
    botConsulta.addMouseListener( this );

    // Ahora se coloca una etiqueta en su propia línea para rotular
    // el área de texto en la que se van a presentar los resultados
    // de las consultas que se realicen
    Label labResultado = new Label( "Resultado" );
    labResultado.setFont( new Font( "Helvetica",Font.PLAIN,16 ) );
    labResultado.setForeground( Color.blue );
    gridbag.setConstraints( labResultado,gbCon );
    gbCon.weighty = 1.0;
    add( labResultado );

    // Ahora se cambia la forma de extensión de la ventana, para que
    // si se agranda la ventana tenga la mayor parte de espacio
    // posible en la zona de texto en donde se presentan los
    // resultados
    gridbag.setConstraints( txaSalida,gbCon );
    txaSalida.setForeground( Color.white );
    txaSalida.setBackground( Color.black );
    add( txaSalida );
    } 

  public void mouseClicked( MouseEvent evt ) {
    // Cuando se pulsa el botón Consulta, se recoge el contenido del 
    // campo de texto txfConsulta y se le pasa al método Select, que 
    // es el que va a realizar la consulta y devolver el resultado 
    // que se va a presentar en la zona de salida
    if( evt.getComponent() == botConsulta ) {
      System.out.println( txfConsulta.getText() );
      txaSalida.setText( Select( txfConsulta.getText() ) );
    }

    // Si se pulsa el botón de Conexión, se intenta establecer la
    // conexión con la base de datos indicada en el campo de texto
    // correspondiente a URL, con el usuario y clave que se hayan
    // indicado en los campos correspondientes
    if( evt.getComponent() == botConexion ) {
      // Se fijan las variables globales de usaurio, clave y url a 
      // los valores que se hayan introducido en los campos
      strUsuario = txfUsuario.getText();
      strClave = txfClave.getText();
      strUrl = txfUrl.getText();

      // La creación de la conexión con la base de datos lanza una
      // excepción en caso de que haya problemas al establecer esa
      // conexión con los aprámetros que se le indiquen, por ello
      // es imprescindible colocar el método getConnection en un
      // bloque try-catch. Si se produce algún problema y se lanza
      // la excepción, aparecerá reflejada en la consola y en el 
      // área que se ha destinado en la ventana a ver los resultados
      try {
        // Ahora se intenta crear una nueva instancia del driver que se
        // va a utilizar. Hay varias formas de especificar el driver que
        // se quiere, e incluso se puede dejar que sea el propio
        // DriverManager de JDBC que seleccione el que considere más
        // adecuado para conectarse a una fuente de datos determinada
        Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver" );
        // La conexión aquí se realiza indicando la URL de la base de
        // datos y el usuario y clave que dan acceso a ella
        con = DriverManager.getConnection( strUrl,strUsuario,strClave );
        // Si la conexión ha sido satisfactoria, cambiamos el rótulo
        // del botón de conexión, para que indique que si se pulsa lo
        // que se realiza será la "Reconexión"
        botConexion.setLabel( "Reconexión a la Base de Datos" );
        txaSalida.setText( "Conexion establecida con "+strUrl );
      } catch( Exception e ) {
        // Se presenta la información correspondiente al error, tanto
        // en la consola como en la zona de salida de la ventana
        e.printStackTrace();
        txaSalida.setText( e.getMessage() );
      }
    }
  }

  // Se implementan vacíos el resto de los métodos del interfaz de
  // eventos del ratón, MouseListener. Si se quiere evitar, también es
  // posible utilizar MouseAdapter, que tiene implementados, pero sin
  // acciones asignadas, todos estos métodos
  public void mouseEntered( MouseEvent evt ) {}
  public void mouseExited( MouseEvent evt ) {}
  public void mousePressed( MouseEvent evt ) {}
  public void mouseReleased( MouseEvent evt ) {}

  // Este es el método que realiza la consulta
  public String Select( String consulta ) {
    String resultado="";
    int cols;
    int pos;
    
    // Hay varios métos que se van a emplear y que lanzan excepciones
    // en caso de que haya algún problema con la consulta, o si se
    // rompe la conexión, etc
    try {
      // En primer lugar, se instancia la clase Statement, que es
      // necesaria para ejecutar la consulta. La clase Connection
      // devuelve un objeto Statement que se enlaza a la conexión
      // abierta para pasar de nuevo el objeto Statement. Así es
      // como la instancia "sentencia" se enlaza a la conexión actual
      // con la base de datos
      Statement sentencia = con.createStatement();

      // El objeto resultSet también es enlazado con la conexión a la
      // base de datos a través de la clase Statement, que contiene el
      // método executeQuery, que devuelve un objeto ResultSet.
      ResultSet rs = sentencia.executeQuery( consulta );

      // Ahora se utiliza el método getMetaData en el resultado para 
      // devolver un objeto MetaData, que contiene el método getColumnCount
      // usado para determinar cuántas columnas de datos están presentes
      // en el resultado.
      cols = ( rs.getMetaData() ).getColumnCount();

      // Aquí se utiliza el método next de la instancia "rs" de 
      // ResultSet para recorrer todas las filas, una a una. Hay formas
      // más optimizadas de hacer esto, utilizando la característica
      // inputStream del driver JDBC
      while( rs.next() ) {
        // Se recorre ahora cada una de las columnas de la fila, es
        // decir, cada celda, una a una
        for( pos=1; pos <= cols; pos++ ) {
          // Este es el método general para obetener un resultado. el
          // método getString intentará moldear el resultado a un String.
          // En este caso solamente se recoge el resultado y se le añade
          // un espacio y todo se añade a la variable "resultado"
          resultado += rs.getString( pos )+" ";
        }

        // Para cada fila que se revise, se le añade un retorno de 
        // carro, para que la siguiente fila empiece en otra línea
        resultado += "\n";
      }

      // Se cierra la "sentencia". En realidad se cierran todos los 
      // canales abiertos para la consulta pero la conexión con la
      // base de datos permanece
      sentencia.close();
    } catch( Exception e ) {
      e.printStackTrace();
      resultado = e.getMessage();
    }

  // Antes de salir, se devuelve el resultado obtenido
  return resultado;
  }
}

Navegador

Home | Anterior | Siguiente | Indice | Correo