Acceso a Datos - Resumen

64
Introducción al acceso a datos. 1.- Introducción. 2.- Acceso a datos. Mediante ficheros. Bases de datos. Mapeo objeto relacional (ORM). Bases de datos XML. Componentes. 2.1.- Qué estrategia o método de acceso a datos usar. Dependerá de las alternativas que haya y del sistema de información en estudio. 3.- Ficheros. Para guardar poca información, es mejor usarlos que usar otro método. 3.1.- Uso ficheros en la actualidad. Por esta razón se emplea XML en tecnologías de comunicación como, por ejemplo, en WML (lenguaje de formato inalámbrico) y WAP (protocolo de aplicaciones inalámbricas). 4.- Bases de datos. 4.1.- Introducción. Las ventajas que aportan los sistemas de bases de datos respecto a los sistemas de archivos convencionales son: Independencia de los datos respecto de los procedimientos. Disminución de las redundancias y en consecuencia, Disminución de la posibilidad de que se produzca inconsistencia de datos. Mayor integridad de los datos. Mayor disponibilidad de los datos. Mayor seguridad de los datos. Mayor privacidad de los datos. Mayor eficiencia en la recogida, codificación y entrada en el sistema. Lo que se suele denominar interfaz con el pasado y futuro: una base de datos debe estar abierta a reconocer información organizada físicamente por otro software. Compartición de los datos. 4.2.- Bases de datos relacionales. 4.3.- Bases de datos orientadas a objetos. una característica general es que el lenguaje de programación y el esquema de la base de datos utilizan las mismas definiciones de tipos. 4.4.- Comparativa entre bases de datos relacionales y orientadas a objetos. Podemos decir que las ventajas de un SGBDOO frente a las relacionales son: Permiten mayor capacidad de modelado. Extensibilidad Disposición de un lenguaje de consulta más expresivo. Adaptación a aplicaciones avanzadas de base de datos. Prestaciones. Reglas de acceso. Clave. 4.4.1.- Desventajas de las bases de datos orientadas a objetos frente a las relacionales. Como desventajas o puntos débiles de las BBDDOO respecto a las relacionales podemos mencionar: La reticencia del mercado Carencia de un modelo de datos universal. Carencia de experiencia. Dificultades en optimización. 4.5.- Bases de datos objeto-relacionales. Con las Bases de Datos Objeto-Relacional se amplía el modelo relacional destacando las siguientes aportaciones: Se aumentan la variedad en los tipos de datos,se pueden crear nuevos tipos de datos Hay extensiones en el control de la Semántica de datos Objeto-Relacionales:Se pueden crear procedimientos almacenados y funciones que tengan un código en algún lenguaje de programación Se pueden compartir varias librerías de clases ya existentes, esto es lo que conocemos como reusabilidad. 5.- Acceso a bases de datos mediante conectores. Un driver JDBC es un componente software que posibilita a una aplicación Java interaccionar con una base de datos. Mediante JDBC el programador puede enviar sentencias SQL, y PL/SQL a una base de datos relacional. JDBC permite embeber SQL dentro de código Java. 6.- Mapeo objeto relacional (ORM). El mapeo objeto-relacional (Object-Relational Mapping, o ORM) consisten en una técnica de programación para convertir datos entre el sistema de tipos utilizado en un lenguaje de programación orientado a objetos y el sistema utilizado en una base de datos relacional. Cuando se requiere almacenar la información de los objetos utilizando una base de datos relacional se comprueba que hay un problema de compatibilidad entre estos dos paradigmas, el llamado desfase objeto-relacional. 6.1.- Capa de persistencia y framework de mapeo. La capa de persistencia de una aplicación es la pieza que permite almacenar, recuperar, actualizar y eliminar el estado de los objetos que necesitan persistir en un sistema gestor de datos. La capa de persistencia traduce entre los dos modelos de datos: desde objetos a registros y desde registros a objetos. Así, si el programa quiere grabar un objeto, entonces llama al motor de persistencia, el motor de persistencia traduce el objeto a registros y llama a la base de datos para que guarde estos registros. Las comunidades open source incluyen importantes tecnologías, entre ellas Hibernate y el framework Spring. Las alternativas más importantes basadas en el estándar, son EJB 3.0 y JDO. Entre las implementaciones comerciales se puede resaltar TopLink. 7.- Bases de datos XML. Las bases de datos XML nativas permiten trabajar con XQL (eXtensible Query Language), el cuál sirve un propósito similar a SQL en una base de datos relacional. El trabajo con bases de datos XML nativas involucra dos pasos básicos: Describir los datos mediante Definiciones de Tipos de Datos (Document Type Definitions, DTD) o esquemas XML y Definir un nuevo esquema de base de datos XML nativa o Mapa de Datos a usar para almacenar y obtener datos. 8.- Desarrollo de componentes. 8.1.- Definición de componente. Un componente es una unidad de software que realiza una función bien definida y posee una interfaz bien definida. 8.2.- JavaBeans. El origen de los JavaBeans lo podemos encontrar en un par de necesidades que Java tenía: Disponer de una tecnología de objetos y componentes reutilizables. Mejorar el proceso para crear interfaces de usuario Un JavaBean es un componente software reutilizable basado en la especificación JavaBean de Sun (ahora Oracle) que se puede manipular visualmente con una herramienta de desarrollo.

Transcript of Acceso a Datos - Resumen

Page 1: Acceso a Datos - Resumen

Introducción al acceso a datos. 1.- Introducción. 2.- Acceso a datos. Mediante ficheros. Bases de datos. Mapeo objeto relacional (ORM). Bases de datos XML. Componentes.

2.1.- Qué estrategia o método de acceso a datos usa r. Dependerá de las alternativas que haya y del sistema de información en estudio.

3.- Ficheros. Para guardar poca información, es mejor usarlos que usar otro método.

3.1.- Uso ficheros en la actualidad. Por esta razón se emplea XML en tecnologías de comunicación como, por ejemplo, en WML (lenguaje de formato inalámbrico) y WAP (protocolo de aplicaciones inalámbricas).

4.- Bases de datos.

4.1.- Introducción. Las ventajas que aportan los sistemas de bases de datos respecto a los sistemas de archivos convencionales son: • Independencia de los datos respecto de los procedimientos. • Disminución de las redundancias y en consecuencia, • Disminución de la posibilidad de que se produzca inconsistencia de datos. • Mayor integridad de los datos. • Mayor disponibilidad de los datos. • Mayor seguridad de los datos. • Mayor privacidad de los datos. • Mayor eficiencia en la recogida, codificación y entrada en el sistema. • Lo que se suele denominar interfaz con el pasado y futuro: una base de datos debe estar abierta a reconocer información organizada

físicamente por otro software. • Compartición de los datos.

4.2.- Bases de datos relacionales. 4.3.- Bases de datos orientadas a objetos. una característica general es que el lenguaje de programación y el esquema de la base de datos utilizan las mismas definiciones de tipos.

4.4.- Comparativa entre bases de datos relacionales y orientadas a objetos. Podemos decir que las ventajas de un SGBDOO frente a las relacionales son:

• Permiten mayor capacidad de modelado. • Extensibilidad • Disposición de un lenguaje de consulta más expresivo. • Adaptación a aplicaciones avanzadas de base de datos. • Prestaciones. • Reglas de acceso. • Clave.

4.4.1.- Desventajas de las bases de datos orientada s a objetos frente a las relacionales. Como desventajas o puntos débiles de las BBDDOO respecto a las relacionales podemos mencionar:

• La reticencia del mercado • Carencia de un modelo de datos universal. • Carencia de experiencia. • Dificultades en optimización.

4.5.- Bases de datos objeto-relacionales. Con las Bases de Datos Objeto-Relacional se amplía el modelo relacional destacando las siguientes aportaciones:

• Se aumentan la variedad en los tipos de datos,se pueden crear nuevos tipos de datos • Hay extensiones en el control de la Semántica de datos Objeto-Relacionales:Se pueden crear procedimientos almacenados y funciones

que tengan un código en algún lenguaje de programación • Se pueden compartir varias librerías de clases ya existentes, esto es lo que conocemos como reusabilidad.

5.- Acceso a bases de datos mediante conectores. Un driver JDBC es un componente software que posibilita a una aplicación Java interaccionar con una base de datos. Mediante JDBC el programador puede enviar sentencias SQL, y PL/SQL a una base de datos relacional. JDBC permite embeber SQL dentro de código Java.

6.- Mapeo objeto relacional (ORM). El mapeo objeto-relacional (Object-Relational Mapp ing, o ORM) consisten en una técnica de programación para convertir datos entre el sistema de tipos utilizado en un lenguaje de programación orientado a objetos y el sistema utilizado en una base de datos relacional. Cuando se requiere almacenar la información de los objetos utilizando una base de datos relacional se comprueba que hay un problema de compatibilidad entre estos dos paradigmas, el llamado desfase objeto-relacional. 6.1.- Capa de persistencia y framework de mapeo. La capa de persistencia de una aplicación es la pieza que permite almacenar, recuperar, actualizar y eliminar el estado de los objetos que necesitan persistir en un sistema gestor de datos. La capa de persistencia traduce entre los dos modelos de datos: desde objetos a registros y desde registros a objetos. Así, si el programa quiere grabar un objeto, entonces llama al motor de persistencia , el motor de persistencia traduce el objeto a registros y llama a la base de datos para que guarde estos registros. Las comunidades open source incluyen importantes tecnologías, entre ellas Hibernate y el framework Spring . Las alternativas más importantes basadas en el estándar, son EJB 3.0 y JDO. Entre las implementaciones comerciales se puede resaltar TopLink. 7.- Bases de datos XML. Las bases de datos XML nativas permiten trabajar con XQL (eXtensible Query Language), el cuál sirve un propósito similar a SQL en una base de datos relacional. El trabajo con bases de datos XML nativas involucra dos pasos básicos:

• Describir los datos mediante Definiciones de Tipos de Datos (Document Type Definitions, DTD) o esquemas XML y • Definir un nuevo esquema de base de datos XML nativa o Mapa de Datos a usar para almacenar y obtener datos.

8.- Desarrollo de componentes. 8.1.- Definición de componente. Un componente es una unidad de software que realiza una función bien definida y posee una interfaz bien definida.

8.2.- JavaBeans. El origen de los JavaBeans lo podemos encontrar en un par de necesidades que Java tenía: • Disponer de una tecnología de objetos y componentes reutilizables. • Mejorar el proceso para crear interfaces de usuario

Un JavaBean es un componente software reutilizable basado en la especificación JavaBean de Sun (ahora Oracle) que se puede manipular visualmente con una herramienta de desarrollo.

Page 2: Acceso a Datos - Resumen

Manejo de ficheros. 1.- Introducción. La librería java.io contiene las clases necesarias para gestionar las operaciones de entrada y salida con Java.

2.- Clases asociadas a las operaciones de gestión d e ficheros y directorios. 2.1.- Clase File. La clase File proporciona una representación abstracta de ficheros y directorios. Dado un objeto File , podemos hacer las siguientes operaciones con él:

• Renombrar el archivo, con el método renameTo(). El objeto File dejará de referirse al archivo renombrado, ya que el String con el nombre del archivo en el objeto File no cambia.

• Borrar el archivo, con el método delete(). También, con deleteOnExit() se borra cuando finaliza la ejecución de la máquina virtual Java.

• Crear un nuevo fichero con un nombre único. El método estático createTempFile() crea un fichero temporal y devuelve un objeto File que apunta a él. Es útil para crear archivos temporales, que luego se borran, asegurándonos tener un nombre de archivo no repetido.

• Establecer la fecha y la hora de modificación del a rchivo con setLastModified(). Por ejemplo, se podría hacer: new File("prueba.txt").setLastModified(new Date().getTi me()); para establecerle la fecha actual al fichero que se le pasa como parámetro, en este caso prueba.txt.

• Crear un directorio, mediante el método mkdir (). También existe mkdirs(), que crea los directorios superiores si no existen. • Listar el contenido de un directorio. Los métodos l ist() y listFiles() listan el contenido de un directorio. list() devuelve un vector

de String con los nombres de los archivos, listFiles() devuelve un vector de objetos File. • Listar los nombres de archivo de la raíz del sistem a de archivos , mediante el método estático listRoots().

Para crear un objeto File nuevo, se puede utilizar cualquiera de los tres constructores siguientes: File miFichero; miFichero = new File( "/etc/kk" ); File f = new File("C:\texto.txt"); o miFichero = new File( "/etc","kk" ); o File miDirectorio = new File( "/etc" ); o miFichero = new File( miDirectorio,"kk" ); Nombres de fichero: String getName(); String getPath(); String getAbsol utePath(); String getParent(); boolean renameTo( Fi le nuevoNombre ) Comprobaciones: boolean exists(); boolean canWrite(); boolean canRe ad(); boolean isFile(); boolean isDirectory(); bool ean isAbsolute() Información general del fichero: long lastModified(); long length() Utilidades de directorio: boolean mkdir(); String[] list() import java.io.*; class InfoFichero { public static void main( String args[] ) throws IOException { if( args.length > 0 ) { for( int i=0; i < args.length; i++ ) { File f = new File( args[i] ); System.out.println( "Nombre: "+f.ge tName() ); System.out.println( "Camino: "+f.ge tPath() ); if( f.exists() ) { System.out.print( "Fichero exis tente " ); System.out.print( (f.canRead() ? " y se puede Leer" : "" ) ) ; System.out.print( (f.canWrite() ? " y se puese Escribir" : "" ) ); System.out.println( "." ); System.out.println( "La longitu d del fichero son "+ f.length()+" bytes" ); } else System.out.println( "El fichero no existe." ); } } else System.out.println( "Debe indicar un fi chero." ); } } 2.2.- Interface FilenameFilter. El interface FilenameFilter se puede usar para crear filtros que establezcan criterios de filtrado relativos al nombre de los ficheros. Una clase que lo implemente debe definir e implementar el método: boolean accept(File dir, String nombre) Este método devolverá verdadero en el caso de que el fichero cuyo nombre se indica en el parámetro nombre aparezca en la lista de los ficheros del directorio indicado por el parámetro dir. import java.io.File; import java.io.FilenameFilter; public class Filtrar implements FilenameFilter { String extension; // Constructor Filtrar(String extension){ this.extension = extension; } public boolean accept(File dir, String name){ return name.endsWith(extension); } public static void main(String[] args) { try { // Obtendremos el listado de los archivos de ese directorio File fichero=new File("c:\\datos\\."); String[] listadeArchivos = fichero.list (); // Filtraremos por los de extension .txt

listadeArchivos = fichero.list(new Filt rar(".txt")); // Comprobamos el número de archivos en el listado int numarchivos = listadeArchivos.lengt h ; // Si no hay ninguno lo avisamos por consola if (numarchivos < 1) System.out.println("No hay archivos que listar"); // Y si hay, escribimos su nombre por con sola. else { for(int conta = 0; conta < listadeA rchivos.length; conta++) System.out.println(listadeArchi vos[conta]); } } catch (Exception ex) { System.out.println("Error al buscar en la ruta indicada"); } } }

Page 3: Acceso a Datos - Resumen

2.3.- Rutas de los ficheros. Para evitar problemas en la ejecución de los programas cuando se ejecuten en uno u otro sistema operativo y, por tanto, persiguiendo que nuestras aplicaciones sean lo más portables posibles, se recomienda usar en Java: File.separator. String substFileSeparator(String ruta){ String separador = "\\"; try{ // Si estamos en Windows if ( File.separator.equals(separador) ) separador = "/" ; // Reemplaza todas las cadenas que coincide n con la expresión // regular dada oldSep por la cadena File.s eparator return ruta.replaceAll(separador, File.s eparator); }catch(Exception e){ // Por si ocurre una java.util.regex.PatternSyn taxException return ruta.replaceAll(separador + separ ador, File.separator); } } 2.4.- Creación y eliminación de ficheros y director ios. Cuando queramos crear un fichero , podemos proceder del siguiente modo: try { // Creamos el objeto que encapsula el fichero File fichero = new File("c:\\prufba\\miFichero.txt "); // A partir del objeto File creamos el fichero físi camente if (fichero.createNewFile()) System.out.println("El fichero se ha creado c orrectamente"); else System.out.println("No ha podido ser creado el fichero"); } catch (Exception ioe) { ioe.getMessage(); } Para borrar un fichero podemos usar la clase File, comprobando previamente si existe el fichero, del siguiente modo: fichero.delete(); Para crear directorios , podríamos hacer: try { // Declaración de variables String directorio = "C:\\prueba"; String varios = "carpeta1/carpeta2/carpeta3" ; // Crear un directorio boolean exito = (new File(directorio)).mkdir (); if (exito) System.out.println("Directorio: " + director io + " creado"); // Crear varios directorios exito = (new File(varios)).mkdirs(); if (exito) System.out.println("Directorios: " + vari os + " creados"); }catch (Exception e){ System.err.println("Error: " + e.getMessage() ); } Para borrar un directorio con Java, tendremos que borrar cada uno de los ficheros y directorios que éste contenga. Al poder almacenar otros directorios, se podría recorrer recursivamente el directorio para ir borrando todos los ficheros. Se puede listar el contenido del directorio e ir borrando con: File[] ficheros = directorio.listFiles(); Si el elemento es un directorio, lo sabemos mediante el método isDirectory(). 3.- Flujos. Un programa en Java, que necesita realizar una operación de entrada/salida (en adelante E/S), lo hace a través de un flujo o stream . Un flujo es una abstracción de todo aquello que produce o consume información. Java define dos tipos de flujos en el paquete java.io:

• Byte streams (8 bits): proporciona lo necesario para la gestión de entradas y salidas de bytes y su uso está orientado a la lectura y escritura de datos binarios. dos clases abstractas que son InputStream y OutputStream , métodos implementados y, de entre todos, destacan read()y write().

• Character streams (16 bits): de manera similar a los flujos de bytes, dos clases abstractas , en este caso: Reader y Writer . los métodos read() y write().

3.1.- Flujos basados en bytes. Las clases principales que heredan de OutputStream, para la escritura de ficheros binarios son:

• FileOutputStream : escribe bytes en un fichero. Si el archivo existe, cuando vayamos a escribir sobre él, se borrará. Por tanto, si queremos añadir los datos al final de éste, habrá que usar el constructor FileOutputStream(String filePath, boolean append) , poniendo append a true.

• ObjectOutputStream: convierte objetos y variables en vectores de bytes que pueden ser escritos en un OutputStream. • DataOutputStream, que da formato a los tipos primitivos y objetos String, convirtiéndolos en un flujo de forma que cualquier

DataInputStream, de cualquier máquina, los pueda leer. Todos los métodos empiezan por "write", como writeByte(), writefloat (), etc.

De InputStream , para lectura de ficheros binarios, destacamos: • FileInputStream: lee bytes de un fichero. • ObjectInputStream : convierte en objetos y variables los vectores de bytes leídos de un InputStream.

Page 4: Acceso a Datos - Resumen

3.2.- Flujos basados en caracteres. Los BufferedReader, BufferedInputStream, BufferedWriter y BufferedOutputStream añaden un buffer intermedio.En el ejemplo anterior para leer caracteres: private void jButton1ActionPerformed(java.awt.e vent.ActionEvent evt) { // TODO add your handling code here: File archivo = null; FileReader fr = null; BufferedReader br = null; try { // Apertura del fichero y creacion de Buff eredReader para poder // hacer una lectura comoda (disponer del metodo readLine()). archivo = new File ("C:\\archivo.txt"); fr = new FileReader (archivo); br = new BufferedReader(fr); // Lectura del fichero String linea; while((linea=br.readLine())!=null){ System.out.println(linea);

jTextArea1.append(linea + "\n"); } } catch(Exception e){ }finally{ // En el finally cerramos el fichero, para asegurarnos // que se cierra tanto si todo va bien com o si salta // una excepcion. try{ if( null != fr ){ fr.close(); } }catch (Exception e2){ } } }

4.- Formas de acceso a un fichero. Acceso aleatorio. Acceso secuencial. Concatenación (tuberías o "pipes").

4.1.- Operaciones básicas sobre ficheros de acceso secuencial. Grabar datos en fichero secuencial.

private void jButton1ActionPerformed(java.awt.event .ActionEvent evt) { // Declarar un objeto de tipo archivo DataOutputStream archivo = null; // Abir, capturar y grabar datos try { int edad = 0 ; // Creando o abriendo para añadir el archivo archivo = new DataOutputStream( new FileOutputStream("c:\\secuencial.dat",true) ); // Escribir el nombre y los apellidos archivo.writeUTF( jTextField1.getText() ); archivo.writeUTF( jTextField2.getText() ); // Obtener la edad edad = Integer.parseInt(jTextField3.get Text());

// Escribir la edad archivo.writeInt(edad) ; // Cerrar fichero archivo.close(); } catch(FileNotFoundException fnfe) { /* Archivo no encontrado */ } catch (IOException ioe) { /* Error al escribir */ } catch (Exception e) { /* Error de otro tipo*/ System.out.println(e.getMessage());} }

private void jButton1ActionPerformed(java.awt.event .ActionEvent evt) { // Nombre del feichero donde se escribirá la información String fileName = "C:\\datosbinarios.dat" ; try { // Se declara un objeto de tipo DataOu tputStream DataOutputStream fileOut = new DataOutputStream (new FileOutputStream(fileName)) ; // Escribir cadena de texto fileOut.writeUTF(jTextArea1.getText()); // Escribir un entero 5 fileOut.writeInt(5); } catch (IOException ioe) { System.out.print(ioe.getMessage()); } }

Page 5: Acceso a Datos - Resumen

Copiar fichero. La clase espera dos argumentos desde la línea de comandos: nombre del fichero fuente y nombre del fichero donde se hará la copia. Se utilizan las clases FileInputStream y FileOutputStream para leer desde el fichero origen, y copiar al fichero destino, respectivamente. import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class copiar { public static void main(String args[]) { // Clase para leer los datos de un fichero FileInputStream fuente = null; // Clase para escribir los datos a un fichero FileOutputStream destino = null; try { // El fichero fuente es el primer parámetr o fuente = new FileInputStream(args[0]); // El fichero destino es el segundo parámetro. destino = new FileOutputStream(args[1],true );

// Leer del fuente hasta llegar la fin de archivo int i = fuente.read(); while (i != -1) { // mientras not EOF destino.write(i); i = fuente.read(); } // Cerrar los ficheros fuente.close(); destino.close(); }catch (IOException e) { System.out.println("Error en operaciones d e ficheros"); } } }

Streams de salida con buffer Creación de Streams de salida con buffer. FileOutputStream miFileStream; BufferdOutpurStream miBufferStream; // Obtiene un controlador de fichero miFileStream = new FileOutputStream( "/tmp/kk" ); // Encadena un stream de salida con buffer miBufferStream = new BufferedOutputStream( miFi leStream ); Volcado y Cierre de Streams de salida con buffer // Se fuerza el volcado del buffer a disco miBufferStream.flush(); // Cerramos el fichero de datos. Siempre se ha de cerrar primero el // fichero stream de mayor nivel miBufferStream.close(); miFileStream.close(); Streams DataOutput Apertura y cierre de objetos DataOutputStream DataOutputStream miDataStream; FileOutputStream miFileStream; BufferedOutputStream miBufferStream; // Obtiene un controlador de fichero miFileStream = new FileOutputStream( "/tmp/kk" ); // Encadena un stream de salida con buffer (por eficiencia) miBufferStream = new BufferedOutputStream( miFileSt ream ); // Encadena un fichero de salida de datos miDataStream = new DataOutputStream( miBufferStream );

// Ahora se pueden utilizar los dos streams de entrada para // acceder al fichero (si se quiere) miBufferStream.write( b ); miDataStream.writeInt( i ); // Cierra el fichero de datos explícitamente. Siempre se cierra // primero el fichero stream de mayor nivel miDataStream.close(); miBufferStream.close(); miFileStream.close(); Escritura en un objeto DataOutputStream void writeBoolean( boolean b ); void writeByte( int i ); void writeShort( int i ); void writeChar( int i ); void writeInt( int i ); void writeFloat( float f ); void writeDouble( double d ); void writeBytes( String s ); void writeChars( string s ); Contabilidad de la salida . . . int numBytes = miDataStream.size() % 4; for( int i=0; i < numBytes; i++ ) miDataStream.write( 0 ); . . .

Las subclases de Writer y Reader que permiten trabajar con ficheros de texto son: • FileReader, para lectura desde un fichero de texto. Crea un flujo de entrada que trabaja con caracteres en vez de con bytes. • FileWriter, para escritura hacia un fichero de texto. Crea un flujo de salida que trabaja con caracteres en vez de con bytes.

También se puede montar un buffer sobre cualquiera de los flujos que definen estas clases: • BufferedWriter se usa para montar un buffer sobre un flujo de salida de tipo FileWriter. • BufferedReader se usa para montar un buffer sobre un flujo de entrada de tipo FileReader.

Veamos un ejemplo de lectura utilizando un BufferRe ader. public class LeeconBuffer { public static void main(String[] args) { String texto = "" ; try { // Crear el flujo de salida FileReader fichero = null; // Ruta y nombre del fichero fichero = new FileReader("c:\\texto.txt "); // Montar un buffer sobre ese flujo BufferedReader buffer = new BufferedRea der(fichero); // Escribimos el texto usando el metodo readLine() while ((texto = buffer.readLine()) != n ull) { System.out.println(texto); } // Cerrar fichero fichero.close(); } catch (IOException e) { // Escribir el error en la consola System.err.println("Error: " + e); } } } Ahora vamos a ver cómo buscar en un archivo secuenc ial, usando ese mismo ejemplo. Al pinchar en buscar :Acrion Perfonmance private void jButton1ActionPerformed(java.awt.event .ActionEvent evt) { boolean seguir = true ; String nombre = "" ; String apellidos = "" ; int edad = 0 ;

String busqueda = jTextField1.getText() ; try{ // Declarar variable DataInputStream archivo = null; // Abrir el archivo

Page 6: Acceso a Datos - Resumen

archivo = new DataInputStream( new FileInputStream("c:\\secuencial.dat") ); // Leer archivo while (seguir) { // Leer el nombre nombre = archivo.readUTF(); // Si el nombre es el que buscamos if (busqueda.equals(nombre)) { System.out.println("encontr ado"); seguir = false ; jLabel2.setText("¡¡Encont rado registro!!"); }

// Leer los otros campos apellidos = archivo.readUTF(); edad=archivo.readInt(); } // Cerrar fichero archivo.close(); } catch(FileNotFoundException fnfe) { /* Arch ivo no encontrado */ } catch (IOException ioe) { /* Error al escri bir */ } }

4.2.- Operaciones básicas sobre ficheros de acceso aleatorio. Java proporciona una clase RandomAccessFile para este tipo de entrada/salida. La clase posee dos constructores:

• RandomAccessFile(File file, String mode). • RandomAccessFile(String name, String mode).

El modo es: "r" si se abre en modo lectura o "rw" si se abre en modo lectura y escritura. Posee métodos específicos de desplazamiento como seek(long posicion) o skipBytes(int desplazamiento) para poder movernos de un registro a otro del fichero, o posicionarnos directamente en una posición concreta del fichero. Escribir en ficheros de acceso aleatorio. public static void main(String[] args)throws IO Exception { RandomAccessFile miRAFile; String s = "Cadena a escribir2\n"; // Abrimos el fichero de acceso aleatorio miRAFile = new RandomAccessFile( "C:\\aleat orio.bin","rw" ); // Nos vamos al final del fichero miRAFile.seek( miRAFile.length() ); // Incorporamos la cadena al fichero miRAFile.writeBytes( s ); // Cerramos el fichero miRAFile.close(); } 5.- Trabajo con ficheros XML: analizadores sintácti cos (parser) y vinculación (binding). 5.1.- Conceptos previos. 5.2.- Definiciones. ¿Qué es y para qué sirve JAXB (Java Architecture for XML Binding)? JAXB simplifica el acceso a documentos XML representando la información obtenida de los documentos XML en un programa en formato Java. Parsear un documento XML consiste en "escanear" el documento y dividirlo o separarlo lógicamente en piezas discretas. Binding: Binding o vincular un esquema (schema) significa generar un conjunto de clases Java que representan el esquema. Compilador de esquema o schema compiler: liga un esquema fuente a un conjunto de elementos de programa derivados. Binding runtime framework : proporciona operaciones de unmarshalling y marshalling para acceder, manipular y validar contenido XML usando un esquema derivado o elementos de programa. Marshalling: es un proceso de codificación de un objeto en un medio de almacenamiento, normalmente un fichero. Unmarshalling : proporciona a una aplicación cliente la capacidad de convertir datos XML a objetos Java JAXB derivados.

5.3.- Introducción a JAXB. JAXB permite almacenar y recuperar datos en memoria en cualquier formato XML, sin la necesidad de implementar un conjunto específico de rutinas XML de carga y salvaguarda para la estructura de clases del programa.

5.4.- Funcionamiento de JAXB. Para construir una aplicación JAXB necesitamos tener un esquema XML. Tras obtener el esquema XML, seguimos los siguientes pasos para construir la aplicación JAXB:

1. Escribir el esquema : es un documento XML que contiene la estructura que se tomará como indicaciones para construir las clases. 2. Generar los ficheros fuente de Java : para esto usamos el compilador de esquema, ya que éste toma el esquema como entrada de

información. 3. Construir el árbol de objetos Java: con nuestra aplicación, se genera el árbol de objetos java, también llamado árbol de contenido, que

representa los datos XML que son validados con el esquema. Hay dos formas de hacer esto: • Instanciando las clases generadas. • Invocando al método unmarshall de una clase generada y pasarlo en el documento. El método unmarshall toma un documento XML

válido y construye una representación de árbol de objetos. 4. Acceder al árbol de contenido usando nuestra aplicación: ahora podemos acceder al árbol de contenido y modificar sus datos. 5. Generar un documento XML desde el árbol de contenid o. Para poder hacerlo tenemos que invocar al método marshall sobre el

objeto raíz del árbol. Ejemplo de proyecto sencillo con JAXB. Creamos fichero .xsd Vamos a suponer una estructura de un archivo que podría usarse en un almacén distribuidor de medicamentos. El fichero albaran.xsd tiene la estructura que el almacén usa cuando envía un pedido de medicamentos que le ha hecho una farmacia.

Page 7: Acceso a Datos - Resumen
Page 8: Acceso a Datos - Resumen

public class ModificaAlbaPed { public static void main(String[] args) { try { // Crear una instancia de la clase JAXBContext para poder manipular las clases generadas en el paquete jaxb.albaran // La clase JAXBContext proporciona al cliente un punto de entrada a la API JAXB. Facilita una abstracción para manejar la información generada para implementar las operaciones del JAXB binding framework como unmarshal y marshal // unmarshal: consiste en convertir datos XML en un árbol de objetos Java // marshal: consiste en convertir un árbol de objetos Java a datos XML JAXBContext jaxbContext = JAXBContext.newInstanc e("jaxb.albaran"); // Crear un objeto de tipo Unmarshaller para convertir datos XML en un árbol de objetos Java Unmarshaller u = jaxbContext.createUnmarshaller( ); // La clase JAXBElement representa a un elemento de un documento XML en este caso a un elemento del documento albaran.xml JAXBElement jaxbElement = (JAXBElement) u.unmars hal(new FileInputStream("C:\\albaran.xml")); // El método getValue() retorna el modelo de contenido (content model) y el valor de los atributos del elemento PedidoType pedidoType = (PedidoType) jaxbElement .getValue(); // Obtenemos una instancia de tipo PedidoType para obtener un Objeto de tipo Direccion Direccion direccion = pedidoType.getFacturarA(); // Establecemos los datos direccion.setNombre("Jose Javier"); direccion.setCalle("Zafiro 3"); direccion.setCiudad("Molina"); direccion.setProvincia("Murcia"); direccion.setCodigoPostal(new BigDecimal("30500" )); // Crear un objeto de tipo Marshaller para posteriormente convertir un el árbol de objetos Java a datos XML Marshaller m = jaxbContext.createMarshaller(); // El método setProperty(String nombrePropiedad, Object value) recibe en este caso la propiedad "jaxb.formatted.output". Esta propiedad controla si al realizar un marshal, debe formatear el resultado XML con saltos de linea e indentaciones para que las personas podamos leerlo cómodamente. Por defecto su valor es falso es decir el XML creado no está formateado // El argumento value en este caso tiene que ser concretamente de tipo Boolean para indicar si queremos que el resultado XML está formateado o no m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); // El método marshall(Object elementoJAXB, OutputStream os) recibe un objeto de tipo JAXBElement para que su contenido lo muestre en la salida estándar debido a que este método está¡ sobrecargo, si miramos la documentación de la API podemos ver como podemos mostrar o escribir el resultado XML de diferentes maneras m.marshal(jaxbElement, System.out); } catch (JAXBException je) { System.out.println(je.getCause()) ; } catch (IOException ioe) { System.out.println(ioe.getMessage()); } } }

Page 9: Acceso a Datos - Resumen

6.- Librerías para conversión de documentos XML a o tros formatos. En nuestro caso, vamos a optar por una herramienta que permite generar informes de todo tipo en Java de una forma sencilla: JasperReport. 6.1.- Introducción a JasperReport. JasperReports es una herramienta que consta de un poderoso motor para la generación de informes. Descarga de JasperReports e integración en NetBeans .

1. Descargamos y descomprimimos 2. Añadimos a librería: Tools-Libraries…….New Libra ry…..

6.2.- Diseñar y compilar la plantilla. Las plantillas de los informes de JasperReports son sencillamente ficheros XML con la extensión .jrxml. Podemos hacer que NetBeans reconozca este tipo de ficheros como XML, para que cuando los editemos en el editor se muestren los mismos códigos de colores en las etiquetas y demás elementos de la sintaxis de XML. En la imagen se ilustra cómo conseguirlo: en NetBeans pinchamos en el menú Tools, y ahí en Options. Ahí seleccionamos Miscellaneous, luego la pestaña Files. Entonces pulsamos en el botón New... para añadir la nueva extensión.

Page 10: Acceso a Datos - Resumen

Los pasos a seguir para trabajar con JasperReport s erían: Paso 1: Diseñar la plantilla del informe: un ficher o .jrxml. El documento de diseño está representado por un archivo XML que mantiene la estructura de un archivo DTD (Document Type Definition) definido por el motor de JasperReports. La generación de un diseño implica editar un archivo XML validado mediante: <!DOCTYPE jasperReport PUBLIC "-//JasperReports//DT D Report Design//EN" "http://jasperreports.sourceforge.net/dtds/jasperre port.dtd"> Estos documentos XML cuentan con una estructura similar a la de cualquier documento de texto. Fundamentalmente se siguen estas secciones:

title Título del informe. pageHeader Encabezado del documento. columnHeader Encabezado de las columnas. detail Detalle del documento. Cuerpo columnFooter Pie de la columna. pageFooter Pie del documento. sumary Cierre del documento.

Paso 2: Compilación : Una vez que se ha realizado el diseño, se compila antes de poder iniciar el proceso de carga de datos. La compilación se lleva a cabo a través del método compileReport(). En este proceso, el diseño se transforma en un objeto serializable de tipo net.sf.jasperreports.engine JasperReport, que luego se guarda en disco.

6.3.- Rellenar el informe con datos, exportar el in forme. Paso 3: Rellenar el informe con datos: mediante los métodos fillReportXXX(), se puede realizar la carga de datos del informe, Como resultado de este proceso, se obtiene un objeto que representa un documento listo para ser impreso, un objeto serializable de tipo JasperPrint. Este objeto puede guardarse en disco para su uso posterior, o bien puede ser impreso, enviado a la pantalla o transformado en PDF, XLS, CSV, etc. Paso 4: Visualización. Ahora podemos optar por mostrar un informe por pantalla, imprimirlo, o bien obtenerlo en algún tipo específico de fichero, como PDF, etc.

• Para mostrar un informe por pantalla se utiliza la clase JasperViewer, la cual, a través de su método main(), recibe el informe a mostrar.

• Para imprimir el informe usaremos los métodos printReport(), printPage() o printPages (), contenidos en la clase JasperPrintManager.

• Para exportar los datos a un formato de archivo específico podemos utilizar los métodos exportReportXXX(). Ejemplo con JasperReports.

Page 11: Acceso a Datos - Resumen
Page 12: Acceso a Datos - Resumen

Seguimos desarrollando el ejemplo. Ahora vamos a hacer que obtenga datos de una base de datos. En concreto, de la base de datos derby que se incluye al instalar el jdk.

Page 13: Acceso a Datos - Resumen
Page 14: Acceso a Datos - Resumen

Manejo de conectores. 1.- Introducción. Java, mediante JDBC (Java Database Connectivity), permite simplificar el acceso a bases de datos relacionales.

1.1.- El desfase objeto-relacional. consiste en la diferencia de aspectos que existen entre la programación orientada a objetos y la base de datos. impedancia Objeto-Relacional, o sea, el conjunto de dificultades técnicas que surgen cuando una base de datos relacional se usa en conjunto con un programa escrito en POO.

2.- Protocolos de acceso a bases de datos. 2.1.- Arquitectura JDBC. El API JDBC soporta dos modelos de procesamiento para acceso a bases de datos: de dos y tres capas. En el modelo de dos capas , una aplicación se comunica directamente a la fuente de datos. En el modelo de tres capas , los comandos se envían a una capa intermedia de servicios, la cual envía los comandos a la fuente de datos. El API JDBC viene distribuido en dos paquetes:

java.sql, dentro de J2SE. javax.sql, extensión dentro de J2EE.

2.2.- Conectores o Drivers. Un conector o driver es un conjunto de clases encargadas de implementar los interfaces del API y acceder a la base de datos. Hay cuatro tipos de drivers JDBC: Tipo 1, Tipo 2, Tipo 3 y Tipo 4, que veremos a continuación. Código de la estructura para conectarnos, ejecutar consulta y procesar resultados. // Establece la conexión Connection con = DriverManager.getConnectio n ("jdbc:odbc:miBD", "miLogin", "miPassword" ); // Ejecuta la consulta Statement stmt = (Statement) con.createStat ement(); ResultSet rs = stmt.executeQuery("SELECT no mbre, edad FROM Empleados"); // Procesa los resultados while (rs.next()) { String nombre = rs.getString("nombre "); int edad = rs.getInt("edad"); } 2.3.- Conectores tipo 1 y tipo 2. Los conectores tipo 1 se denominan también JDBC-ODBC Bridge (puente JDBC-ODBC). Los conectores tipo 2 se conocen también como: API nativa. Convierten las llamadas JDBC a llamadas específicas de la base de datos para bases de datos como SQL Server, Informix, Oracle, o Sybase. los drivers tipo 2 no pueden usarse para Internet.

2.4.- Conectores tipo 3 y tipo 4. Tipo 3: JDBC-Net pure Java driver. Tiene una aproximación de tres capas. Tipo 4: Protocolo nativo. En este caso se trata de conectores que convierten directamente las llamadas JDBC al protocolo de red usando por el sistema gestor de la base de datos. Un ejemplo de este tipo de conector es Oracle Thin.

3.- Conexión a una base de datos. En Java, para establecer una conexión con una base de datos podemos utilizar el método getConnection() de la clase DriverManager. La ejecución de este método devuelve un objeto Connection que representa la conexión con la base de datos. Si no se encuentra ningún conector adecuado, se lanza una SQLException

3.1.- Instalar el conector de la base de datos. Para que podamos ejecutar el código anterior, necesitamos instalar el conector de la base de datos. En la siguiente presentación vamos a ver cómo descargarnos el conector que necesitamos para trabajar con MySQL. Como verás, tan sólo consiste en descargar un archivo, descomprimirlo y desde NetBeans añadir el fichero .jar que constituye el driver que necesitamos. Instalar conector JDBC para MySQL:

• En esta pagina lo descargamos: http://www.mysql.com/downloads/connector/j/

Page 15: Acceso a Datos - Resumen

• En la pantalla que aparece después, pinchamos en la parte de abajo para poder seleccionar los servidores de descarga donde se aloja el conector JDBC. Ahora, elegimos uno de lo servidores que aparecen y pinchamos en FTP

• En NetBeans, situándonos en el nombre el proyecto pulsamos el botón derecho del ratón.En el menú contextual que aparece

seleccionamos Properties.Seleccionamos el nodo de las Librerías del proyecto.Pinchamos en el botón Add JAR/Folder.Buscamos y elegimos el fichero comentado anteriormente, el .jar.Tan solo queda pulsar Ok y hemos acabado.

3.2.- Pool de conexiones. El proceso de creación y destrucción de una conexión a una base de datos es costoso e influye sensiblemente en el rendimiento de una aplicación. Es mejor en estos casos, por tanto, abrir una o más conexiones y mantenerlas abiertas. La versión 3.0 de JDBC proporciona un pool de conexiones que funciona de forma transparente. El código básico para conectarnos a una base de datos con pool de conexiones transparente a través de JNDI podría ser:

//Initial Context es el punto de entrada para comenzar a explorar un espacio de nombres. javax.naming.Context ctx = new InitialContext(); dataSource = (DataSource) ctx.lookup("java:comp/env /jdbc/Basededatos");

Cada vez que necesitamos realizar una operación tendremos que escribir el siguiente código para obtener una conexión lógica: connection = dataSource.getConnection();

Cuando hayamos finalizado la operación entonces cerraremos la conexión lógica con el siguiente código: connection.close();

4.- Creación de la base de datos. 5.- Operaciones: ejecución de consultas. Podemos utilizar los siguientes tipos de sentencias:

Statement : para sentencias sencillas en SQL. PreparedStatement : para consultas preparadas, como por ejemplo las que tienen parámetros. CallableStatement: para ejecutar procedimientos almacenados en la base de datos.

El API JDBC distingue dos tipos de consultas: Consultas: SELECT Actualizaciones: INSERT, UPDATE, DELETE, sentencias DDL.

5.1.- Ejemplo: consultas con MS-Access. En primer lugar tenemos que definir la fuente de datos ODBC .

instalar el driver JDBC. Dentro del código se carga el driver antes de acced er a la base de datos: Class.forName("jdbc:odbc:admdb"); realizar la conexión a la base de datos para comenz ar a trabajar con ella.

Connection con = DriverManager.getConnection("jdbc: odbc:admdb"); Las consultas que se realizan a una base de datos s e realizan utilizando objetos de las clases Stateme nt y PreparedStatement. Estos objetos se crean a partir de una conexión.

Statement stmt = con.createStatement(); La clase Statement contiene los métodos executeQuer y y executeUpdate para realizar consultas y actuali zaciones. Así por ejemplo, para obtener los nombres de los medicamentos que tenemos en la tabla medicamentos, de la base de datos farm acia.mdb que creamos anteriormente, tendríamos que emplear la sentencia:

ResultSet rs = stmt.executeQuery("SELECT nombre fro m medicamentos"); El método executeQuery devuelve un objeto ResultSet para poder recorrer el resultado de la consulta ut ilizando un cursor. El método next se emplea para hacer avanzar el cursor. Para obtener una columna del registro utilizamos los métodos get. Hay un método get para cada tipo básico Java y para las cadenas.

while (rs.next()) String usuario = rs.getString("nombre");

Comentar que un método interesante del cursor es wasNull que nos informa si el último valor leído con un método get es nulo. Respecto a las consultas de actualización, executeUpdate, retornan el número de registros insertados, registros actualizados o eliminados, dependiendo del tipo de consulta que se trate. import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class Main { public static void main(String[] args) { try {

Page 16: Acceso a Datos - Resumen

// Cargar el driver de Microsoft Access Class.forName("sun.jdbc.odbc.JdbcOdbcDr iver"); // Cadena de conexión para conectar con Access String connectionUrl = "jdbc:odbc:admdb " ; // Obtener la conexión Connection con = DriverManager.getConne ction(connectionUrl); // La clase Statement contiene los métodos executeQuery y executeUpdate para realizar consultas y actualizaciones Statement stmt = con.createStatement(); //El método executeQuery devuelve un objeto ResultSet para poder recorrer el resultado de la consulta utilizando un cursor. // Esta consulta obtiene todos los datos, todos los campos, )debido al *), almacenados en la tabla medicamentos. ResultSet rs = stmt.executeQuery("SELEC T * from medicamentos"); // Mientras queden datos while (rs.next()) { // Imprimir en la consola String codigo = rs.getString("c odigo"); String nombre = rs.getString("n ombre"); String precio = rs.getString("p recio"); String pvp = rs.getString("pvp" ); String unidades = rs.getString( "unidades"); System.out.println(codigo + "-- -" + nombre + "--" +precio+ "--" +pvp+ "--" +unidad es); } } catch (SQLException e) { System.out.println("SQL Exception: "+ e .toString()); } catch (ClassNotFoundException cE) { System.out.println("Excepción: "+ cE.t oString()); } }} 5.2.- Consultas preparadas. Las consultas preparadas están representadas por la clase PreparedStatement. Son consultas precompiladas, por lo que son más eficientes, y pueden tener parámetros. Una consulta se instancia del modo que vemos con un ejemplo:

PreparedStatement pstmt = con.preparedStatement("SE LECT * from medicamentos"); Si hay que emplear parámetros en una consulta, se puede hacer usando el carácter ‘?’. Por ejemplo, para realizar una consulta de un medicamento que tenga un código determinado, haríamos la consulta siguiente:

PreparedStatement pstmt = con.preparedStatement("SE LECT * from medicamentos WHERE codigo = ? "); Establecemos los parámetros de una consulta utilizando métodos set que dependen del tipo SQL de la columna. Así, le decimos que el primer parámetro, que es el único que tiene esta consulta, es “712786”:

pstmt.setString(1, "712786"); Finalmente, ejecutamos la consulta utilizando el método executeQuery() o executeUpdate(), ambos sin parámetros, dependiendo del tipo de consulta. Ejemplo: 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(); }} 6.- Ejecución de procedimientos almacenados en la b ase de datos. Además, estos procedimientos suelen ser de dos clases:

• Procedimientos almacenados. • Funciones, las cuales devuelven un valor que se puede emplear en otras sentencias SQL.

Un procedimiento almacenado típico tiene: • Un nombre. • Una lista de parámetros. • Unas sentencias SQL.

6.1.- Ejecutando procedimentos almacenados en MySQL . A continuación, vamos a realizar un procedimiento almacenado en MySQL, que simplemente insertará datos en la tabla clientes. -Si no tenemos creada la tabla de clientes , la creamos. Por simplicidad, en este ejemplo, trabajamos sobre la base de datos que viene por defecto en MySQL, el esquema denominado: test. Para crear la tabla de clientes, el script correspondiente es:

delimiter $$ CREATE TABLE `clientes` ( `Cod_Cliente` int(3) NOT NULL DEFAULT '0', `Nombre` tinytext, `Telefono` tinytext, PRIMARY KEY (`Cod_Cliente`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1$$

-Creamos el procedimiento almacenado en la base de d atos . Sería tan fácil como lo que ves en el siguiente enlace: DELIMITER $$ CREATE DEFINER=`root`@`localhost` PROCEDURE `insert aCliente`(IN Cod_Cliente INTEGER, IN Nombre TinyTex t,IN Telefono TinyText ) BEGIN INSERT INTO clientes VALUES (Cod_Cliente, Nombre, Telefono); END

Page 17: Acceso a Datos - Resumen

-Crear la clase Java para desde aquí, llamar al pro cedimiento almacenado: public class inserConProcAlma { public static void main(String[] args) { try { // Cargar el driver de mysql Class.forName("com.mysql.jdbc.Driver"); // Cadena de conexión para conectar con MySQL en localhost,seleccionar la base de datos llamada ‘test’ con usuario y contraseña del servidor de MySQL: root y admin String connectionUrl = "jdbc:mysql://lo calhost/test?" + "user=root&passw ord=admin"; // Obtener la conexión Connection con = DriverManager.getConne ction(connectionUrl); // El procedimiento almacenado tendrá tres parámetros CallableStatement prcProcedimientoAlmac enado = con.prepareCall("{ call inserta Cliente(?, ?,?) }"); // cargar parametros en el procedimiento almacenado prcProcedimientoAlmacenado.setInt("Cod_Cliente ", 765); prcProcedimientoAlmacenado.setString("N ombre", "Antonio Pérez") ; prcProcedimientoAlmacenado.setString("T elefono", "950121314") ; // ejecutar el procedimiento prcProcedimientoAlmacenado.execute(); } catch (SQLException e) { System.out.println("SQL Exception: "+ e .toString()); } catch (ClassNotFoundException cE) { System.out.println("Exceción: "+ cE.to String()); } }} Si hemos definido la tabla correctamente, con su clave primaria, y ejecutamos el programa, intentando insertar una fila igual que otra insertada, o sea, con la misma clave primaria, obtendremos un mensaje al capturar la excepción de este tipo:

SQL Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '765' for key 'PRIMARY'

7.- Transacciones. Cuando tenemos una serie de consultas SQL que deben ejecutarse en conjunto, con el uso de transacciones podemos asegurarnos de que nunca nos quedaremos a medio camino de su ejecución.

7.1.- Commit y Rollback. Una transacción tiene dos finales posibles, COMMIT o ROLLBACK . Si se finaliza correctamente y sin problemas se hará con COMMIT, con lo que los cambios se realizan en la base de datos, y si por alguna razón hay un fallo, se deshacen los cambios efectuados hasta ese momento, con la ejecución de ROLLBACK. Por defecto, al menos en MySQL o con Oracle, en una conexión trabajamos en modo autocommit con valor true. Eso significa que cada consulta es una transacción en la base de datos. Por tanto, si queremos definir una transacción de varias operaciones, estableceremos el modo autocommit a false con el método setAutoCommit de la clase Connection. Puedes ver un ejemplo sencillo de cómo se puede utilizar commit y rollback: tras las operaciones se realiza el commit, y si ocurre una excepción, al capturarla realizaríamos el rollback. BEGIN … SET AUTOCOMMIT OFF update cuenta set saldo=saldo + 250 where dni=”1234 5678-L”; update cuenta set saldo=saldo - 250 where dni=”8900 9999-L”; COMMIT; … EXCEPTION WHEN OTHERS THEN ROLLBACK ; END; 8.- Excepciones y cierre de conexiones. 8.1.- Excepciones. Cuando se produce un error durante la ejecución de un programa, se genera un objeto asociado a esa excepción. try { // Bloque de instrucciones del try }catch (FileNotFoundException fnfe){ // Bloque para excepción por fichero no encontrado }catch(IOException ioe){ // Bloque para excepción por error de entrada salida }catch(SQLException sqle){ // Bloque para excepción por error con SQL }catch(Exception e){ }finally{ //Instrucciones finales para, por ejemplo, limpieza }

Page 18: Acceso a Datos - Resumen

8.2.- Cierre de conexiones. Resulta totalmente conveniente cerrarlas con el método close cuando ya no se utilizan.

Page 19: Acceso a Datos - Resumen

Mapeo objeto relacional. 1.- Concepto de Mapeo objeto-relacional(ORM). Es una técnica de programación que se utiliza con el propósito de convertir datos entre el utilizado en un lenguaje de programación orientado a objetos y el utilizado en una base de datos relacional, gracias a la la persistencia. traducir los objetos a formas que puedan ser almacenadas en bases de datos preservando las propiedades de los objetos y sus relaciones; estos objetos se dice entonces que son persistentes .

2.- Herramientas ORM. Características y herramienta s más utilizadas. Object Relational Mapping (ORM) es la herramienta que nos sirve para transformar representaciones de datos de los Sistemas de Bases de Datos Relacionales, a representaciones (Modelos) de objetos. En el modelo relacional, cada fila en la tabla se mapea a un objeto y cada columna a una propiedad.

2.1.- Características. Una herramienta ORM permite tomar un objeto Java y hacerlo persistente, carga el objeto de la base de datos a memoria y permite hacer consultas a las tablas de la base de datos. Ventajas de ORM.

• Ayudan a reducir el tiempo de desarrollo de software. • Abstracción de la base de datos. • Reutilización. • Permiten persistir objetos a través de un método Orm.Save y generar el SQL correspondiente. • Permiten recuperar los objetos persistidos a través de un método Orm.Load. • Lenguaje propio para realizar las consultas. • Independencia de la base de datos. • Incentivan la portabilidad y escalabilidad de los programas de software.

Desventajas de ORM. • Tiempo utilizado en el aprendizaje. • Menor rendimiento (aplicaciones algo más lentas). • Sistemas complejos.

2.2.- Herramientas ORM más utilizadas. Hibernate: para la plataforma Java. Utiliza archivos declarativos (XML) Java Persistence Api (JPA): es una especificación de Sun Microsystems para la persistencia de objetos Java a cualquier base de datos relacional. iBatis: iBatis es un framework de persistencia desarrollado por la Apache software Foundation.

3.- Instalación y configuración de Hibernate. El archivo de configuración de Hibernate recibe el nombre de Hibernate.cfg.xml y contiene información sobre la conexión de la base de datos y otras propiedades. 4.- Ficheros de configuración y mapeo. Estructura y propiedades. Para utilizar Hibernate necesitamos tener una base de datos relacional instalada en NetBeans. La base de datos Sakila es una muestra gratuita disponible, como plugin, en NetBeans. 4.1.- Ficheros de configuración. Propiedades. El archivo de configuración de Hibernate es el Hibernate.cfg.xml y contiene información sobre la conexión de base de datos, las asignaciones de recursos y otras propiedades de conexión.

Page 20: Acceso a Datos - Resumen
Page 21: Acceso a Datos - Resumen

Las propiedades más importantes del fichero Hibernate.cfg.xml son:

• Hibernate.dialect : Dialecto o lenguaje empleado. Por ejemplo, MySQL. • Hibernate.connection.driver_class . Driver utilizado para la conexión con la base de datos. • Hibernate.connection.url . Dirección de la base de datos con la que se va a conectar Hibernate. • Hibernate.connection.username . Nombre del usuario que va a realizar la extracción de información. Por defecto, el nombre de

usuario es root. • Hibernate.connection.password . Contraseña el root. • Hibernate.show_sql . Para mostrar la herramienta. Por defecto, su valor es true.

4.2.- Ficheros de mapeo. Estructura, elementos y pr opiedades. Hibernate utiliza ficheros de mapeo para relacionar tablas con objetos Java, estos ficheros están en formato XML y que tienen extensión .hbm.xml. Para extraer un tabla concreta de la base de datos, la sintaxis en el mapeo requiere definir el POJO Nombre_Clase.hbm.xml, donde nombre_clase se corresponderá con el nombre la tabla que queremos extraer y donde se describe cómo se relacionan clases y tablas y propiedades y columnas. Podemos utilizar la ingeniería inversa para crear archivos de mapeo basados en tablas de la base de datos que seleccionemos. El archivo de ingeniería inversa es Hibernate.reveng.xml. Mediante el asistente de NetBeans, seleccionamos Nuevo Mapeo en Hibernate y rellenamos los campos que nos piden, en función de la información que nos interese extraer de la base de datos. De esta manera, NetBeans genera un POJO nombre_clase.java (siendo nombre_clase aquella tabla cuya información nos interesa extraer de la base de datos) con todos los campos necesarios.

Page 22: Acceso a Datos - Resumen

5.- Mapeo de colecciones, relaciones y herencia. 1. Mapeo de colecciones. Hay bastantes rangos de mapeos que se pueden generar para colecciones que cubran diversos modelos relacionales.

2. Mapeo de relaciones. Para mapear las relaciones, se usan los identificadores de objetos (OID). Son la llave primaria de la tabla relacionada y se agregan como una columna más en la tabla donde se quiere establecer la relación.

3. Mapeo de herencia. Para el caso de la herencia se presenta el problema que las base de datos relacionales no la soportan. Así es que somos nosotros quienes debemos modelar como se verá la herencia en el modelo relacional. 6.- Clases persistentes. Las clases persistente son clases en una aplicación que nos van a servir para representar entidades de la base de datos. Para poder indicar las clases y atributos que son persistentes, se utiliza un fichero de configuración XML, que se denomina descriptor de persistencia.

7.- Sesiones; estados de un objeto. Para poder utilizar la persistencia en Hibernate es necesario definir un objeto Session utilizando la clase SesssionFactory . Los estados en los que se puede encontrar un objetos son:

• Transitorio (Transient). En este estado estará un objeto recién creado que no ha sido enlazado con el gestor de persistencia. • Persistente: Ente este caso el objeto está enlazado con la sesión. Todos los cambios que se realicen será persistentes. • Disociado (Detached): En este caso nos encontramos con un objeto persistente que sigue en memoria después de que termine la

sesión. En este caso existe en Java y en la base de datos. • Borrado (Removed): En esta caso el objeto está marcado para ser borrado de la base de datos. Existe en la aplicación Java y se

borrará de la base de datos al terminar la sesión.

8.- Carga, almacenamiento y modificación de objetos . Para cargar un objeto de acceso a datos en la aplicación Java, el método load() de la clase Session suministra un mecanismo para capturar una instancia persistente, si conocemos su identificador. El método load() acepta un objeto Class, y cargará el estado de una nueva instancia de esa clase, inicializada en estado persistente. El método load() lanzará una excepción irrecuperable si no existe la fila de base de datos correspondiente. Si no se está seguro de que exista una fila correspondiente, debe usarse el método get(), el cual consulta la base de datos inmediatamente y devuelve null si no existe una fila correspondiente.

8.1.- Almacenamiento y modificando de objetos persi stentes. Para almacenar objetos persistentes se proceso siguiendo los siguientes pasos:

1. Se instancia un objeto nuevo (estado transitorio). 2. Se obtiene una sesión y se comienza la transacción, inicializando el contexto de persistencia. 3. Una vez obtenida da la sesión, se llama al método save(), el cual introduce el objeto en el contexto de persistencia. Este método

devuelve el identificador del objeto persistido. 4. Para que los cambios sean sincronizados en las bases de datos, es necesario realizar el commit de la transacción. Dentro del objeto

sesión se llama al método flush(). Es posible llamarlo explícitamente. En este momento, se obtiene la conexión JDBC a la bases de datos para poder ejecutar la oportuna sentencia.

5. Finalmente, la sesión se cierra, con el objeto de liberar el contexto de persistencia, y por tanto, devolver la referencia del objeto creado al estado disociado.

Para borrar objetos persistentes, podemos ejecutar Session.delete(), que quitará el estado de un objeto de la base de datos.

9.- Consultas SQL. Usando Hibernate, la ejecución de consultas SQL nativas se controla por medio de la interfaz SQLQuery, la cual se obtiene llamando a Session.createSQLQuery().La consulta SQL más básica es para obtener a una lista de escalares (valores) . sess.createSQLQuery("SELECT * FROM Personas").list( ); sess.createSQLQuery("SELECT ID,NOMBRE, EDAD FROM PE RSONAS").list(); Estas retornarán una lista de objetos arrays (Object[]) con valores escalares para cada columna en la tabla PERSONAS. Otro tipo de consulta más compleja, es la consulta de entidades. Para obtener los objetos entidades desde una consulta sql nativa, se utiliza por medio de addEntity(). sess.createSQLQuery("SELECT * FROM PERSONAS").addEn tity(Persona.class); sess.createSQLQuery("SELECT ID,NOMBRE,EDAD FROM PER SONAS").addEntity(Persona.class); 10.- Lenguajes propios de la herramienta ORM. Hibernate utiliza un lenguaje de consulta potente (HQL) que se parece a SQL. HQL es completamente orientado a objetos y comprende nociones como herencia, polimorfismo y asociación. Entre las características más importantes de HQL.

Page 23: Acceso a Datos - Resumen

• Soporte completo para operaciones relacionales: HQL permite representar consultas SQL en forma de objetos. HQL usa clases y atributos o propiedades en vez de tablas y columnas.

• Regresa sus resultados en forma de objetos: Las consultas realizadas usando HQL regresan los resultados de las mismas en la forma de objetos o listas de objetos, que son más fáciles de usar.

• Consultas Polimórficas: Podemos declarar el resultado usando el tipo de la superclase e Hibernate se encargara de crear los objetos adecuados de las subclases correctas de forma automática.

• Soporte para características avanzadas: HQL contiene muchas características avanzadas que son muy útiles y que no siempre están presentes en todas las bases de datos, o no es fácil usarlas, como paginación, fetch joins con perfiles dinámicos, inner y outer joins, etc. Además soporta proyecciones, funciones de agregación (max, avg), y agrupamientos, ordenamientos, y subconsultas.

• Independiente del manejador de base de datos: Las consultas escritas en HQL son independientes de la base de datos (siempre que la base de datos soporte la característica que estamos intentando utilizar ^

10.1.- Lenguaje HQL. Clausula from: la siguientes sería una consulta que mostraría todos los datos de una tabla de nombre Alumnos: from Alumnos Cláusula select: La cláusula select escoge qué objetos y propiedades devolver en el conjunto de resultados de la consulta. Un ejemplo de consulta podría ser select alumno.nombre from Alumnos alumno where alu mno.nombre like 'A%' La cláusula where: La cláusula where nos permite refinar la lista de instancias retornadas. Si no existe ningún alias, puede referirse a las propiedades por nombre: from Alumnos where nombre='Francisco'. Si existe un alias, usaremos un nombre de propiedad calificado: from Alumnos as alumnos where alumnos.nombre='Francisco' . Esto retorna instancias de Alumnos llamados "Francisco". Funciones de agregación . Las consultas HQL pueden retornar resultados de funciones de agregación sobre propiedades: select avg(alumnos.nota), sum(alumnos.nota), max(alumnos.n ota), count(alumnos) from Alumnos alumnos. Expresiones . Las expresiones utilizadas en la cláusula where incluyen lo siguiente: operadores matemáticos, operadores de comparación binarios, operadores lógicos , paréntesis ( ) que indican agrupación, funciones Java, etc. La cláusula order by . La lista retornada por una consulta se puede ordenar por cualquier propiedad de una clase retornada o componentes. La palabra asc o desc opcionales indican ordenamiento ascendente o descendente respectivamente. La cláusula group by . Una consulta que retorna valores agregados se puede agrupar por cualquier propiedad de una clase retornada o componentes: Subconsultas . Para bases de datos que soportan subconsultas, Hibernate soporta subconsultas dentro de consultas. Una subconsulta se debe encerrar entre paréntesis (frecuentemente por una llamada a una función de agregación SQL). Incluso se permiten subconsultas correlacionadas (subconsultas que se refieren a un alias en la consulta exterior).

Page 24: Acceso a Datos - Resumen

public static String CONSULTA_BASADA_EN_NOMBRE="fro m Actor a where a.firstName like '"; public static String CONSULTA_BASADA_EN_APELLIDO="f rom Actor a where a.lastName like '";

private void ejecutaConsultaBasadaEnNombre(){ ejecutaHQLConsulta(CONSULTA_BASADA_EN_NOMBRE+tx tNombre.getText()+"%'"); } private void ejecutaConsultaBasadaEnApellidos(){ ejecutaHQLConsulta(CONSULTA_BASADA_EN_APELLIDO+ txtApellidos.getText()+"%'"); } private void ejecutaHQLConsulta(String hql){ try{ Session session=HibernateUtil.getSessionFactory ().openSession(); session.beginTransaction(); Query consulta=session.createQuery(hql); List resultList=consulta.list(); mostrarResultado(resultList); session.getTransaction().commit(); } catch (HibernateException he){ he.printStackTrace(); } }

Page 25: Acceso a Datos - Resumen

private void btConsultaActionPerformed(java.awt.eve nt.ActionEvent evt) { // TODO add your handling code here: if(!txtNombre.getText().trim().equals("")){ ejecutaConsultaBasadaEnNombre(); }else if(!txtApellidos.getText().trim().equals( "")){ ejecutaConsultaBasadaEnApellidos(); } }

private void mostrarResultados(List resuList){ Vector<String> tableHeaders=new Vector<String>( ); Vector tableData=new Vector(); tableHeaders.add("Actor id"); tableHeaders.add("Nombre"); tableHeaders.add("Apellidos"); tableHeaders.add("Actualizado"); for (Object o:resuList){ Actor actor=(Actor) o; Vector <Object> oneRow=new Vector<Object>() ; oneRow.add(actor.getActorId()); oneRow.add(actor.getFirstName()); oneRow.add(actor.getLastName()); oneRow.add(actor.getLastUpdate()); tableData.add(oneRow); } tbResultados.setModel(new DefaultTableModel(tab leData, tableHeaders)); }

11.- Gestión de transacciones. Un transacción es un conjunto de órdenes que se ejecutan formando un unidad de trabajo, en forma indivisible o atómica. Session session = HibernateUtil.getSessionFactory() .openSession(); Transaction tx = null; try { tx = session.beginTransaction(); // Utilizar la Session para saveOrUpdate/get/delete/...tx.commit(); } catch (Exception e) { if (tx != null) { tx.rollback(); throw e; } } finally { session.close(); }// Al finalizar la aplicación ...HibernateUtil.shutdown( ); Cuando se crea el objeto Session , se le asigna la conexión de la base de datos que va a utilizar. Una vez obtenido el objeto Session , se crea una nueva unidad de trabajo ( Transaction) utilizando el método beginTransaction. Dentro del contexto de la transacción creada, se pueden invocarlos métodos de gestión de persistencia proporcionados por el objeto Session, para recuperar, añadir, eliminar o modificar el estado de instancias de clases persistentes. También se pueden realizar consultas. Si las operaciones de persistencia no han producido ninguna excepción, se invoca el método commit de la unidad de trabajo para confirmar los cambios realizados. En caso contrario, se realiza un rollback para deshacer los cambios producidos. Sobre un mismo objeto Session pueden crearse varias unidades de trabajo. Finalmente se cierra el objeto Session invocando su método close.

Page 26: Acceso a Datos - Resumen

Bases de datos objeto-relacionales y orientadas a o bjetos. 1.- Introducción. Las Bases de Datos Orientadas a Objetos (BDOO) o Bases de Objetos se integran directamente y sin problemas con las aplicaciones desarrolladas en lenguajes orientados a objetos, ya que soportan un modelo de objetos puro y son ideales para almacenar y recuperar datos complejos permitiendo a los usuarios su navegación directa (sin un mapeo entre distintas representaciones). Las Bases de Datos Objeto-Relacionales (BDOR ) son bases de datos relacionales que han evolucionado hacia una base de datos más extensa y compleja , incorporando conceptos del modelo orientado a objetos. Pero en estas bases de datos aún existe un mapeo de objetos subyacente, que es costoso y poco flexible, cuando los objetos y sus interacciones son complejos.

2.- Características de las bases de datos orientada s a objetos. La principal característica de las BDOO es que soportan un modelo de objetos puro y que el lenguaje de programación y el esquema de la base de datos utilizan las mismas definiciones de tipos. Soportan las características propias de la Orientación a Objetos. Identificador de objeto (OID). Cada objeto tiene un identificador. Jerarquía y extensión de tipos. Se pueden definir nuevos tipos basándose en otros tipos predefinidos. Objetos complejos. Acceso navegacional de datos. Gestión de versiones.

2.1.- Ventajas e inconvenientes. Ventajas : la transparencia, Gran capacidad de modelado. Flexibilidad. Soporte para el manejo de objetos complejos. Alta velocidad de procesamiento. Extensibilidad. Mejora los costes de desarrollo, Facilitar el control de acceso y concurrencia, Funcionan de forma eficiente en entornos cliente/servidor y arquitecturas distribuidas. Desventajas : Carencia de un modelo de datos universal. Falta de estándares. Complejidad. Competencia de otros modelos. Difícil optimización de consultas.

3.- Gestores de bases de datos orientadas a objetos . • Db4o de Versant. Es una BDOO Open Source para Java y .NET. Se distribuye bajo licencia GPL. • Matisse . Es un SGBOO basado en la especificación ODMG, proporciona lenguajes para definición y manipulación de objetos, así

como interfaces de programación para C, C++, Eiffel y Java. • ObjectDB . Es una BDOO que ofrece soporte para Java, C++, y Python entre otros lenguajes. No es un producto libre, aunque

ofrecen versiones de prueba durante un periodo determinado. • EyeDB . Es un SGBOO basado en la especificación ODMG, proporciona lenguajes para definición y manipulación de objetos, e

interfaces de programación para C++ y Java. Se distribuye bajo licencia GNU y es software libre. • Neodatis, ObjectStore y GemStone . Son otros SGBDOO.

3.1.- Objetos simples y objetos estructurados. Un objeto de tipo simple u objeto simple es aquel que no contiene a otros objetos y por tanto posee una estructura de un solo nivel de profundidad en este sentido.Un objeto de tipo estructurado u objeto estructurado incluye entre sus componentes a otros objetos. Entre un objeto y sus componentes de cada nivel, existen dos tipos de referencia:

• Referencia de propiedad. Se aplica cuando los componentes de un objeto se encapsulan dentro del propio objeto y se consideran, por tanto, parte de ese objeto. Relación es-parte-de.

• Referencia de asociación. Se aplica cuando entre los componentes del objeto estructurado existen objetos independientes, pero es posible hacer referencia a ellos desde el objeto estructurado. Relación está-asociado-con.

Una relación uno a muchos se representa mediante un objeto tipo colección (List, Set, etc.).

3.2.- Instalación del gestor de objetos Db4o. descargar de www.db4o.com

Page 27: Acceso a Datos - Resumen

4.- El API de la base de objetos. Los principales paquetes del API de Db4o son los siguientes:

• com.db4o. Paquete principal (core) de la Base de Objetos. • com.db4o.query. Paquete con funcionalidades de consulta. • om.db4o.config. Paquete con funcionalidades de configuración.

La documentación del API viene en formato JavaDoc y la puedes encontrar en el directorio /doc/api del fichero .zip descargado y descomprimido. Siempre que trabajemos con bases de objetos Db4o utilizaremos el interface ObjectContainer , puesto que es quien representará a la base de objetos, sea embebida o no.

4.1.- Apertura y cierre de conexiones. En general, la conexión de una aplicación Java con una base de objetos se podrá realizar vía:

• JDBC. • El API proporcionado por el propio gestor de objetos.

Abrir conexión. Podemos utilizar las siguientes clases: • Db4oEmbedded . Es una clase que hereda de java.lang.Object y proporciona métodos estáticos como openFile() para abrir una

instancia de la base de datos en modo embebido. • ObjectServer. Es un interfaz que permite trabajar con una base de datos db4o en modo cliente-servidor. Una vez abierta la base

de datos como servidor mediante Db4o.openServer(), el método openClient() del interfaz ObjectServer permitirá abrir conexiones cliente directamente en memoria o bien mediante TCP/IP.

Cerrar conexión . Para ello utilizaremos el método close() de la interfaz ObjectContainer.

Page 28: Acceso a Datos - Resumen

package congreso; public class ponente { private String nif; private String nombre; private String email; private float cache; //constructores public ponente() { this.nif=null; this.nombre = null; this.email = null; this.cache = 0; } public ponente(String ni, String n, String e) { this.nif=ni; this.nombre = n; this.email = e; this.cache = -1; //caché no asignado } public ponente(String ni, String no, String e, fl oat c) { this.nif=ni; this.nombre = no; this.email = e; this.cache = c; } //métodos básicos para asignar y obtener valores de atributos public void setNif(String n) { this.nif = n; } public String getNif() { return this.nif;

} public void setNombre(String n) { this.nombre = n; } public String getNombre() { return this.nombre; } public void setEmail(String e) { this.email = e; } public String getEmail() { return this.email; } public void setCache(float c) { this.cache = c; } public float getCache() { return this.cache; } @Override //comportamiento del método toString heredado de la superclase Objet //Devuelve los atributos de un objeto ponente public String toString() { if (this.cache != -1) { return this. nif+" "+this.nombre+" "+this.ema il+" Caché:"+this.cache; } else { return this.nif+" "+this.nombre +" "+this.ema il; } } }

package congreso; import com.db4o.Db4oEmbedded; import com.db4o.ObjectContainer; public class Main { public static void main(String[] args) { // TODO code application logic here ObjectContainer db = Db4oEmbedded.openFile( Db4oEmbedded.newConfiguration(),"congreso.db4o"); //La base de datos física es el fichero "congreso.db4o" almacenado en la //carpeta raíz del proyecto creado try { almacenarPonentes(db); } finally { db.close(); //cerrar la conexión a la base de datos } }

Page 29: Acceso a Datos - Resumen

//Método para almacenar datos en la Base de Objetos. public static void almacenarPonentes(ObjectContai ner db) { //se crean cuatro objetos tipo alumno con valores asignados ponente p1 = new ponente("11A", "Antonio Camaco ", "[email protected]", 300); ponente p2 = new ponente("22B","Isabel Pérez", "[email protected]", 100); ponente p3 = new ponente("33C","Ana Navarro", " [email protected]", 200); ponente p4 = new ponente("44D","Pedro Sánchez", "[email protected]", 90); //Persistir Objetos: almacenamos los objetos con el método store() db.store(p1); db.store(p2); db.store(p3); db.store(p4); }

4.2.- Consultas a la base de objetos. A una BDOO se podrán realizar consultas mediante:

• Un lenguaje de consultas como OQL , si el gestor está basado en el estandar ODMG e incluye sentencias del tipo SQL. • El API proporcionado por el propio sistema gestor de bases de datos orientadas a objetos.

Los tres sistemas de consulta que proporciona Db4o basados en el API del propio gestor, son los siguientes: • Consultas por ejemplo. Query By Example (QBE). Es la forma más sencilla y básica de realizar cosultas, pero tienen bastantes

limitaciones. • Consultas nativas. Native Queries (NQ). Es la interface principal de consultas de la base de objetos. Permiten realizar un filtro

contra todas las instancias de la base de objetos. • Consultas SODA. Simple Object Data Access (SODA ). Permite generar consultas dinámicas.

public static void consultarPonentes(ObjectContaine r db) { ponente p = new ponente(null, null, null, 0); / /prototipo de búsqueda ObjectSet res = db.queryByExample(p); //realiza ción de consulta mostrarConsulta(res);//obtención de resultados }

//Método para mostrar objetos recuperados de la Base de Objetos public static void mostrarConsulta(ObjectSet resu l) { System.out.println("Recuperados " + resul.size( ) + " Objetos"); while (resul.hasNext()) { System.out.println(resul.next()); } }

Page 30: Acceso a Datos - Resumen

//consulta de un ponente en concreto. Consultar ponentes de cache 200. public static void consultarPonente200(ObjectCont ainer db) { ponente p = new ponente(null, null, null, 200); ObjectSet res = db.queryByExample(p); mostrarConsulta(res); }

//consulta de ponentes por nombre. Al método se le pasa el parámetro nomb public static void consultarPonentePorNombre(Obje ctContainer db, String nomb) { ponente p = new ponente(null, nomb, null, 0);// prototipo de búsqueda ObjectSet res = db.queryByExample(p); mostrarConsulta(res); }

//Método para consultar todos los ponentes public static void consultaSODAponentes(ObjectCon tainer db) { Query query = db.query();//declara un objeto Qu ery //indica la clase a la que se aplicarán restricciones query.constrain(ponente.class); ObjectSet result = query.execute(); //Ejecuta l a consulta mostrarConsulta(result);//muestra los resultado s de la consulta }

//Consulta SODA de todos los ponentes con caché 200 public static void consultaSODAcache200(ObjectConta iner db) { Query query = db.query(); //se declara objeto t ipo Query() query.constrain(ponente.class); //clase a la qu e se aplican restricciones //establece restricción del valor 200 para cache query.descend("cache").constrain(200); ObjectSet result = query.execute(); //ejecuta c onsulta mostrarConsulta(result);//muestra resultados de consulta }

Page 31: Acceso a Datos - Resumen

//consulta SODA de ponentes ordenados por caché, de más a menos public static void consultaSODAponentesOrdenadosC ache(ObjectContainer db) { Query query = db.query(); query.constrain(ponente.class); query.descend("cache").orderDescending(); ObjectSet result = query.execute(); mostrarConsulta(result); } MEtodo Main public static void main(String[] args) { //Conexión, apertura o creación de la base de objetos congreso.db4o ObjectContainer db = Db4oEmbedded.openFile(Db4oEmbedded.newConfiguration (), "congreso.db4o"); //operaciones sobre la base de objetos congreso try { almacenarPonentes(db); System.out.println("CONSULTAS QBE: ******************************"); System.out.println("TODOS LOS PONENTES: ***** "); consultarPonentes(db); System.out.println("PONENTES con caché 200: * ****"); consultarPonente200(db); System.out.println("PONENTE de nombre Antonio Camaco: *****"); consultarPonentePorNombre(db, "Antonio Camaco "); System.out.println("CONSULTAS NQ: *******************************");

System.out.println("PONENTES con caché 200: * ****"); consultarPonenteNQcache200(db); System.out.println("CONSULTAS SODA: *****************************"); System.out.println("TODOS LOS PONENTES: ***** "); consultaSODAponentes(db); System.out.println("PONENTES con caché 200: * ****"); consultaSODAcache200(db); System.out.println("PONENTES con caché entre 50 y 200: *****"); consultaSODAcacheEntre50_200(db); System.out.println("TODOS los PONENTES de más a menos caché: *****"); consultaSODAponentesOrdenadosCache(db); } catch (Exception e) { //código para el tratamiento de la excepción } finally { db.close(); // cerrar la base de datos antes de salir } }

4.3.- Actualización de objetos simples. Para modificar objetos almacenados debes seguir los siguientes pasos:

• Cambiar los valores del objeto con los nuevos valores. • Almacenar de nuevo el objeto con el método store() de la interfaz ObjectContainer.

Db4o necesita conocer previamente un objeto para poder actualizarlo. Esto significa que para poder ser actualizados los objetos, éstos deben de haber sido insertados o recuperados en la misma sesión; en otro caso se añadirá otro objeto en vez de actualizarse. Para eliminar objetos almacenados utilizaremos el método delete() de la interface ObjectContainer . //método que modifica el e-mail de un ponente cuyo nif se pasa como parámetro // y almacena en la base de objetos los nuevos valores public static void actualizarEmailPonente(ObjectC ontainer db, String nif, String em) { //se consulta a la base de objetos por el ponente del nif indicado

//consulta SODA de ponentes con cache entre 50 y 200 public static void consultaSODAcacheEntre50_200(ObjectContainer db) { Query query = db.query(); query.constrain(ponente.class); //se declara una de las restricciones con Constraint Constraint constra1 = query.descend("cache").constrain(200).smaller(); //se enlazan las dos restricciones a aplicar query.descend("cache").constrain(50).greater().and( constra1); ObjectSet result = query.execute(); mostrarConsulta(result); }

//consultar los ponentes de caché 200 public static void consultarPonenteNQcache200(ObjectContainer db) { List res = db.query(new com.db4o.query.Predicat e() { public boolean match(ponente p) { return p.getCache() == 200; } //método abstracto @Override public boolean match(Object et) { throw new UnsupportedOperationException("No t supported yet."); } }); mostrarConsulta((ObjectSet) res); }

Page 32: Acceso a Datos - Resumen

ObjectSet res = db.queryByExample(new ponente(n if, null, null, 0)); ponente p = (ponente) res.next(); //se obtiene el objeto consultado en p p.setEmail(em); //se cambia el email del objeto db.store(p); //se alamcena de nuevo el objeto p oenente } public static void borrarPonenteporNif(ObjectContai ner db, String nif) { //se consulta a la base de objetos por el ponente del nif indicado ObjectSet res = db.queryByExample(new ponente(n if, null, null, 0)); ponente p = (ponente) res.next(); //se obtiene el objeto consultado en p db.delete(p); //se elimina el objeto poenente d e la base de objetos } Y en el Main public static void main(String[] args) { ObjectContainer db = Db4oEmbedded.openFile(Db4o Embedded.newConfiguration(), "congreso.db4o"); try { almacenarPonentes(db); consultarPonentes(db); consultarPonente200(db); actualizarEmailPonente(db, "22B", "isaperez@g mail.com"); borrarPonenteporNif(db, "11A"); consultarPonentes(db); } catch (Exception e) { //código para el tratamiento de la excepción } finally { db.close(); // cerrar la base de datos antes de salir } } 4.4.- Actualización de objetos estructurados. Los objetos estructurados son objetos que contienen a su vez a otros objetos (objetos hijo u objetos miembro). Los objetos estructurados se almacenan asignando valores con set() y después persistiendo el objeto con store(). La eliminación o borrado de un objeto estructurado se realiza mediante el método delete(). Para eliminar objetos estructurados en cascada o de forma recursiva, eliminando los objetos miembro, habrá que configurar de modo apropiado la base de objetos antes de abrirla, mediante el paquete com.db4o.config. En el caso de modo embebido, se hará mediante la interface EmbeddedConfiguration. En la nueva configuración se debe indicar cascadeOnDelete(true ). Para actualizar de forma recursiva todos los objeto miembro habrá que indicar en la configuración cascadeOnUpdate(true). Clase estructurada Charla, formada por otra clase e n segundo nivel, Ponente: package actualizarcongresoestructurados; //clase que implementa un charla. Cada charla tiene un título y está asignada //a un ponente. CLASE ESTRUCTURADA: contiene un objeto ponente public class charla { private String titulo; private float duracion; private ponente pl; //constructor public charla(String ti, float h) { this.titulo = ti; this.pl = null; this.duracion=h; } //Método para obtener el ponente de una charla public ponente getPonente() { return pl; } //Método para asignar el ponente de una charla public void setPonente(ponente p) {

this.pl = p; } //Método para obtener el título de una charla public String getTitulo() { return titulo; } //Método para obtener la duración de una charla public float getDuracion() { return duracion; } //Método para asignar el ponente de una charla public void setDuracion(float h) { this.duracion = h; } //Método para mostrar título y ponente de una charla @Override public String toString() { return "Charla: " + titulo + ", " + duracion + " horas. PONENTE: " +pl ; } }

Clase Principal para objetos estructurados, para co nsultas y actualizaciones package actualizarcongresoestructurados; //API necesaria de db4o import com.db4o.Db4oEmbedded; import com.db4o.ObjectContainer; import com.db4o.ObjectSet; import com.db4o.query.Query; public class Main { public static void main(String[] args) { //Conexión a la base de objetos y apertura de la base de objetos congreso.db4o ObjectContainer db = Db4oEmbedded.openFile(Db4oEmbedded.newConfiguration (), "congreso.db4o"); //llamada a métodos para operar con la base de objetos try { almacenarCharlas(db); System.out.println("CHARLAS EXISTENTES EN LA BDOO:"); mostrarCharlasQBE(db); System.out.println("CHARLAS EXISTENTES con tí tulo XML:"); consultaSODACharla_concreta(db, "XML"); System.out.println("CHARLAS EXISTENTES del po nente ANTONIO CAMACO:"); mostrarCharlasCamacoQBE(db);

System.out.println("ELIMINACIÓN de la charla XML:"); borrarCharlaporTitulo(db, "XML"); System.out.println("CHARLAS Y PONENTES EXISTE NTES tras borrar charla XML:"); mostrarCharlasQBE(db); mostrarPonentes(db); System.out.println("MODIFICACIÓN de las horas de la CHARLA Db4o:"); actualizarHorasCharla(db, "Db4o", 8.0f); consultaSODACharla_concreta(db, "Db4o"); } catch (Exception e) { //código para el tratamiento de la excepción } finally { db.close(); // cerrar la base de datos antes de salir } } //Almacenar objetos estructurados //Método para insertar charlas en la Base de Objetos y almacenarlas public static void almacenarCharlas(ObjectContain er db) { //se crean 4 objetos tipo charla charla c1 = new charla("Bases de Datos Orientad as a Objetos", 2); charla c2 = new charla("MySQL y PostGreSQL", 3) ;

Page 33: Acceso a Datos - Resumen

charla c3 = new charla("XML", 2); charla c4 = new charla("Db4o", 3); //se crean 4 objetos ponente ponente p1 = new ponente("11A", "Antonio Camaco ", "[email protected]", 300); ponente p2 = new ponente("22B", "Isabel Pérez", "[email protected]", 100); ponente p3 = new ponente("33C", "Ana Navarro", "[email protected]", 200); //se le asigna un ponente a cada charla c1.setPonente(p1); c2.setPonente(p2); c3.setPonente(p3); c4.setPonente(p1); //Persistir Objetos: almacenamos los objetos con el método store() db.store(c1); db.store(c2); db.store(c3); db.store(c4); } //Método para mostrar objetos recuperados de la Base de Objetos public static void mostrarConsulta(ObjectSet resu l) { //mensaje indicando el total de objetos recuperados System.out.println("Recuperados " + resul.size( ) + " Objetos"); while (resul.hasNext()) {//bucle que obtiene ob jeto a objeto System.out.println(resul.next()); } } //Consulta QBE de objetos estructurados. Consulta de todas las charlas. public static void mostrarCharlasQBE(ObjectContai ner db) { //se crea el objeto charla patrón de búsqueda charla c = new charla(null, 0); //Consulta las charlas con patrones indicados ObjectSet resul = db.queryByExample(c); mostrarConsulta(resul);//método que muestra los objetos recuperados de BDOO } //Consulta SODA de objetos estructurados //Se consulta la charla cuyo título se pasa en parámetro tit public static void consultaSODACharla_concreta(Ob jectContainer db, String tit) { Query query = db.query(); //declara un objeto q uery query.constrain(charla.class);//establece la cl ase a la que se aplicará la restricción query.descend("titulo").constrain(tit);//establ ece la restricción de búsqueda ObjectSet resul = query.execute();//ejecuta con sulta mostrarConsulta(resul);//método que muestra los objetos recuperados de la BDOO }

//Consulta de objetos estructurados. Consulta QBE //Consulta de las charlas del ponenete Anotnio Camaco public static void mostrarCharlasCamacoQBE(Object Container db) { //se crea objeto ponente con patrón de búsqueda (el ejemplo) ponente p = new ponente(null, "Antonio Camaco", null, 0); //se crea el objeto charla con patrón de búsqueda charla c = new charla(null, 0); c.setPonente(p); //se asocia el ponente de búsq ueda a la charla ObjectSet resul = db.queryByExample(c); //Consu lta las charlas con patrones indicados mostrarConsulta(resul);//método que muestra los objetos recuperados de BDOO } //Borrado de objetos estructurados. Se utiliza Consulta SODA //Se elimina la charla de título tit sin eliminar al ponente asociado public static void borrarCharlaporTitulo(ObjectCo ntainer db, String tit) { Query query = db.query(); //declaración de un o bjeto query(). query.constrain(charla.class);//establece la cl ase a la que se aplicará la restricción query.descend("titulo").constrain(tit);//establ ece la restricción de búsqueda ObjectSet resul = query.execute();//ejecuta con sulta con restricción búsqueda while (resul.hasNext()) { //bucle que recupera los objetos charla y elimina de la BDOO charla c = (charla) resul.next(); System.out.println("Eliminando: " + c); db.delete(c); } } //Consulta de todos los objetos ponente. Consulta QBE public static void mostrarPonentes(ObjectContaine r db) { //se crea objeto p con patrón de búsqueda (el ejemplo) ponente p = new ponente(null, null, null, 0); //consulta los ponentes de patrón ponente(null, null, null, 0). Consulta QBE ObjectSet res = db.queryByExample(p); mostrarConsulta(res); //método que muestra los objetos recuperados de BDOO } //Modificación de Objetos estructurados. Con consulta QBE //Actualiza la duración de la charla de título tit en d horas public static void actualizarHorasCharla(ObjectCo ntainer db, String tit, float d) { //consulta la charla de patrón charla(tit,0). Consulta QBE ObjectSet res = db.queryByExample(new charla(ti t, 0)); charla c = (charla) res.next(); //obtiene la ch arla consultada c.setDuracion(d); //asigna la nueva duración db.store(c); //almacena la charla modificada } }

Clase Principal para objetos estructurados, para el iminación de objetos package eliminacioncascada; //API necesaria de db4o import com.db4o.Db4oEmbedded; import com.db4o.ObjectContainer; import com.db4o.ObjectSet; import com.db4o.config.EmbeddedConfiguration; //est ablecer configuración de conexión import com.db4o.query.Query; public class Main { public static void main(String[] args) { //Conexión a la base de objetos y apertura de la base de objetos congreso.db4o //Se CONFIGURA la apertura de manera que al eliminar un objeto estructurado //de tipo charla, se eliminen también los objeto hilo tipo ponente. //Se indica la nueva configuración de conexión, con borrado en cascada EmbeddedConfiguration config = Db4oEmbedded.newConfiguration(); config.common().objectClass(charla.class).cascadeOn Delete(true); //Se abre la conexión a la base de objetos congreso.db4o ObjectContainer db = Db4oEmbedded.openFile(conf ig, "congreso.db4o");

//llamada a métodos para operar con la base de objetos try { almacenarCharlas(db); System.out.println("CHARLAS EXISTENTES EN LA BDOO:"); mostrarCharlasQBE(db); System.out.println("ELIMINACIÓN de la charla XML:"); borrarCharlaporTitulo(db, "XML"); System.out.println("CHARLAS Y PONENTES EXISTE NTES tras borrar charla XML:"); mostrarCharlasQBE(db); mostrarPonentes(db); } catch (Exception e) { //código para el tratamiento de la excepción } finally { db.close(); // cerrar la base de datos antes de salir } } //Almacenar objetos estructurados //Método para insertar charlas en la Base de Objetos y almacenarlas public static void almacenarCharlas(ObjectContain er db) { //se crean 4 objetos tipo charla

Page 34: Acceso a Datos - Resumen

charla c1 = new charla("Bases de Datos Orientad as a Objetos", 2); charla c2 = new charla("MySQL y PostGreSQL", 3) ; charla c3 = new charla("XML", 2); charla c4 = new charla("Db4o", 3); //se crean 4 objetos ponente ponente p1 = new ponente("11A", "Antonio Camaco ", "[email protected]", 300); ponente p2 = new ponente("22B", "Isabel Pérez", "[email protected]", 100); ponente p3 = new ponente("33C", "Ana Navarro", "[email protected]", 200); //se le asigna un ponente a cada charla c1.setPonente(p1); c2.setPonente(p2); c3.setPonente(p3); c4.setPonente(p1); //Persistir Objetos: almacenamos los objetos con el método store() db.store(c1); db.store(c2); db.store(c3); db.store(c4); } //Método para mostrar objetos recuperados de la Base de Objetos public static void mostrarConsulta(ObjectSet resu l) { //mensaje indicando el total de objetos recuperados System.out.println("Recuperados " + resul.size( ) + " Objetos"); while (resul.hasNext()) {//bucle que obtiene ob jeto a objeto System.out.println(resul.next()); } } //Consulta QBE de objetos estructurados. Consulta de todas las charlas. public static void mostrarCharlasQBE(ObjectContai ner db) { //se crea el objeto charla patrón de búsqueda

charla c = new charla(null, 0); //Consulta las charlas con patrones indicados ObjectSet resul = db.queryByExample(c); mostrarConsulta(resul);//método que muestra los objetos recuperados de BDOO } //Borrado de objetos estructurados. Se utiliza Consulta SODA //Se elimina la charla de título tit sin eliminar al ponente asociado public static void borrarCharlaporTitulo(ObjectCo ntainer db, String tit) { Query query = db.query(); //declaración de un o bjeto query(). query.constrain(charla.class);//establece la cl ase a la que se aplicará la restricción query.descend("titulo").constrain(tit);//establ ece la restricción de búsqueda ObjectSet resul = query.execute();//ejecuta con sulta con restricción búsqueda while (resul.hasNext()) { //bucle que recupera los objetos charla y elimina de la BDOO charla c = (charla) resul.next(); System.out.println("Eliminando: " + c); db.delete(c); } } //Consulta de todos los objetos ponente. Consulta QBE public static void mostrarPonentes(ObjectContaine r db) { //se crea objeto p con patrón de búsqueda (el ejemplo) ponente p = new ponente(null, null, null, 0); //consulta los ponentes de patrón ponente(null, null, null, 0). Consulta QBE ObjectSet res = db.queryByExample(p); mostrarConsulta(res); //método que muestra los objetos recuperados de BDOO } }

5.- El lenguaje de consulta de objetos OQL. OQL (Object Query Languaje) es el lenguaje de consulta de objetos propuesto en el estándar ODMG. Las siguientes son algunas de las características más relevantes de OQL:

• Es un lenguaje declarativo del tipo de SQL • Su sintaxis es similar a la de SQL • No posee primitivas para modificar el estado de los objetos • Puede ser usado como un lenguaje autónomo o incrustado dentro de otros lenguajes • Una consulta OQL incrustada en uno de estos lenguajes de programación puede devolver objetos que coincidan con el sistema de

tipos de ese lenguaje. • Permite acceso tanto asociativo como navegacional:

o Una consulta asociativa devuelve una colección de objetos. o Una consulta navegacional accede a objetos individuales y las interrelaciones entre objetos sirven para navegar entre objetos.

5.1.- Sintaxis, expresiones y operadores. sentencia SELECT del OQL estándar es la siguiente: SELECT [DISTINCT] <expresión, ...> FROM <lista from> [WHERE <condición> ] [ORDER BY <expresión>] SELECT p.nombre, p.email FROM p in Profesor WHERE p.ingreso <= 1990 ORDER BY p.nombre; En las consultas se necesita un punto de entrada, que suele ser el nombre de una clase. El resultado de una consulta es una colección que puede ser tipo bag (si hay valores repetidos) o tipo set (no hay valores repetidos). En este último caso habrá que especificar SELECT DISTINCT. En general, una consulta OQL puede devolver un resultado con una estructura compleja especificada en la misma consulta utilizando struct. Además, en una consulta OQL se pueden utilizar, entre otros, los siguientes operadores y expresiones: Operadores de acceso: "." / "->" aplicados a un atributo, una expresión o una relación. FIRST / LAST (primero / último elemento de una lista o un vector). Operadores aritméticos: +, -, *, /, -(unario), MOD, ABS para formar expresiones aritméticas. Operadores relacionales: >, <, >=, <=, <>, = que permiten comparaciones y construir expresiones lógicas. Operadores lógicos: NOT, AND, OR que permiten enlazar otras expresiones. Operadores de colecciones como funciones de agregacións (MAX(), MIN(), COUNT(), SUM() y AVG()) y cuantificadores (FOR ALL, EXISTS). Realización de agrupaciones mediante GROUP BY y filtro de los grupos mediante HAVING. Combinación de consultas mediante JOINs Unión, intersección y resta de colecciones mediante los operadores UNION, INTERSEC y EXCEPT.

Page 35: Acceso a Datos - Resumen

5.2.- Matisse, un gestor de objetos que incorpora O QL. El propio gestor proporciona el driver matisse.jar para interactuar con aplicaciones escritas en Java. Dentro de los paquetes del API destacamos:

• com.matisse. Proporciona las clases e interfaces básicos para trabajar con Java y una base de datos de objetos Matisse. • MtDatabase . Clase que proporciona todos los métodos para realizar las conexiones y transacciones en la base de objetos. • com.matisse.sql. Proporciona clases que permiten interactuar con la base de objetos vía JDBC.

Descargar en www.matisse.com , el archivo matisse.jar se encuentra en la carpeta lib de instalacion, agregar en netbeans en biblioteca del proyecto. Creacion de base de datos doctorado:

Page 36: Acceso a Datos - Resumen
Page 37: Acceso a Datos - Resumen

Metodo Main: package doctoradomatisse; //librería proporcionada por Matisse (necesaria porque vamos a utilizar los //objetos propios de Matisse, en lugar de los genéricos que nos //proporcionaría una conexión mediante JDBC) import com.matisse.MtDatabase; import com.matisse.MtException; public class Main { public static void main(String[] args) { //crea el objeto base de datos MtDatabase indicando la cadena de conexión //nombre del host "zailla" y base de datos "doctorado" //no se necesita usuario porque no se ha deficnido un control de acceso MtDatabase db = new MtDatabase("zailla", "d octorado"); //mensaje para la Salida System.out.println("=========== connectFrom MtDatabase ==========\n"); try { //conecta con la base de datos db.open(); //inicia una transacción (matisse gestiona todas las operaciones como transacciones) db.startTransaction(); //insertar datos en registros insertarObjetos(db); } catch (MtException mte) { //mensaje para la Salida System.out.println("MtException : " + m te.getMessage()); } finally { //confirma cualquier transacción en proceso if (db.isTransactionInProgress()) { db.commit(); } //cierra la base de datos db.close(); } } private static void insertarObjetos(MtDatabase db) { //crea objetos Departamento Departamento d1 = new Departamento(db);

d1.setNombre("Bases de Datos"); Departamento d2 = new Departamento(db); d2.setNombre("Lenguajes"); // Crea objetos Tesis Tesis t1 = new Tesis(db); t1.setTitulo("Persistencia de objetos"); t1.setTema("Bases de Objetos"); Tesis t2 = new Tesis(db); t2.setTitulo("Bases de Datos Nativas XML"); t2.setTema("Bases de Datos XML"); Tesis t3 = new Tesis(db); t3.setTitulo("Mapeo Objeto-Relacional"); t3.setTema("Bases de Datos"); Tesis t4 = new Tesis(db); t4.setTitulo("Multiproceso en Java"); t4.setTema("Lenguajes de Programación"); // Crea un objeto Profesor Profesor p1 = new Profesor(db); p1.setNombre("Ana Martos Gil"); p1.setEmail("[email protected]"); p1.setIngreso(1990); Profesor p2 = new Profesor(db); p2.setNombre("Isabel Ruz Granados"); p2.setEmail("[email protected]"); p2.setIngreso(1986); Profesor p3 = new Profesor(db); p3.setNombre("Antonio Barea Navarro"); p3.setEmail("[email protected]"); p3.setIngreso(1995); //establece relaciones entre Profesor y Departamentoanteriormente creados //Al establecer una relación no hace falta establecer la inversa*/ p1.setPertenece(d1); p1.appendDirige(t1); p2.setPertenece(d1); p2.appendDirige(t2); p2.appendDirige(t3); p3.setPertenece(d2); p3.appendDirige(t4); } }

Page 38: Acceso a Datos - Resumen

5.3.- Ejecución de Sentencias OQL. Por ejemplo, para recuperar el valor de todos los atributos de los objetos tipo Profesor, escribiríamos la siguiente sentencia OQL: SELECT * FROM Profesor; Y para recuperar del atributo nombre de los objetos tipo Profesor cuyo año de ingreso es anterior al 1990, escribiríamos la siguiente sentencia : SELECT nombre FROM Profesor WHERE ingreso <= 1990; Toda sentencia SELECT finaliza en punto y coma. Se puede usar where, Group by, Al recupear todos los atributos de una clase mediante SELECT *, la consulta retornará el OID de cada objeto recuperado, así como las interrelaciones o relaciones definidas para esa clase. El OID y la interrelación son del tipo string y se representan mediante un número hexadecimal. se pueden hacer JOIN de clases Ejemplo: nombre de cada profesor y título y tema de las tesis que dirige con JOIN SELECT p.nombre, t.titulo, t.tema FROM Profesor p JOIN Tesis t ON t.es_dirigida = p.O I Mediante SELECT se pueden realizar consultas navegacionales haciendo referencia a las interrelacionaes entre objetos. Ejemplo: nombre de cada profesor y título y tema de las tesis que dirige, navegacional SELECT t.titulo AS "Tesis", tema, t.es_dirigida.nom bre AS "Profesor " FROM Tesis t; También se pueden realizar consultas navegacionales a través de la referencia de los objetos (REF()). Ejemplo: tesis que dirigen los profesores con ingreo el 1990 o posterior SELECT REF(p.dirige) FROM Profesor p 1) WHERE p.ingreo >= 1990;

Page 39: Acceso a Datos - Resumen

5.4.- Ejecución de sentencias OQL vía JDBC. Para ello, la conexión a la base de objetos se realizará vía JDBC. Matisse proporciona dos formas de manipular objetos vía JDBC: mediante JDB puro o mediante una mezcla de JDBC y Matisse. Para crear una conexión vía JDBC puro utilizaremos java.SQL.* y com.Matisse.SQL.MtDriver, no siendo necesario en este caso el paquete com.Matisse.

• La cadena de conexión será de la forma: String url = "JDBC:mt://" + hostname + "/" + dbname; • La conexión se realizará mediante Connection jcon = DriverManager.getConnection(url);

Para crear una conexión vía JDBC y Matisse (a través de MtDatabase) se necesita java.SQL.* y com.Matisse.MtDatabase. • La cadena de conexión será de la forma: MtDatabase db = new MtDatabase(hostname, dbname); • La conexión se realizará mediante: db.open(); y Connection jcon = db.getJDBCConnection();

package doctoradomatisseoql; //bibliotecas necesarias import com.matisse.*; import com.matisse.sql.MtResultSet; import java.sql.*; public class Main { public static void main(String[] args) { //Crea un objeto MtDatabase asociando la cadena de conexión MtDatabase dbcon = new MtDatabase("localhos t", "doctorado"); // Abrir una conexión a la base de objetos dbcon.open(); try { Connection jdbcon = dbcon.getJDBCConnec tion(); // Crea la sentencia Statement stmt = jdbcon.createStatement (); //Define la consulta SELECT String query = "SELECT nombre, email FR OM Profesor;"; //Ejecuta la consulta y obtiene un EesulSet ResultSet rset = stmt.executeQuery(quer y); // Imprime total de objetos recuperados System.out.println("Total selected: " + ((MtResultSet) rset).getTotal NumObjects()); System.out.println(""); // Imprime los nombre de columna ResultSetMetaData rsMetaData = rset.get MetaData(); int numberOfColumns = rsMetaData.getCol umnCount(); // Obtiene los nombres de columna; indexa las columnas comenzando desde 1 for (int i = 0; i < numberOfColumns; i+ +) { System.out.print(String.format("%30 s",

rsMetaData.getColumnName(i + 1)) + " "); } System.out.println(""); for (int i = 0; i < numberOfColumns; ++ i) { System.out.print(" ---------------- ------------- "); } System.out.println(""); String nombrep, emailp; // Read filas (objetos) one by one while (rset.next()) { // Obtiene los valores de la primera y segunda columna nombrep = rset.getString(1); emailp = rset.getString(2); // Imprime la fila en curso System.out.println(String.format("% 30s", nombrep) + " " + String.format("%30s", ema ilp)); } // resetea y cierra la conexión a la base de objetos rset.close(); stmt.close(); } catch (SQLException e) { System.out.println("SQLException: " + e .getMessage()); } if (dbcon.isVersionAccessInProgress()) { dbcon.endVersionAccess(); } else if (dbcon.isTransactionInProgress()) { dbcon.rollback(); } dbcon.close(); } }

6.- Características de las bases de datos objeto-re lacionales. Como consecuencia de esto, aparecen nuevas características, entre las que podemos destacar las siguientes:

• Tipos definidos por el usuario. • Tipos Objeto. Posibilidad de creación de objetos como nuevo tipo de dato que permiten relaciones anidadas. • Reusabilidad. Posibilidad de guardar esos tipos en el gestor de la BDOR, para reutilizarlos en tantas tablas como sea necesario • Creación de funciones. Posibilidad de definir funciones y almacenarlas en el gestor. • Tablas anidadas. Se pueden definir columnas como arrays o vectores multidimensionales, • Herencia con subtipos y subtablas.

6.1.- El estándar SQL99. Por ejemplo, el siguiente segmento de SQL crea un nuevo tipo de dato, un tipo estructurado de nombre profesor y que incluye en su definición un método, el método sueldo() . CREATE TYPE profesor AS ( id INTEGER, nombre VARCHAR (20), sueldo_base DECIMAL (9,2), complementos DECIMAL (9,2), INSTANTIABLE NOT FINAL METHOD sueldo() RETURNS DECIMAL (9,2)); CREATE METHOD sueldo() FOR profesor BEGIN ...... END;

Page 40: Acceso a Datos - Resumen

7.- Gestores de Bases de Datos Objeto-Relacionales. A continuación te indicamos algunos ejemplos de gestores objeto-relacionales, tanto de código libre como propietario, todos ellos con soporte para Java: De código abierto:

• PostgreSQL • Apache Derby

De código propietario • Oracle • First SQL • DB2 de IBM

7.1.- Instalación del Gestor PostgreSQL.

Page 41: Acceso a Datos - Resumen

Para los ejemplos, crearemos una base de datos de p rueba denominada anaconda. Con la ayuda de pgAdmin, esta tarea puede realizarse en uno cuantos clics:

Page 42: Acceso a Datos - Resumen

7.2.- Tipos de datos: tipos básicos y tipos estruct urados. PostgreSQL no soporta herencia de tipos pero permite definir nuevos tipos de datos mediante los mecanismos de extensión. Te vamos a comentar tres categorías de tipos de datos que encontramos en PostgreSQL:

• Tipos básicos : el equivalente a los tipos de columna usados en cualquier base de datos relacional. • Tipos compuestos: un conjunto de valores definidos por el usuario con estructura de fila de tabla, y que como tal puede estar

formada por tipos de datos distintos. • Tipos array : un conjunto de valores distribuidos en un vector multidimensional, con la condición de que todos sean del mismo tipo

de dato (básico o compuesto). Ofrece más funcionalidades que el array del estándar SQL99. Entre los tipos básicos, podemos destacar:

• Tipos numéricos. Aparte de valores enteros, números de coma flotantes, y números de precisión arbitraria, PostgreSQL incorpora también un tipo entero auto-incremental denominado serial.

• Tipos de fecha y hora. Además de los típicos valores de fecha, hora e instante, PostgreSQL incorpora el tipo interval para representar intervalos de tiempo.

• Tipos de cadena de caracteres . Prácticamente los mismos que en cualquier BDR. • Tipos largos. Como por ejemplo, el tipo BLOB para representar objetos binarios. En la actualidad presentes en muchas BDR como

MySQL. Los tipos compuestos de PostgreSQL son el equivalente a los tipos estructurados definidos por el usuario del estándar SQL99. De hecho, son la base sobre la que se asienta el soporte a objetos. Por ejemplo, podemos crear un nuevo tipo, el tipo dirección a partir del nombre de la calle (varchar), del número de la calle (integer) y del código postal (integer), de la siguiente forma: CREATE TYPE direccion AS ( calle varchar, numero integer, codigo_postal integer); y luego definir la tabla de nombre afiliados, con una columna basada en el nuevo tipo: CREATE TABLE afiliados AS( afiliado_id serial, nombre varchar(45), apellidos varchar(45), domicilio direccion); 7.3.- Conexión mediante JDBC.

Una vez integrado en un proyecto, el driver puede utilizarse desde cualquiera de sus clases mediante un: import java.sql.* Recuerda que en JDBC, una base de datos está representada por una URL. La cadena correspondiente tiene una de las tres formas siguientes: jdbc: postgresql: base de datos jdbc: postgresql: //host/base de datos jdbc: postgresql: //host: puerto/base de datos Para conectar con la base de datos, utilizaremos el método DriverManager.getConnection() que devuelve un objeto Connection (la conexión con la base de datos). Una de las posibles sintaxis de este método es: Connection conn = DriverManager.getConnection(url, username, password); Una vez abierta, la conexión se mantendrá operativa hasta que se llame a su método close() para efectuar la desconexión. Si al intentar conectar con la base de datos ésta no existe, se generará una excepción del tipo PSQLException "FATAL: no existe la base de datos ...". En cualquier caso se requiere un bloque try-catch .

Page 43: Acceso a Datos - Resumen

//cadena url de la base de datos anaconda en el servidor local (no hay //que indicar el puerto si es el por defecto) String url = "jdbc:postgresql://localhost/anaco nda"; //conexión con la base de datos Connection conn = null; try { //abre la conexión con la base de datos a la que apunta el url //mediante la contraseña del usuario postgres conn = DriverManager.getConnection(url, "post gres", "password"); //hace algo útil... } catch (SQLException ex) { //imprime la excepción System.out.println(ex.toString()); } finally { //cierra la conexión conn.close(); } 7.4.- Consulta y actualización de tipos básicos. PostgreSQL implementa los objetos como filas, las clases como tablas, y los atributos como columnas. Por tanto, si nuestra conexión es conn, para enviar un comando Statement haríamos lo siguiente: Crear la sentencia, por ejemplo.: Statement sta = conn.createStatement(); Ejecutar la sentencia: sta.executeQuery(string sentenciaSQL); si sentencia SQL es una consulta (SELECT) sta.executeUpdate(string sentenciaSQL); si sentenci aSQL es una actualización (INSERT, UPDATE O DELETE) sta.execute(string sentenciaSQL); si sentenciaSQL e s un CREATE, DROP, o un ALTER Ejemplo package PaquetePrincipal; /****************************************************************************** * ilustra característica meramente relacionales de PostgreSQL mediante el * uso de comandos public class Main { public static void main(String[] args) throws SQL Exception { //cadena url de la base de datos anaconda en el servidor local String url = "jdbc:postgresql://localhost/anaco nda"; //conexión con la base de datos Connection conn = null; try { //abre la conexión con la base de datos a la que apunta el url //mediante la contraseña del usuario postgres conn = DriverManager.getConnection(url, "post gres", "1234"); //elimina las tablas (si existen) drop_Ejemplo(conn); //vuelve a crear las tablas de ejemplo crearTablas(conn); //inserta algunos regitros insertarRegistros(conn); //realiza alguna consultas consulta_Ejemplo1(conn); consulta_Ejemplo2(conn); //modifica algunos registros update_Ejemplo(conn); //borra algunos registros delete_Ejemplo(conn); } catch (SQLException ex) { //imprime la excepción System.out.println(ex.toString()); } finally { //cierra la conexión conn.close(); } } /**************************************************************************** * borra todas las tablas del ejemplo (si es que existen) private static void drop_Ejemplo(Connection conn) throws SQLException { //consulta SQL String consulta = "DROP TABLE IF EXISTS datos_m eteo;" + "DROP TABLE IF EXISTS meses;" + "DROP TABLE IF EXISTS provincias;"; //comando auxiliar para ejecutar la consulta Statement sta = conn.createStatement(); //ejecuta la consulta sta.execute(consulta); //cierra el objeto auxiliar sta.close(); } * creamos las tablas ('meses', 'provincias' y 'datos_meteo') y establecemos * tanto las claves primarias ('mes_id', 'provincia_id' y 'datos_meteo_id'), * como las foráneas (columnas 'mes_id' y 'provincia_id' de la tabla * 'datos_meteo') *

* el tipo de las claves primarias "mes_id" y "provincia_id" es entero. El * tipo de la clave primaria de la tabla 'datos_meteo' es serial (entero * autoincremental en PostgreSQL). El resto son cadenas de longitud variable * , o numeric (el tipo numérico para cálculos exactos). * * la tabla 'meses' tiene un relacion de uno a varios con la tabla * 'datos_meteo', mediante el campo 'mes_id' de la segunda * * la tabla 'provincias' tiene un relacion de uno a varios con la tabla * 'datos_meteo', mediante el campo 'provincia_id' de la segunda * * ambas relaciones implementan actualizaciones y eliminaciones en cascada private static void crearTablas(Connection conn) throws SQLException { //consulta String consulta = "CREATE TABLE meses(" + "mes_id integer NOT NULL," + "mes varchar(25)," + "CONSTRAINT mes_id PRIMARY KEY (mes_i d )" + ");" + "CREATE TABLE provincias(" + "provincia_id integer NOT NULL," + "provincia varchar(25)," + "CONSTRAINT provincia_id PRIMARY KEY (provincia_id )" + ");" + "CREATE TABLE datos_meteo(" + "datos_meteo_id serial NOT NULL," + "provincia_id integer," + "mes_id integer," + "temp_min numeric," + "temp_max numeric," + "precipitaciones integer," + "CONSTRAINT datos_meteo_id PRIMARY KE Y (datos_meteo_id )," + "CONSTRAINT mes_id FOREIGN KEY (mes_i d) REFERENCES " + "meses (mes_id) MATCH SIMPLE ON UPDAT E CASCADE ON " + "DELETE CASCADE," + "CONSTRAINT provincia_id FOREIGN KEY (provincia_id) REFERENCES " + "provincias (provincia_id) MATCH SIMP LE ON UPDATE CASCADE " + "ON DELETE CASCADE" + ");"; //comando auxiliar para ejecutar la consulta Statement sta = conn.createStatement(); //ejecuta la consulta sta.execute(consulta); //cierra el objeto auxiliar sta.close(); }

Page 44: Acceso a Datos - Resumen

* insertamos algunos registros en las tablas (por suspuesto, respetando * la integridad de las relaciones) private static void insertarRegistros(Connection conn) throws SQLException { //consulta String consulta = "INSERT INTO meses (mes_id, m es) VALUES (1, 'enero');" + "INSERT INTO meses (mes_id, mes) VALU ES (2, 'febrero');" + "INSERT INTO meses (mes_id, mes) VALU ES (3, 'marzo');" + "INSERT INTO meses (mes_id, mes) VALU ES (4, 'abril');" + "INSERT INTO meses (mes_id, mes) VALU ES (5, 'mayo');" + "INSERT INTO meses (mes_id, mes) VALU ES (6, 'junio');" + "INSERT INTO meses (mes_id, mes) VALU ES (7, 'julio');" + "INSERT INTO meses (mes_id, mes) VALU ES (8, 'agosto');" + "INSERT INTO meses (mes_id, mes) VALU ES (9, 'septiembre');" + "INSERT INTO meses (mes_id, mes) VALU ES (10, 'octubre');" + "INSERT INTO meses (mes_id, mes) VALU ES (11, 'noviembre');" + "INSERT INTO meses (mes_id, mes) VALU ES (12, 'diciembre');" + "INSERT INTO provincias (provincia_id , provincia) " + "VALUES (1, 'Barcelona');" + "INSERT INTO provincias (provincia_id , provincia) " + "VALUES (2, 'Madrid');" + "INSERT INTO provincias (provincia_id , provincia) " + "VALUES (3, 'Murcia');" + "INSERT INTO provincias (provincia_id , provincia) " + "VALUES (4, 'Valencia');" + "INSERT INTO datos_meteo (provincia_i d, mes_id, temp_min, " + "temp_max, precipitaciones) VALUES (1 , 1, 4.4, 13.4, 41);" + "INSERT INTO datos_meteo (provincia_i d, mes_id, temp_min, " + "temp_max, precipitaciones) VALUES (1 , 4, 8.5, 17.6, 49);" + "INSERT INTO datos_meteo (provincia_i d, mes_id, temp_min, " + "temp_max, precipitaciones) VALUES (1 , 7, 18.6, 27.5, 20);" + "INSERT INTO datos_meteo (provincia_i d, mes_id, temp_min, " + "temp_max, precipitaciones) VALUES (1 , 10, 12.6, 21.5, 91);" + "INSERT INTO datos_meteo (provincia_i d, mes_id, temp_min, " + "temp_max, precipitaciones) VALUES (2 , 1, 0.3, 10.6, 33);" + "INSERT INTO datos_meteo (provincia_i d, mes_id, temp_min, " + "temp_max, precipitaciones) VALUES (2 , 4, 5.4, 18.0, 39);" + "INSERT INTO datos_meteo (provincia_i d, mes_id, temp_min, " + "temp_max, precipitaciones) VALUES (2 , 7, 16.1, 33.0, 11);" + "INSERT INTO datos_meteo (provincia_i d, mes_id, temp_min, " + "temp_max, precipitaciones) VALUES (2 , 10, 8.3, 20.6, 39);" + "INSERT INTO datos_meteo (provincia_i d, mes_id, temp_min, " + "temp_max, precipitaciones) VALUES (3 , 1, 5.12, 15.82, 38);" + "INSERT INTO datos_meteo (provincia_i d, mes_id, temp_min, " + "temp_max, precipitaciones) VALUES (3 , 4, 9.3, 19.9, 25);" + "INSERT INTO datos_meteo (provincia_i d, mes_id, temp_min, " + "temp_max, precipitaciones) VALUES (3 , 7, 19.9, 28.4, 6);" + "INSERT INTO datos_meteo (provincia_i d, mes_id, temp_min, " + "temp_max, precipitaciones) VALUES (3 , 10, 18.7, 23.4, 14);" + "INSERT INTO datos_meteo (provincia_i d, mes_id, temp_min, " + "temp_max, precipitaciones) VALUES (4 , 1, 5.0, 15.5, 38);" + "INSERT INTO datos_meteo (provincia_i d, mes_id, temp_min, " + "temp_max, precipitaciones) VALUES (4 , 4, 9.4, 20.6, 38);" + "INSERT INTO datos_meteo (provincia_i d, mes_id, temp_min, " + "temp_max, precipitaciones) VALUES (4 , 7, 19.8, 30.9, 14);" + "INSERT INTO datos_meteo (provincia_i d, mes_id, temp_min, "

+ "temp_max, precipitaciones) VALUES (4 , 10, 13.3, 23.4, 74);"; //comando auxiliar para ejecutar la consulta Statement sta = conn.createStatement(); //ejecuta la consulta sta.execute(consulta); //cierra el objeto auxiliar sta.close(); } * mostramos todos los registros de la tabla 'datos_meteo'. Tanto en bruto, * como mostrando los nombres de los meses y de la provincias. En el segundo * caso, redondeamos las columnas 'temp_min' y 'temp_max" private static void consulta_Ejemplo1(Connection conn) throws SQLException { // System.out.print("Todos los datos meteorológico s, tanto en bruto:\n"); //consulta SQL String consulta = "SELECT * FROM datos_meteo"; //comando auxiliar para ejecutar la consulta Statement sta = conn.createStatement(); //ejecuta la consulta para que devuelva un conjunto de registros ResultSet res = sta.executeQuery(consulta); //imprime el resultado imprimir_ResultSet(res); // System.out.print("\ncomo con los valores tomado s de las " + "tablas relacionadas:\n"); //consulta SQL consulta = "SELECT " + "datos_meteo.datos_meteo_id, " + "provincias.provincia, " + "meses.mes, " + "round(datos_meteo.temp_min), " + "round(datos_meteo.temp_max), " + "datos_meteo.precipitaciones " + "FROM meses,provincias,datos_meteo " + "WHERE " + "meses.mes_id=datos_meteo.mes_id " + "AND " + "provincias.provincia_id=datos_meteo. provincia_id"; //comando auxiliar para ejecutar la consulta sta = conn.createStatement(); //reutiliza el comando para enviar la nueva consulta y que //devuelva un ResultSet res = sta.executeQuery(consulta); //imprime el resultado imprimir_ResultSet(res); //cierra los objetos auxiliares res.close(); sta.close(); } * calcula la temperatura media por mes de Murcia (provincia_id=3) private static void consulta_Ejemplo2(Connection conn) throws SQLException { System.out.print("\nTemperatura media por mes d e Murcia:\n"); //consulta SQL String consulta = "SELECT " + "meses.mes, " + "ROUND((datos_meteo.temp_max+datos_meteo.temp_min)/ 2,2) " + "FROM meses,datos_meteo " + "WHERE " + "meses.mes_id=datos_meteo.mes_id " + "AND " + "datos_meteo.provincia_id=3"; //comando SQL Statement sta = conn.createStatement(); //ejecuta la consulta para que devuelva un conjunto de registros ResultSet res = sta.executeQuery(consulta); //imprime el resultado imprimir_ResultSet(res); //cierra los objetos auxiliares res.close(); sta.close(); }

Page 45: Acceso a Datos - Resumen

* cambia la clave principal del registro 'Valencia' en la tabla 'provincias' * (de 4 a 5), y comprueba la actualización en cascada de los registros * relacionados en la tabla 'datos_meteo' private static void update_Ejemplo(Connection conn) throws SQLException { System.out.print("\nCambia la clave principal d e 'Valencia' en la tabla " + "'provincias'(de 4 a 5)."); //consulta SQL String consulta = "UPDATE provincias " + "SET provincia_id=5 " + "WHERE " + "provincia_id=4"; //comando SQL Statement sta = conn.createStatement(); //ejecuta la consulta para que muestre el número de filas modificadas System.out.print("\nComo resultado, " + sta.executeUpdate(consulta) + " fila actualizada:\n"); //nueva consulta SQL consulta = "SELECT * FROM provincias"; //reutiliza el comando para enviar la nueva consulta y que //devuelva un ResultSet ResultSet res = sta.executeQuery(consulta); //imprime el resultado imprimir_ResultSet(res); System.out.print("\ny muestra la actualización en cascada de los registros " + "relacionados en la tabla 'datos_mete o':\n"); //nueva consulta SQL consulta = "SELECT * FROM datos_meteo WHERE provincia_id=5"; //reutiliza el comando para enviar la nueva consulta y que //devuelva un ResultSet res = sta.executeQuery(consulta); //imprime el resultado imprimir_ResultSet(res); //cierra los objetos auxiliares res.close(); sta.close(); } * borra la provincia de 'Valencia' en la tabla 'provincias', y comprueba el * borrado en cascada de los registros relacionados en la tabla 'datos_meteo' private static void delete_Ejemplo(Connection con n) throws SQLException { System.out.print("\nBorra la provincia 'Valenci a' en la tabla " + "'provincias':\n"); //consulta SQL String consulta = "DELETE FROM provincias WHERE provincia = 'Valencia'"; //comando SQL Statement sta = conn.createStatement(); //ejecuta la consulta para que muestre las filas eliminadas System.out.print("\nComo resultado, " + sta.executeUpdate(consulta)

+ " fila eliminada:\n"); //nueva consulta consulta = "SELECT * FROM provincias"; //reutiliza el comando para enviar la nueva consulta y que //devuelva un ResultSet ResultSet res = sta.executeQuery(consulta); //imprime el resultado imprimir_ResultSet(res); System.out.print("\ny muestra el borrado en cas cada de los registros " + "relacionados en la tabla 'datos_mete o':\n"); //nueva consulta consulta = "SELECT " + "datos_meteo.datos_meteo_id, " + "provincias.provincia, " + "meses.mes, " + "round(datos_meteo.temp_min), " + "round(datos_meteo.temp_max), " + "datos_meteo.precipitaciones " + "FROM meses,provincias,datos_meteo " + "WHERE " + "meses.mes_id=datos_meteo.mes_id " + "AND " + "provincias.provincia_id=datos_meteo. provincia_id"; //reutiliza el comando para enviar la nueva consulta y que //devuelva un ResultSet res = sta.executeQuery(consulta); //imprime el resultado imprimir_ResultSet(res); //cierra los objetos auxiliares res.close(); sta.close(); } * imprime en la Salida el resultSet especificado, con los datos de cada * columna tabulados private static void imprimir_ResultSet(ResultSet resultSet) throws SQLException { //número de columnas del resultset ResultSetMetaData metaDatos = resultSet.getMeta Data(); int columnas = metaDatos.getColumnCount(); //mientras quedan registros por leer en el ResultSet while (resultSet.next()) { //imprime los registros for (int i = 1; i <= columnas; i++) { //seguidos de un tabulador System.out.print(resultSet.getString(i) + " \t"); } //línea en blanco System.out.println(); } } }

7.5.- Consulta y actualización de tipos estructurad os. Imaginemos que tenemos el tipo estructurado direccion: CREATE TYPE direccion AS ( calle varchar, numero integer, codigo_postal varcha r); y la tabla afiliados, con una columna basada en el nuevo tipo: CREATE TABLE afiliados(afiliado_id serial, nombre v archar, apellidos varchar, domicilio direccion); Cómo insertamos valores en una tabla con un tipo estructurado? Se puede hacer de dos formas: Pasando el valor del campo estructurado entre comillas simples (lo que obliga a encerrar entre comillas dobles cualquier valor de cadena dentro), y paréntesis para encerrar los subvalores separados por comas: INSERT INTO afiliados (nombre, apellidos, direccio n). INSERT INTO afiliados (nombre, apellidos, direccion ) VALUES ('Onorato', 'Maestre Toledo', '(“Calle de Rufino”, 56, 98080)');

Page 46: Acceso a Datos - Resumen

Mediante la función ROW que permite dar valor a un tipo compuesto o estructurado. INSERT INTO afiliados (nombre, apellidos, direccion ) VALUES ('Onorato', 'Maestre Toledo', ROW('Call e de Rufino', 56, 98080)); ¿Cómo se referencia una subcolumna de un tipo estructurado? Se emplea la notación punto, '.' , tras el nombre de la columna entre paréntesis, (tanto en consultas de selección, como de modificación) . Por ejemplo: SELECT (domicilio).calle FROM afiliados WHERE (domi cilio).codigo _postal=98080 devolvería el nombre de la calle Maestre Toledo. Los paréntesis son necesarios para que el gestor no confunda el nombre del campo compuesto con el de una tabla. Y ¿cómo se elimina el tipo estructurado? Se elimina con DROP TYPE, por ejemplo DROP TYPE direccion; En el siguiente enlace puedes descargar un proyecto java que incluye más ejemplos de todo esto.

7.6.- Consulta y actualización de tipos array. PostgreSQL permite especificar vectores multidimensionales como tipo de dato para las columnas de una tabla. Por ejemplo: Declaración de una columna de tipo vector: nombre_columna tipo_dato[] Declaración de una columna de tipo matriz multidimensional: nombre_columna tipo_dato[][] En el siguiente ejemplo puedes ver la creación de una tabla con una columna de tipo array de varchar y como se insertan y consultan valores: //comando sta = conn.createStatement(); sta.execute("DROP TABLE IF EXISTS tareas"); //crea una tabla con una columna matricial de tipo varchar sta.execute("CREATE TABLE tareas(comercial_id integer," + "agenda varchar[][])"); //inserta un registro de dos tareas por día para el comercial número 3 //durante dos días (día 1: [0][0],[0][1]; día 2:[1][0],[1][1]) sta.executeUpdate("INSERT INTO tareas VALUES( 3," + "'{{\"reunión 9:30\",\"comida 14:30 \"}," + "{\"reunión 8:30\",\"cena 22:30\"}} ')"); //consulta todas las tarea del segundo día del comercial número 3 ResultSet rst = sta.executeQuery("SELECT agen da[2:2] " + "FROM tareas WHERE comercial_id=3") ; //muestra el resultado while (rst.next()) { System.out.println(rst.getString(1)); } //consulta la segunda tarea del primer día del comercial número 3 rst = sta.executeQuery("SELECT agenda[1][2] " + "FROM tareas WHERE comercial_id=3") ; 7.7.- Funciones del gestor desde Java. En PostgreSQL se pueden construir funciones mediante diferentes lenguajes:

• Funciones de lenguaje de consultas o funciones SQL (Escritas en lenguaje SQL) • Funciones de lenguaje procedural (Escritas en lenguaje PL/pgSQL, PL/Java, etc) • Funciones de lenguaje de programación (Escritas en un lenguaje de compilado, como C o C++),

Sobre estas funciones debes saber que: • Los parámetros de la función pueden ser cualquier tipo de dato admitido por el gestor (un tipo básico, un tipo compuesto, un array o

alguna combinación de ellos). • El tipo devuelto puede ser un tipo básico, un array o un tipo compuesto.

Su estructura general tiene la forma: CREATE FUNCION nombre_funcion(tipo_1,tipo_2,...) RETURN tipo AS $$sentencia_sql$$ LANGUAJE SQL Y debes tener en cuenta que:

• Los argumentos de la función SQL se pueden referenciar en las consultas usando la sintaxis $n:, donde $1 se refiere al primer argumento, $2 al segundo, y así sucesivamente. Si un argumento es un tipo compuesto, entonces se usará la notación '.' para acceder a sus subcolumnas.

• Por último, al final de la función hay que especificar que la función se ha escrito en lenguaje SQL mediante las palabras clave LANGUAJE SQL.

Por ejemplo, desde Java podemos crear una función que convierte un tipo estructurado, por ejemplo el tipo direccion, en una cadena. Para ello, ejecutaremos un comando Statement (sta) con el código SQL de creación de la función: sta.execute (“CREATE FUNCTION cadena_direccion(dire ccion)”+ “RETURNS varchar AS 'SELECT $1.calle||' '|| CAST($1 .numero AS varchar)”+ “LANGUAGE SQL”;) Si una función tiene algún parámetro de tipo estructurado, no se podrá eliminar el tipo hasta que no se elimine la función. Se puede usar DROP C nb_tipo CASCADE para eliminar de manera automática las funciones que utilizan ese tipo. En el siguiente enlace te puedes descargar un ejemplo donde se crean funciones del gestor y se invocan desde una aplicación Java. package PaquetePrincipal; import java.sql.*; * crea un tipo estructurado, una tabla con el tipo como columna, y una * función sobre el tipo que devuelve una cadena compuesta public class Main { //URL de la base de datos anaconda static String url = "jdbc:postgresql://localhost/ anaconda"; //contraseña del usuario 'postgres' para acceder a la base de datos anaconda static String passwd = "1234"; public static void main(String[] args) throws SQL Exception { //conexión Connection conn = null; Statement sta = null;

try { //abre la conexión con la base de datos a la que apunta el url //mediante la contraseña del usuario postgres conn = DriverManager.getConnection(url, "post gres", passwd); //comando sta = conn.createStatement(); //elimina todo lo que va a crearse, si existe previamente. Como la función //depende del tipo y se especificó CASCADE, se elimina con el tipo sta.execute("DROP TABLE IF EXISTS empleados") ; sta.execute("DROP TYPE IF EXISTS puesto CASCA DE"); //crea el tipo estructurado sta.execute("CREATE TYPE puesto AS(nombre var char," + "cargo varchar,sueldo numeric)");

Page 47: Acceso a Datos - Resumen

//crea la función que transforma el tipo estructurado en una cadena sta.execute("CREATE FUNCTION cadena_puesto(pu esto) RETURNS varchar AS $$" + "SELECT $1.nombre||' como '||$1.car go||' tiene un sueldo de '" + "||CAST(ROUND($1.sueldo,2) AS varch ar)||'€';$$" + "LANGUAGE SQL"); //crea la tabla con una columna del tipo creado (columna ocupacion) sta.execute("CREATE TABLE empleados(empleado_ id serial," + "ocupacion puesto)"); //inserta un par de registros sta.executeUpdate("INSERT INTO empleados (ocu pacion)" + "VALUES(ROW('Antonio','Comercial',1 450.32));" + "INSERT INTO empleados (ocupacion)" + "VALUES(ROW('Juan','Encargado',1230 .48))"); //consulta el valor de la función sobre la columna del tipo

ResultSet rst = sta.executeQuery("SELECT cadena_puesto(ocupacion) " + "FROM empleados"); //muestra el resultado while (rst.next()) { System.out.println(rst.getString(1)); } } catch (SQLException ex) { //imprime la excepción System.out.println(ex.toString()); } finally { sta.close(); conn.close(); } } }

8.- Gestión de transacciones. Las transacciones deben cumplir el criterio ACID.

• Atomicidad. Se deben cumplir todas las operaciones de la transacción o no se cumple ninguna; no puede quedar a medias. • Consistencia. La transacción solo termina si la base de datos queda en un estado consistente. • Isolation (Aislamiento). Las transacciones sobre la misma información deben ser independientes, para que no interfieran sus

operaciones y no se produzca ningún tipo de error. • Durabilidad. Cuando la transacción termina el resultado de la misma perdura, y no se puede deshacer aunque falle el sistema.

Algunos sistemas proporcionan también puntos de salvaguarda (savepoints) que permiten descartar selectivamente partes de la transacción, justo antes de acometer el resto.

8.1.- Transacciones en una base objeto-relacional. Los SGBDOR gestionan transacciones mediante las sentencias COMMIT (confirmar transacción) y ROLLBACK (deshacer transacción). Por tanto, si queremos que un grupo de sentencias se ejecuten como una transacción, tras abrir una conexión Connection conn; habrá que: Poner autocommit=false de la siguiente manera: if (conn.getAutoCommit() ) conn.setAutoCommit( false ); Mediante un bloque try-catch controlar si se deshace conn.rollback o confirma conn.commit la transacción iniciada con esa conexión. En el siguiente enlace puedes ver como se controlan una secuencia de insert como una transacción en PostgreSQL. ////////////////////////////////////////////// // primer boton try{ if (con.getAutoCommit() ) con.setAutoCommit( false ); stat = con.createStatement(); //vamos hacer le primer insert String unsql = "insert into persona values ('"+jTex tField1.getText() + "', '" + jTextField2.getText() + "', '" +jTextField3.getText()+ "', '" +jTextField4.getText () + "' )"; stat.executeUpdate(unsql); } catch(Exception e){ System.out.println(" "+e); } /////////////////////////////////////////////////////////// // segundo boton try{ String unsql = "insert into persona values ('"+jTex tField5.getText() + "', '" + jTextField6.getText() + "', '" + jTextFiel d7.getText() + "', '" +jTextField8.getText() + "' )"; stat.executeUpdate(unsql); con.commit(); } catch(Exception e){ deshacer( ); // este metodo es importante System.out.println(" "+e); }

//////////////////////////////////////////////////////////// void deshacer( ) { try { con.rollback(); } catch (SQLException e) { System.out.println("Error. No hemos podido deshacer ." + e.getMessage() ); } } //////////////////////////////////////////////////////////// aunque esta un podo desordenado el codigo, espero que dando una vuelta lo entiendan, sobre la conexion Connection con; // variables de instancia Statement stat ; /** Creates new form ventana */ public ventana() { // en el constructor initComponents(); try{ Class.forName("org.postgresql.Driver"); // este bus ca el jar de postgres con = DriverManager.getConnection("jdbc:postgresql://loca lhost/arq2 tier", "postgres", "1234"); } catch(Exception e){ System.out.println(" "+e); } }

Otro ejemplo public static void executeTransaction(Connection co n) { try { //Switch to manual transaction mode by setting //autocommit to false. Note that this starts the fi rst //manual transaction. con.setAutoCommit(false); Statement stmt = con.createStatement(); stmt.executeUpdate("INSERT INTO Production.ScrapRea son(Name) VALUES('Wrong size')"); stmt.executeUpdate("INSERT INTO Production.ScrapRea son(Name) VALUES('Wrong color')"); con.commit(); //This commits the transaction and st arts a new one. stmt.close(); //This turns off the transaction. System.out.println("Transaction succeeded. Both rec ords were written to the database."); } catch (SQLException ex) { ex.printStackTrace(); try { con.rollback();

Page 48: Acceso a Datos - Resumen

System.out.println("Transaction failed. No records were written to the database."); } catch (SQLException se) { se.printStackTrace(); } } } 8.2.- Transacciones en un gestor de objetos. Las transacciones en un sistema de objetos puro, se gestionan mediante COMMIT y ROLLBACK . Veamos un ejemplo sencillo con la base de objetos db4o. En Db4o:

• Siempre se trabaja dentro de una transacción. • Al abrir un ObjectContainer se crea e inicia implícitamente una transacción. • Las transacciones se gestionan explícitamente mediante commit y rollback. • La transacción actual hace commit implícitamente cuando se cierra el ObjectContainer.

Por ejemplo, podemos deshacer una transacción con rollback , restaurando la base de objetos al estado anterior, justo al estado después del último commit o rollback. En el siguiente ejemplo, al ejecutar db.rollback después de db.store(p3) y db.store(p4), hará que los objetos p3 y p4 no se almacenen en la base de objetos.

Bases de datos xml. 1.- Introduccion. el estándar XML ha sido ampliamente aceptado y adoptado para el almacenamiento e intercambio de información

1.1.- Documentos XML centrados en datos y centrados en texto. los tipos de documentos XML que nos podemos encontrar son:

• Documentos centrados en datos , Ejemplos: Facturas, Pedidos Ficha de alumno. • Documentos centrados en texto . Ejemplos: Libros, Informes, Memorias, Artículos bibliográficos.

1.2.-Opciones de almacenamiento.

Para almacenar documentos XML tenemos las siguientes opciones: • Almacenamiento directo del fichero. • Almacenar el documento en una base de datos existen te

o Directamente como una columna tipo binario grande ( BLOB) dentro de una tabla. o Mediante mapeo basado en tablas, o basado en objeto s.

• Almacenar el documento en una base de datos nativa XML. Por tanto, podemos hablar de dos tipos de Sistemas de Bases de Datos que soportan documentos XML:

• BD XML-compatibles: desglosan un documento XML en su correspondiente modelo relacional o de objetos. • BD XML Nativas: respetan la estructura del documento, permiten hacer consultas sobre dicha estructura y recuperan el documento

tal y como fue insertado originalmente

2.-Bases de Datos Nativas XML. una NXD o BD XML debe cumplir las siguientes propiedades: • Define un modelo lógico de datos XML, • El documento XML es la unidad lógica de almacenamiento • No tiene ningún modelo de almacenamiento físico subyacente concreto. • Documentos y colecciones. Indexación. Identificador único. Consultas. Actualizaciones. Validación. Soportan transacciones,

2.1.-Estrategias de almacenamiento. • El almacenamiento basado en texto consiste en almacenar el documento XML entero en forma de texto • El almacenamiento basado en modelo consiste en definir un modelo de datos lógico, como DOM, para la estructura jerárquica de

los documentos XML

2.2.-Colecciones y documentos.

Una colección: Es un conjunto de documentos agrupados, normalmente, en función de la información que contienen. Un documento: Información XML

2.3.-Gestores nativos XML comerciales y libres Gestores nativos XML comerciales o de código propietario son:

• TaminoXML Server . Es un gestor nativo XML de la empresa SoftwareAG. • TEXTML de Isiasoft.

Gestores nativos XML libres o de código abierto son: • Qizx. Es una base de datos nativa XML escrita en Java. Almacena los datos utilizando una representación que se basa en el

modelo de XPath 2,0. • eXist. Utiliza un sistema de almacenamiento propio

3.-Instalación del gestor Qizx.

Page 49: Acceso a Datos - Resumen

3.1.- Gestionar la base de datos XML mediante QizxS tudio.

Page 50: Acceso a Datos - Resumen

4.- El lenguaje de consultas XQuery. Está basado en el lenguaje XPath, (XML Path Language), Es un lenguaje declarativo,

4.1.- Modelo de datos. El modelo de datos en que se sustenta XQuery es el modelo de datos de XPath. XPath modela un documento XML como una estructura jerárquica en forma de árbol. El árbol está formado por nodos, y hay siete tipos de nodos: raíz, elemento, texto, atributo, espacio de nombres, instrucción de procesamiento y comentario. Los principales nodos de la estructura jerárquica o en árbol en un documento XML son: (puedes verlos en la imagen ampliable superior)

• Nodo raíz o /. Es el primer nodo del documento. • Nodo elemento. Cualquier elemento de un documento XML. Cada nodo elemento posee un padre y puede o no tener hijos. En el

caso de que no tenga hijos, es un nodo hoja. • Nodo texto. Cualquier elemento del documento que no esté marcado con una etiqueta de la DTD del documento XML. • Nodo atributo. Un nodo elemento puede tener etiquetas que complementen la información de ese elemento. Esto sería un nodo

atributo.

4.2.- Caminos de localización. Hay dos tipos de caminos de localización: • Caminos relativos. • Caminos absolutos.

Los siguientes, son algunos ejemplos de caminos de localización: • cuadro selecciona los elementos cuadro hijos del nodo contextual. • cuadro//titulo selecciona los elementos titulo descendientes de los elementos cuadro hijos del nodo contextual. • selecciona todos los elementos hijos del nodo contextual. • @año selecciona el atributo año del nodo contextual • @* selecciona todos los atributos del nodo contextual • cuadro[1] selecciona el primer hijo cuadro del nodo contextual • cuadro[@año=1907] selecciona todos los hijos cuadro del nodo contextual que tengan un atributo año con valor 1907.

Page 51: Acceso a Datos - Resumen

4.3.- Primeras consultas XQuery. Una consulta XQuery es una expresión que lee una secuencia de datos en XML y devuelve como resultado otra secuencia de datos en XML, donde:

• Una secuencia es un conjunto ordenado de cero o más ítems. • Un ítem es cualquier tipo de nodo del árbol XML o un valor atómico.

Las funciones que se pueden invocar para referirnos a colecciones y documentos dentro de la BD son las siguientes: collection(camino de la colección) doc(camino del documento) Así por ejemplo: La consulta collection(/Books): devuelve el contenido de la colección de ruta absoluta /Books. La consulta doc(/Empresa.xml): devuelve el documento /Empresa.xml completo. La consulta collection(/Books)//book/title devuelve los nodos title de todos los libros (book) de la colección /Books Si se utilizan espacios de nombres o namespaces, entonces la consulta anterior se redactaría de la siguiente forma: declare namespace t = http://www.qizx.com/namespace/Tutorial; collection( /Books)//t:book/t:title La consulta doc(/Empresa.xml)//nombre devuelve todos los nodos nombre del documento /Empresa.xml

doc("Empresa.xml") collection("/Cursos") devuelve todos los documentos dentro de cursos collection("/Cursos")//curso/nombre consutar nombre de cada curso doc("/Cursos/BasesDeDatosXML.xml")//curso/profesor consultar nombre profesor del cuso BD de xml collection("/Cursos")//curso[plazas=20] cursos con 20 plazas collection("/Cursos")//curso[plazas=20]/nombre nombres de cursos de 20 plazas 4.4.- Expresiones FLWOR. Se trata de una expresión que permite la unión de variables sobre conjuntos de nodos y la iteración sobre el resultado. Las diferentes cláusulas de una expresión FLWOR son: For. Permite seleccionar los nodos que se quieren consultar, guardándose su valor en una variable (identificador que comienza por $). Al conjunto de valores de la variable se le llama tupla. Let. (opcional). Asocia valores a variables. Where (opcional). Permite filtrar los resultados según una condición. Order (opcional). Permite ordenar la secuencia de valores o resultados. Return . Genera los valores de salida o devueltos. for $curso in collection("/Cursos")//curso let $profe:=$curso/profesor where $curso/plazas=20 order by $curso/nombre return $curso/nombre La consulta devuelve los nombres de los cursos con 20 plazas, ordenados por nombre de curso for $curso in collection("/Cursos")//curso where $curso/precio/@cuota="mensual" order by $curso/nombre return $curso/nombre devuelve los nombres de los cursos con cuota mensua l (derecha). Como cuota es un atributo del elemento precio, recuerda que se le antecede con un carácter @. for $curso in collection("/Cursos")//curso where $curso/aula=2 order by $curso/nombre return $curso/nombre nombre de cursos que se dan en aula 2 for $curso in collection("/Cursos")//curso let $n:=($curso/nombre) where $curso/aula=2 order by $curso/nombre return data($n) nombre de cursos que se dan en aula 2 pero sin ense ñar etiquetas

Page 52: Acceso a Datos - Resumen

for $curso in collection("/Cursos")//curso let $n:=($curso/nombre) where $curso/aula=2 and $curso/precio[@cuota="mensu al"] and $curso/precio[@moneda="euro"] and $curso/precio<50 order by $curso/nombre return data($n) devuelve los nombre de cursos donde el aula 2 , pre cio de cuota mensual, y precio menor de 50 en euros ordenado por nombre y sin etiquetas 4.5.- XQuery Update Facility. XQuery Update Facility es una extensión de XQuery que permite la actualización de documentos mediante las cláusulas insert, delete, replace y rename. El funcionamiento de las cláusulas de XQuery Update es el siguiente: insert. Permite la inserción de uno o varios nodos antes (before) o después (after) del nodo indicado. También se puede insertar al principio (as first into) o al final del documento (as last into). delete. Elimina uno o varios nodos del documento. replace. Tiene dos funciones:

Modificar el valor del nodo Modificar el nodo completo

rename. Renombra un nodo (elemento, atributo o instrucciones de proceso) sin afectar a su contenido. Eliminar la empresa de id=2 en el documento Empresa.xml: delete node doc(Empresa.xml)//empresa[@id=2] Insertar el nodo tipoAccesible/tipo al final del documento /Aulas/aula3 insert node tipoAccesible/tipo as last into doc(Aul as/aula3.xml)//aula (:Sustituye el nombre de la empres de id=1 por "Emp resaAlfa" :) replace value of node doc("Empresa.xml")//empresa[@id="1"]/nombre with "EmpresaAlfa" (:insertar el nodo <gerente> en empresa de id=1:) insert node <gerente>Pedro Golondrino Verde</gerente> as last i nto doc("Empresa.xml")//empresa[@id="1"] (: cambiar el nombre del nodo <edificio> de cada do cumento de la colección Aulas por <lugar>:) for $x in collection("/Aulas/*")//aula/edificio return rename node $x as "lugar" (:Elimina el nodo <gerente> de empresa id=1 :) delete node doc("/Empresa.xml")//empresa[@id="1"]/gerente (:Inserta dos nodos :) insert node <empresa id="2"> <nombre>Empresa Z</nombre> <cif>B32674129</cif> <direccion> C\ Las Marinas 25</direccion> <localidad>Granada</localidad> <provincia>Granada</provincia> <cpostal>18004</cpostal> <telefono>952245689</telefono> <email>[email protected]</email> <web>www.dominioz.es</web> </empresa> after doc("Empresa.xml")//empresa[1], insert node <empresa id="4"> <nombre>Empresa Beta</nombre> <cif>B12374129</cif> <direccion> C\ Golondrina 45</direccion> <localidad>Granada</localidad> <provincia>Granada</provincia> <cpostal>18004</cpostal>

<telefono>952123456</telefono> <email>[email protected]</email> <web>www.dominiobeta.es</web> </empresa> as last into doc("Empresa.xml") borrar nodo con id=2 delete node doc("Empresa.xml")//empresa[@id=2] remplazar valor de un nodo replace value of node doc("Empresa.xml")//empresa[@id="1"]/email with "[email protected]", replace node doc("Empresa.xml")//empresa[@id="1"]/telefono with <telefono>888888</telefono> (: renombre el nodo 'empresa/web' poniendolo en may usculas:) for $x in doc("Empresa.xml")//empresa/web return rename node $x as upper-case(name($x)) (:Inserta nodo tipo en el documento aula3 :) insert node <tipo>Accesible</tipo> as last into doc("Aulas/aula3.xml")//aula

5.- Trabajar con colecciones y documentos desde Jav a. El API principal para trabajar con una base de datos XML de Qizx en un entorno de desarrollo Java, la proporciona el paquete com.qizx.api, A continuación te indicamos algunas de las clases e interfaces del paquete com.qizx.api de más interés:

• Interface Library. Representa una BD XML o una conexión a una BD XML. • Interface LibraryManager . Representa el gestor de las BDs. Permite gestionar grupos de BDs XML. • Clase Configuration. Inicializa y configura un LibraryManager, permitiendo su creación, apertura, etc.. • Interface LibraryMember . Representa un miembro de una BD XML, esto es, colección o documento. • Algunas sub_interfaces son: Collection, Document, NonXMLDocument . • Interface Collection . Para gestionar colecciones y sub_colecciones que contienen documentos XML. • Interface Document. Representa documentos XML bien formados y almacenados en una BD XML. • Al ser un LibraryMember (miembro de una Library), puede tener propiedades (metadatos) que pueden consultarse. • Clase Index. Para crear índices en una BD XML y especificar sus parámetros.

El paquete org.xml.sax. Proporciona las clases e interfaces para el API SAX, que es un componente de la API de Java para el procesamiento de documentos XML, como por ejemplo el interface XMLReader. El paquete com.qizx.xdm. Proporciona clases e interfaces para el análisis y procesamiento de documentos XML, como por ejemplo la clase DocumentParser.

Page 53: Acceso a Datos - Resumen

5.1.- Creación de la BD XML. Establecer conexiones. Crear la BD XML. El método createLibrary(String nombreBD, File directorioBD) crea la BD de nombre nombreBD localizada físicamente en el directorio directorioBD, directorio que debe existir y estar vacío. Si el parámetro directorioBD se pasa como valor null, la BD se creará en un subdirectorio del grupo con nombre nombreBD. Establecer conexión. El método openLibrary(String nombreBD) abre una nueva sesión o conexión a la BD de nombre nombreBD. //Metodo que obtiene la conexion a una bd XML private static Library obtenerBD(LibraryManager b dManager, String bdNombre) throws QizxException { //Abre una conexion a la BD XML de nombre bdNombre Library bd = bdManager.openLibrary(bdNombre); //Si no se ha abierto la BD (porque no existe) if (bd == null) { System.out.println("Creando BD XML '" + bdNom bre + "'..."); //Crea la BD XML bdManager.createLibrary(bdNombre, null); //Abre una conexion a la BD creada bd = bdManager.openLibrary(bdNombre); } //devuelve la conexion return bd; } 5.2.- Cerrar conexiones. Cerrar una conexión . El método Library.close() cierra la conexión a una BD particular. Si ha comenzado alguna transacción, no se podrá invocar close(), solo se podrá invocar commit() o rollback(), por lo que antes de invocar close(), debemos comprobar mediante el método Library.isModified() si la base de datos está inmersa en una transacción. Cerrar todas las conexiones. El método LibraryManager.closeAllLibraries(int tiempo) cierra todas las conexiones a BD del mismo grupo y libera los recursos. Mediante el parámetro int tiempo, se le puede indicar el tiempo en milisegundos que debe esperar el sistema antes de realizar la desconexión de aquellas Library //Metodo que cierra la conexion a la base de datos private static void cerrar(Library bd, LibraryMan ager bdManager) throws QizxException { //Si la base de datos esta inmersa en una trans accion if (bd.isModified()) { //deshace los cambios realizados por la trans accion bd.rollback(); } //cierra la conexion a la base de datos bd bd.close(); //cierra las bases de datos del grupo despues d e 10000 ms bdManager.closeAllLibraries(10000 /*ms*/); } 5.3.- Crear colecciones. createCollection(String ruta). Método de la interface Library. Crea una colección en la BD, cuya posición dentro de la base de datos es la que indica el parámetro ruta, siendo ruta una camino o path absoluto en la BD. createChildCollection(String nombre). Método de la interface Collection. Crea una sub_colección dentro de una colección, denominada nombre. Collection coleccion = bd.getCollection(ruta); //si no existe la coleccion, crea la coleccio n if (coleccion == null) { System.out.println("Creando coleccion '" + ruta + "'..."); coleccion = bd.createCollection(ruta); } 5.4.- Añadir documentos. ImportDocument(String documentoRuta, Tipo fuente). Método de la interface Library. Existen diferentes métodos para importar documentos XML a una BD XML, que se diferencian fundamentalmente en el Tipo de los documentos fuente indicados como segundo parámetro: java.io.File, java.net.URL, org.xml.sax.InputSource, etc. En todos los casos, la fuente debe contener un documento XML bien formado. Uno de estos métodos es:

Page 54: Acceso a Datos - Resumen

importDocument(String documentoRuta, File fichero). Importa un documento a la base de datos mediante un análisis o parsing de un fichero de texto XML ubicado en fichero. Si ya existe un documento con la misma ruta en la BD, se sustituye por los nuevos contenidos. Si la colección que contiene este documento no existe, se crea automáticamente. System.out.println("Importando '" + fichero + "' co mo documento '" + ruta + "'..."); //importa a bd, en la posicion indicada por r uta, el documento XML cuyo texto XML esta en ficher o bd.importDocument(ruta, fichero); Qizx incluye la biblioteca resolver.jar, conocida como resolución del catálogo XML, que forma parte del proyecto Apache XML Commons, de manera que todos los métodos Library.importDocument() son catálogos XML habilitados. Por lo tanto, basta con especificar el correspondiente importDocument() y dejar que mediante resolver.jar se realice la resolución, análisis o parsing.

5.5.- Creación de una BD XML e importación de colec ciones y documentos. Otros métodos útiles al llenar una Library con colecciones y documentos, o bien trasladar colecciones y documentos de una Library a otra son los siguientes: Library.getMember(String rutaAbsoluta): devuelve el miembro especficado en esa ruta de la BD, un LibraryMember (documento o colección), o bien null si no existe ese miembro. Library.getDocument(String rutaAbsoluta): devuelve el documento especficado en esa ruta de la BD, un Document o bien null si no existe. Library.getCollection(String rutaAbsoluta ): devuelve colección especficada en esa ruta de la BD, una Collection o bien null si no existe. Library.getName(): devuelve el nombre de la Library o base de datos. LibraryMember.getPath(): devuelve la ruta completa del miembro en esa BD. LibraryMember.getName(): devuelve el nombre del miembro dentro de su colección sin '/', excepto si es la colección raríz. LibraryMember.isDocument(): devuelve verdadero si el miembro es un documento. LibraryMember.isCollection(): devuelve verdadero si el miembro es una colección. Collection.getChildren(): devuelve un iterador, un LibraryMemberIterator, sobre los miembros de esa colección (documentos o colecciones). LibraryMemberIterator.iter.moveToNextMember(): devuelve verdadero si accede al siguiente miembro. LibraryMemberIterator.getCurrentMember(): devuleve el mimbro actual.

Page 55: Acceso a Datos - Resumen

/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package poner; import java.io.File; import java.io.FileFilter; /** * * @author IMCG */ //Clase que implementa un filtro para extensiones de ficheros class FiltroFichero implements FileFilter { //array para guardar las extensiones precedidas por '.' private String[] sufijo; private boolean noCoincide; public FiltroFichero(String patron) { noCoincide = false; //Guarda las extensiones de los ficheros a procesar String[] extension = patron.split(",");

sufijo = new String[extension.length]; //Guarda cada extensión, precedida por punto for (int i = 0; i < extension.length; ++i) { String ext = extension[i]; sufijo[i] = "." + ext; } } @Override public boolean accept(File file) { if (file.isDirectory()) { return true; } String nombre = file.getName(); boolean res = false; for (int i = 0; i < sufijo.length; ++i) { if (nombre.endsWith(sufijo[i])) { res = true; break; } } return noCoincide ? !res : res; } }

//Interfaces y clases para gestionar la BD XML, las colecciones y documentos import com.qizx.api.Collection; import com.qizx.api.Configuration; import com.qizx.api.Library; import com.qizx.api.LibraryManager; import com.qizx.api.LibraryMember; import com.qizx.api.QizxException; //Interfaces y clases para el procesamiento y análisis de documentos XML import org.xml.sax.SAXException; //Interfaces y clases para gestionar archvios y flujos de datos import java.io.File; import java.io.FileFilter; import java.io.IOException;

//Valores que se establecen para la creación de la base de datos XML que //se creará y los colecciones que contendrá //ruta del Grupo de bases de datos (Library Group):C:\dbxml

private static String directorioGrupoRoot = "c:\\ dbxml"; //Nombre de Bilioteca o BD (Library): Tutorial private static String bdNombre = "Tutorial"; //ruta raíz colecciones de la BD o Library private static String ruta = "/"; //ruta de las colecciones origen a importar private static String datosOrigenRoot ="C:\\qizx\\docs\\samples\\book_data\\"; //array con nombres de las colecciones origen a importar private static String[] datosOrigenNombre = {"Author Blurbs", "Authors", "Books", "Publishers "}; //filtro de extensiones de los ficheros (documentos) a importar: xml y xhtml private static String extensionFiltro = "xml,xhtm l";

private static LibraryManager obtenerBDManager(File directorioGrupo) throws IOException, QizxException { //objeto para gestionar el directorio asociado al grupo de bibliotecas //si existe el directorio asociado al grupo, devuelve el manejador //LibraryManager asociado

if (directorioGrupo.exists()) { return Configuration.openLibraryGroup(directo rioGrupo); //si no existe el directorio, intenta crearlo, y devuelve el manejador //LibraryManager asociado } else { if (!directorioGrupo.mkdirs()) { throw new IOException("no se puede crear di rectorio '" + directorioGrupo + "'"); } System.out.println("creando el Grupo de BDs e n '" + directorioGrupo + "'..."); return Configuration.createLibraryGroup(direc torioGrupo); } }

Page 56: Acceso a Datos - Resumen

//Método que obtiene la conexión a una bd XML private static Library obtenerBD(LibraryManager b dManager, String bdNombre) throws QizxException { //Abre una conexión a la BD XML de nombre bdNombre

Library bd = bdManager.openLibrary(bdNombre); //Si no se ha abierto la BD (porque no existe) if (bd == null) { System.out.println("Creando BD XML '" + bdNom bre + "'..."); //Crea la BD XML bdManager.createLibrary(bdNombre, null); //Abre una conexión a la BD creada bd = bdManager.openLibrary(bdNombre); } //devuelve la conexión return bd; }

private static void llenar(Library bd, File fiche ro, FileFilter filtro, String ruta) throws IOException, QizxException, SAXExc eption { if (fichero.isDirectory()) { //si es directorio //obtiene la colección de la BD bd situada en esa ruta Collection coleccion = bd.getCollection(ruta) ; //si no existe la colección, crea la colección if (coleccion == null) { System.out.println("Creando colección '" + ruta + "'..."); coleccion = bd.createCollection(ruta); }

//Guarda en files, los ficheros con extensión coincidente en el filtro File[] files = fichero.listFiles(filtro); if (files == null) { throw new IOException("Error al listar dire ctorio '" + fichero + "'"); } //para cada fichero lo incluye en su correspondiente colección de la BD for (int i = 0; i < files.length; ++i) { File file = files[i]; llenar(bd, file, filtro, ruta + "/" + file. getName()); } //si no es un directorio, lo importa como documento XML analizándolo } else { System.out.println("Importando '" + fichero + "' como documento '" + ruta + "'..."); //importa a bd, en la posición indicada por ruta, el documento XML // cuyo texto XML está en fichero bd.importDocument(ruta, fichero); } }

METODO MAIN public static void main(String[] args) throws IOException, QizxException, SAXException { //variables locales String nombre; //filtro de ficheros FileFilter filtro = new FiltroFichero(extensionFilt ro); //crea el objeto file directorioGrupo apuntando a e sa ruta File directorioGrupo = new File(directorioGrupoRoot ); //obtiene un grupo de bibliotecas LibraryManager bdManager = obtenerBDManager(directo rioGrupo); //crea una bilbioteca o BD Library bd = obtenerBD(bdManager, bdNombre); //crea objeto miembro con la ruta absoluta LibraryMember miembroBD = bd.getMember(ruta); //comprueba si el miembro es una colección válida boolean miembroEsColeccion = (miembroBD != null && miembroBD.isCollection()); //si no es una colección, cierra la BD bd y el grupo bdManager, y //muestra mensaje if (!miembroEsColeccion) { cerrar(bd, bdManager); System.out.println("'" + ruta + "', no existe o no es una colección"); }

try { //para cada miembro for (int i = 0; i < datosOrigenNombre.length; i++) { //accede a la colección fuente para guardar su ruta y nombre File fichero = new File(datosOrigenRoot + datosOrig enNombre[i]); //si es un miembro colección, guarda en 'nombre' su nombre antecedido por / if (miembroEsColeccion) { nombre = ruta + datosOrigenNombre[i]; //el método llenar() vincula a la base de datos bd, la coleccion //localizada en 'fichero', denominada 'nombre', conteniendo los //documentos según filtro llenar(bd, fichero, filtro, nombre); } } System.out.println("Confirmados cambios..."); //Confirma las operaciones de la transacción bd.commit(); } catch (Exception ex) { System.err.println(ex); System.exit(1); } finally { //cierra o realiza la desconexión de la BD bd cerrar(bd, bdManager); } }

METODO CERRAR CONEXIÓN A BD //método que cierra la conexión a la base de datos private static void cerrar(Library bd, LibraryMan ager bdManager) throws QizxException { //Si la base de datos está inmersa en una transacción if (bd.isModified()) { //deshace los cambios realizados por la transacción bd.rollback(); } //cierra la conexión a la base de datos bd bd.close(); //cierra las bases de datos del grupo después de 10000 ms bdManager.closeAllLibraries(10000 /*ms*/); } }

Page 57: Acceso a Datos - Resumen

5.6.- Eliminar colecciones y documentos. Para eliminar colecciones y documentos se pueden utilizar los métodos: deleteMember(String ruta). Método de la interface Library. Elimina el miembro de la BD indicado mediante el parámetro ruta, que será la ruta absoluta dentro de la BD. Si se trata de una colección, elimina también de forma recursiva todos sus documentos y sub_colecciones. delete(). Método de la interface LibraryMember. Es equivalente al método anterior, es decir, es equivalente a Library.deleteMember(getPath()). ¿Y cómo eliminar toda la base de datos? Para ello, puedes utilizar: el método deleteLibrary(String bdNnombre) de la interface LibraryManager, que elimina físicamente la BD. package delete; /** * * @author IMCG */ import com.qizx.api.Configuration; import java.io.File; import com.qizx.api.QizxException; import com.qizx.api.Library; import com.qizx.api.LibraryManager; public class Delete { //ruta del Grupo de bibliotecas: private static String directorioGrupoRoot = "c:\\ dbxml"; //Nombre de la BD o Lidrary private static String bdNombre = "Libros"; //colecciones a borrar private static String[] coleccionNombre = {"/Author s","/Books"}; //sin nombres, borra toda la base de datos... // private static String[] coleccionNombre = {}; /**************************************************************************** * * @param args * @throws QizxException */ public static void main(String[] args) throws QizxException { //variables locales String ruta; //crea el objeto file 'directorioGrupo' apuntando a esa ruta File directorioGrupo = new File(directorioGrupo Root); // Conexión o apertura del gestor del grupo; LibraryManager bdManager = Configuration.openLibraryGroup(director ioGrupo); //si no se especifica ninguna colección if (coleccionNombre.length == 0) { //borra toda la base de datos System.out.println("Borrando toda la base de datos '" + bdNombre + "'...");

if (!bdManager.deleteLibrary(bdNombre)) { System.out.println("Base de datos '" + bdNo mbre + "' no encontrada"); } bdManager.closeAllLibraries(10000 /*ms*/); } else { //Abre la base de datos Library bd = bdManager.openLibrary(bdNombre); try { //borra las colecciones especificadas for (int i = 0; i < coleccionNombre.length; i++) { ruta = coleccionNombre[i]; if (bd.deleteMember(ruta)) { System.out.println("Borrando colección '" + ruta + "' de la BD '" + bdNombre + "'. .."); }else System.out.println("Colección '" + ruta + "' de la BD '" + bdNombre + "' no encontrada") ; } System.out.println("Confirmando los cambios ..."); bd.commit(); } finally { //cierra la base de datos cerrar(bd, bdManager); } } } //método que cierra la conexión a la base de datos private static void cerrar(Library bd, LibraryMan ager bdManager) throws QizxException { //si la base de datos está inmersa en una transacción if (bd.isModified()) { //deshacer las opersciones de la transacción bd.rollback(); } //cierra la conexión a la base de datos bd.close(); //cierra todas las bases de datos del grupo en 10000 ms bdManager.closeAllLibraries(10000); } }

5.7.- Consultar documentos. La respuesta es sí, pero será necesario realizar los siguientes pasos para procesar una consulta desde Java:

• Compilar la consulta o expresión XQuery mediante el método Library.compileExpression(). Si la compilación se realiza sin errores (CompilationException), devolverá un objeto Expression.

• Evaluar la expresión o consulta compilada mediante el método Expression.evaluate(). Si la evaluación se realiza sin errores (EvaluationException), devolverá el resultado en forma de un ItemSequence.

• Iterar sobre la secuencia obtenida. Un ItemSequence permite iterar sobre una secuencia de Items, donde un Item es un valor atómico o un nodo XML.

Un iterador siempre tiene un método moveToNextItem() que mueve la posición del cursor al siguiente elemento y un getCurrentItem() que devuelve el elemento encontrado en la posición actual del cursor. Por tanto, los pasos anteriores suponen las siguientes sentencias de código: Expression exprcompilada = bd.compileExpression(scr iptConsulta); ItemSequence resultado = exprcompilada.evaluate(); while (resultado.moveToNextItem()) { Item resul = resul.getCurrentItem(); /* Manipular resultado*/ } Otros métodos útiles en el procesamiento de una consulta son los siguientes:

• El método Item.getNode() que obtiene el valor de un nodo XML (objeto tipo Node) tras la evaluación de una consulta XQuery compilada.

• El método XMLSerializer.serializeToString() que permite serializar como una cadena un nodo XML. • El método Expression.bindImplicitCollection() que permite escribir consultas con rutas o paths las cuales no van precedidas por

las expresiones collection(XXX) o doc(YYY). Por ejemplo, usando bindImplicitCollection() se puede utilizar como expresión: //book/title en vez de collection(/Books)//book/title

• El método Expression.bindVariable() que permite vincular una secuencia de Items a una variable. package query; //Para manejar ficheros y stream import java.io.IOException; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader;

//para manejar BD XML import com.qizx.api.Library; import com.qizx.api.LibraryManager; import com.qizx.api.Configuration; //para manejar excepciones import com.qizx.api.QizxException;

Page 58: Acceso a Datos - Resumen

import com.qizx.api.Message; import com.qizx.api.CompilationException; //para gestionar consultas XQuery import com.qizx.api.Item; import com.qizx.api.ItemSequence; import com.qizx.api.Node; import com.qizx.api.Expression; import com.qizx.api.util.XMLSerializer; /****************************************************************************** * clase para ejecutar scripts de consulta XQuery en una BD de XML * * @author IMCG */ public class Query { //ruta del Grupo de BD private static String directorioGrupoRoot = "c:\\ dbxml"; //Library o base de datos private static String bdNombre = "Cursillos"; //"Tutorial"; //ruta donde se almacenan los scripts con consultas XQuery private static String directorioScriptsRoot = "C:\\BDCursillosXML\\cursillos_query\\"; //directorio de scripts BD Tutorial // "C:\\qizx\\docs\\samples\\book_queries\\"; //nombres de los scripts con consultas XQuery - ficheros .xq private static String[] scriptNombre = { "10CursosAula2Nombre_flwor.xq", "11CursosAula2Condiciones_flwor.xq"}; //Si cambias a la BD Tutorial, los scripts son {"7.xq", "8.xq"}; public static void main(String[] args) throws IOException, QizxException { //variables para controlar el rango de resultados de la consulta que se //imprimen int min = 0; int max = Integer.MAX_VALUE; //fichero para recoger cada script XQuery de consulta File scriptFile; //objeto file 'directorioGrupo' apuntando a la ruta del Library Group File directorioGrupo = new File(directorioGrupo Root); // Conexión o apertura del gestor del grupo LibraryManager bdManager = Configuration.openLibraryGroup(director ioGrupo); //Conexión a la BD Library bd = bdManager.openLibrary(bdNombre); try { //Para cada script con consulta XQuery for (int i = 0; i < scriptNombre.length; ++i) { //recoge la ruta del fichero de script con consulta XQuery scriptFile = new File(directorioScriptsRoot + scriptNombre[i]); //mensaje indicando el script XQuery que se ejecutará System.out.println("Ejecutando '" + scriptF ile + "'..."); //carga el contenido del script XQuery en una cadena String consultaXquery = cargaScript(scriptF ile); //imprime la expresión de consulta XQuery System.out.println("---\n" + consultaXquery + "\n---"); //compila la consulta, almacenado resultado en expr Expression expr = compileExpression(bd, con sultaXquery); //evalúa la consulta para mostrar resultados en el rango [min, max] evaluarExpression(expr, min, max); } } finally { //cierra conexión con BD cerrar(bd, bdManager); } } //Método para compilar la consulta controlando errores private static Expression compileExpression(Libra ry bd, String consultaXquery) throws IOException, QizxException { Expression expr; try { expr = bd.compileExpression(consultaXquery); } catch (CompilationException e) {

//mensajes de error devueltos tras la compilación Message[] messages = e.getMessages(); for (int i = 0; i < messages.length; ++i) { System.out.println(messages[i].toString()); } throw e; } //devuelve consulta compilada return expr; } //método que evalúa la consulta y muestra un rango de resultados entre min y max private static void evaluarExpression(Expression expr, int min, int max) throws QizxException { //evalúa la consulta compilada obteniendo un ItemSequence ItemSequence results = expr.evaluate(); if (min > 0) { results.skip(min); } XMLSerializer serializer = new XMLSerializer(); serializer.setIndent(2); int count = 0; while (results.moveToNextItem()) { Item result = results.getCurrentItem(); //genera el número de orden para cada resultado de la consulta System.out.print("[" + (min + 1 + count) + "] "); //imprime uno de los resultados de la consulta mostrarResultado(serializer, result); System.out.println(); ++count; //si ya se han imprimido un total de max resultados, no imprime más if (count >= max) { break; } } System.out.flush(); } //Método para mostrar el resultado private static void mostrarResultado(XMLSerialize r serializer, Item result) throws QizxException { //Si no es un nodo del árbol XML, escribe el resultado como cadena if (!result.isNode()) { System.out.println(result.getString()); return; } //obtiene el nodo XML del resultado, el Item Node node = result.getNode(); //prepara la serialización de otro arbol XML serializer.reset(); //El método XMLSerializer.serializeToString se utiliza para obtener la //representación de cadena de un nodo. String xmlForm = serializer.serializeToString(n ode); //Imprime el nodo del resultado como cadena System.out.println(xmlForm); } //método que cierra la conexión a la base de datos private static void cerrar(Library bd, LibraryMan ager bdManager) throws QizxException { //si la basede datos esta inmersa en una transacción, deshacer los cambios if (bd.isModified()) { bd.rollback(); } //cerrar conexión con base de datos bd.close(); //cerrar las bases de datos del grupo dentro de 100000 ms bdManager.closeAllLibraries(10000); } //método que devuelve la consulta XQuery (almacenada en fichero file) //en una cadena private static String cargaScript(File file) throws IOException { //Obtiene el contenido del fichero

Page 59: Acceso a Datos - Resumen

InputStreamReader in = new InputStreamReader(new FileInputStre am(file), "UTF-8"); //cadena para construir la cadena resultante StringBuilder build = new StringBuilder(); //array de tamaño suficiente para alamcenar los caracteres del script char[] chars = new char[8192]; int count; try { //mientras hay caracteres en el contenido del fichero //los añade a la cadena while ((count = in.read(chars, 0, chars.lengt h)) != -1) {

if (count > 0) { build.append(chars, 0, count); } } } finally { in.close(); } return build.toString(); } }

5.8.- Actualizar documentos. Aunque dependiendo del sistema de BD XML pueden existir diferentes formas de actualizar documentos, la manera más sencilla de hacerlo es mediante Update XQuery, la extensión del lenguaje XQuery que permite inserciones, eliminaciones y modificaciones de nodos de los documentos XML. En el siguiente enlace dispones de un ejemplo detallado de actualización XQuery desde una aplicación Java. La actualización consiste en añadir dos nodos empresa al documento /Empresa.xml de la BD Cursillos.package edit; import com.qizx.api.ItemSequence; import java.io.IOException; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import com.qizx.api.QizxException; import com.qizx.api.Message; import com.qizx.api.CompilationException; import com.qizx.api.Configuration; import com.qizx.api.Expression; import com.qizx.api.LibraryMember; import com.qizx.api.Library; import com.qizx.api.LibraryManager; /****************************************************************************** * clase para ejecutar scripts de actualización XQuery * * @author IMCG */ public class XUpdate { //ruta del Grupo de BD private static String directorioGrupoRoot = "c:\\ dbxml"; //Base de Datos private static String bdNombre = "Cursillos"; //fichero con script de actualización private static String ficheroScriptRoot = "C:\\BDCursillosXML\\cursillos_query\\12u pdate_insert.xq"; // "C:\\BDCursillosXML\\cursillos_query\\13update_delete.xq"; // "C:\\BDCursillosXML\\cursillos_query\\14update_replace_node_value.xq"; // "C:\\BDCursillosXML\\cursillos_query\\15update_rename_varios_nodos.xq"; /**************************************************************************** * * @param args * @throws IOException * @throws QizxException */ public static void main(String[] args) throws IOException, QizxException { //variables locales LibraryMember queryRoot=null; File scriptFile; //crea el objeto file apuntando a esa ruta File directorioGrupo = new File(directorioGrupo Root); // Conexión o apertura del gestor del grupo de BD LibraryManager bdManager = Configuration.openLibraryGroup(director ioGrupo); //abre conexión a la BD Library bd = bdManager.openLibrary(bdNombre); try { scriptFile = new File(ficheroScriptRoot); //carga el contenido del fichero de script en una cadena String script = cargarScript(scriptFile); System.out.println("---\n" + script + "\n---" );

//compila la consulta de actualización Expression expr = compilaExpression(bd, scrip t, queryRoot); //evalúa la consulta compilada evaluaExpression(expr, bd); } finally { cerrar(bd, bdManager); } } /**************************************************************************** * Compila la consulta * * @param bd: base de datos * @param script: script con consulta de actualización * @param queryRoot: camino localización consulta * @return expression compilada * @throws IOException * @throws QizxException */ private static Expression compilaExpression(Library bd, String script, LibraryMember queryRoot) throws IOException, QizxException { Expression expr; try { expr = bd.compileExpression(script); } catch (CompilationException e) { Message[] messages = e.getMessages(); for (int i = 0; i < messages.length; ++i) { System.out.println(messages[i].toString()); } throw e; } if (queryRoot != null) { expr.bindImplicitCollection(queryRoot); } return expr; } /**************************************************************************** * Evalúa la expresión compilada * * @param expr: expresión compilada * @param library: base de datos * @throws QizxException */ private static void evaluaExpression(Expression e xpr, Library library) throws QizxException { /*ItemSequence results =*/ ItemSequence evaluate = expr.evaluate(); // confirmar cambios en BD library.commit(); } /**************************************************************************** * Cierra la base d edatos y el su grupo * @param bd * @param bdManager * @throws QizxException */ private static void cerrar(Library bd, LibraryMan ager bdManager) throws QizxException {

Page 60: Acceso a Datos - Resumen

bd.close(); bdManager.closeAllLibraries(10000 /*ms*/); } /**************************************************************************** * Carga el script de consulta de actualización en una cadena */ private static String cargarScript(File file) throws IOException { InputStreamReader in = new InputStreamReader(new FileInputStre am(file), "UTF-8"); StringBuilder build = new StringBuilder(); char[] chars = new char[8192];

int count; try { while ((count = in.read(chars, 0, chars.lengt h)) != -1) { if (count > 0) { build.append(chars, 0, count); } } } finally { in.close(); } return build.toString(); } }

El siguiente ejemplo actualiza un documentos de la colección Authors, insertando un nuevo pseudónimo al autor que se pasa como parámetro al script. El script de actualización está almacenado dentro del directorio de instalación de Qizx: docs\samples\programming\edit\pseudonym.xq package update_2; import java.io.IOException; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import com.qizx.api.QizxException; import com.qizx.api.Message; import com.qizx.api.CompilationException; import com.qizx.api.Configuration; import com.qizx.api.QName; import com.qizx.api.Expression; import com.qizx.api.LibraryMember; import com.qizx.api.Library; import com.qizx.api.LibraryManager; /****************************************************************************** * clase para ejecutar scripts de actualización Update XQuery */ public class Main { //ruta del Grupo de bibliotecas: db private static String directorioGrupoRoot = "c:\\ dbxml"; //Bilioteca o base de datos: Tutorial private static String bdNombre = "Tutorial"; //nombre de la colección que se quiere modificar private static String coleccionNombre = "Authors" ; //Ruta del script de actualización //recuerda corregir, si hay algún QName con Q minúscula y poner a mayúscula private static String ficheroScriptRoot = "C:\\qizx\\docs\\samples\\programming\\ed it\\pseudonym.xq"; //nombre de las variables requeridas por el script private static String[] varName = {"authorName", "pseudo"}; //valores de las varibales requeridas por el script private static String[] varValue = {"Philip José Farmer", "Kilgort Trout"}; //para ejecutar este ejemplo varias veces hay que poner un pseudónimo //distinto cada vez, porque no se aceptan pseudónimos duplicados; o bien, //volver a colocar los datos originates ejecutando Qizx_put */ public static void main(String[] args) throws IOException, QizxException { //variables locales int length; File scriptFile; LibraryMember queryRoot = null; //array de variables QName - nombre calificado QName[] varQName; //crea el objeto file apuntando a esa ruta File directorioGrupo = new File(directorioGrupo Root); // Conexión o apertura del gestor del grupo de BD LibraryManager bdManager = Configuration.openLibraryGroup(director ioGrupo); //conexión a la BD Library bd = bdManager.openLibrary(bdNombre); length = varName.length; //crea tantos objetos QName como elementos tenga el array varName varQName = new QName[length];

//Para cada elemento en varName for (int i = 0; i < length; i++) { //construye un QName válido o null varQName[i] = procesaQName(bd, varName[i]); } //comprueba la existencia de la colección if (coleccionNombre != null) { queryRoot = bd.getMember(coleccionNombre); if (queryRoot == null) { cerrar(bd, bdManager); System.out.println("'" + coleccionNombre + "no encontrada"); } } try { scriptFile = new File(ficheroScriptRoot); //carga el script de actualización String script = cargarScript(scriptFile); System.out.println("---\n" + script + "\n---" ); //compila la expresión de consulta Expression expr = compilarExpresion(bd, scrip t, queryRoot, varQName, varValue); //evalúa la expresión compilada evaluarExpresion(expr, bd); } finally { cerrar(bd, bdManager); } } //Método para procesar y generar un QName válido o null private static QName procesaQName(Library bd, Str ing qName) { int length = qName.length(); if (length == 0) { return null; } int pos; if (qName.charAt(0) == '{' && (pos = qName.last IndexOf('}')) > 0) { if (pos + 1 == length) { return null; } String uri = qName.substring(1, pos); String localPart = qName.substring(pos + 1); if (uri.length() == 0) { return bd.getQName(localPart); } else { return bd.getQName(localPart, uri); } } else { if (qName.startsWith("xml:")) { String localPart = qName.substring(4); return bd.getQName(localPart, "http://www.w3.org/XML/1998/namespa ce"); } else { return bd.getQName(qName); } } } /**************************************************************************** * Compila la expresión de actualización */

Page 61: Acceso a Datos - Resumen

private static Expression compilarExpresion(Libra ry bd, String script, LibraryMember queryRoot, QName[] varNames, String[] varValues) throws IOException, QizxException { Expression expr; try { expr = bd.compileExpression(script); } catch (CompilationException e) { Message[] messages = e.getMessages(); for (int i = 0; i < messages.length; ++i) { System.out.println(messages[i].toString()); } throw e; } if (queryRoot != null) { expr.bindImplicitCollection(queryRoot); } if (varNames != null) { for (int i = 0; i < varNames.length; ++i) { expr.bindVariable(varNames[i], varValues[i] , /*type*/ null); } } return expr; } /**************************************************************************** * Evalúa la consulta de actualización compilada */ private static void evaluarExpresion(Expression e xpr, Library library) throws QizxException { /*ItemSequence results =*/ expr.evaluate(); //confirma transacción

library.commit(); } /**************************************************************************** * Cierra la base de datos y todas las del grupo */ private static void cerrar(Library bd, LibraryMan ager bdManager) throws QizxException { bd.close(); bdManager.closeAllLibraries(10000 /*ms*/); } /**************************************************************************** * Carga el script de actualización */ private static String cargarScript(File file) throws IOException { InputStreamReader in = new InputStreamReader(new FileInputStre am(file), "UTF-8"); StringBuilder buffer = new StringBuilder(); char[] chars = new char[8192]; int count; try { while ((count = in.read(chars, 0, chars.lengt h)) != -1) { if (count > 0) { buffer.append(chars, 0, count); } } } finally { in.close(); } return buffer.toString(); } }

6.- Indexación. De manera predeterminada, Qizx indexa la mayor parte de la información disponible en documentos XML: elementos, atributos, nodos, y texto completo. Esto se realiza automáticamente. Qizx soporta los siguientes tipos de índices:

• Índices elemento. Dado un nombre de elemento, este índice devuelve todos los elementos XML en todos los documentos de una BD con este nombre.

• Índices de atributos. Dado un nombre de atributo y un valor, este índice devuelve todos los elementos que tienen un atributo con este nombre y valor. Hay tres tipos de índices de atributos, de acuerdo con el tipo del valor del atributo: texto, numéricos y de fecha / hora. o Texto. Por defecto, todos los valores de los atributos están indexados como cadenas o texto. o Numérico. Si el valor se puede convertir al tipo double, entonces se añade como índice atributo numérico. o Fecha/Hora. Si el valor se puede convertir al tipo date-time, entonces se añade como índice date/time.

• Índices de contenido simple. Dado un nombre de elemento y un valor, este índice devuelve todos los elementos que tienen un contenido simple correspondiente a este valor.

• Índices Full-text. Dada una palabra, este índice devuelve todos los nodos de texto que contienen una ocurrencia de esta palabra.

6.1.- Especificaciones de indexado. Una especificación de indexado es un documento XML con las siguiente estructura: • El elemento raíz tiene el nombre indexing. • El elemento indexing contiene los atributos que definen las condiciones generales. • Contiene una lista de reglas aplicables a los elementos o atributos.

Ejemplo de xml de endexacion: <indexing xmlns:t="http://www.qizx.com/namespace/Tu torial"> <!-- Default rules --> <element as="numeric+string"/> <element as="date+string" /> <element as="string" /> <attribute as="numeric+string" /> <attribute as="date+string" /> <attribute as="string" /> <!-- Custom rules --> <element name="t:birthDate" context="t:author" as="date" sieve="com.qizx.api.util.text. FormatDateSieve" format="MMMM d, yyyy" locale="en-US" tim ezone="GMT" /> <element name="t:publicationDate" context="t:book " as="numeric" sieve="reindex.RomanNumberS ieve" /> </indexing>

Page 62: Acceso a Datos - Resumen

6.2.- Re-Indexar una BD desde Java. Para reindexar una BD XML de Qizx será necesario un objeto de la clase com.qizx.api.Indexing. Cada objeto Indexing es una especificación de indexado con las reglas y parámetros necesarios para construir índices en una BD o Library. El método Indexing.parse(InputSource source) analiza la especificación de indexado contenida en el texto XML del parámetro source. Además se necesitan los siguientes métodos de Library: Library.setIndexing(Indexing ind). Define las reglas de indexado para esa BD o Library, especificadas en el parámetro ind. Normalmente va seguida de un reindexado, que se realiza mediante el método reIndex(). Library.reIndex(). Reconstruye y reorganiza los índices de una BD package reindex; import java.io.IOException; import java.io.File; import org.xml.sax.SAXException; import org.xml.sax.InputSource; import com.qizx.api.QizxException; import com.qizx.api.Indexing; import com.qizx.api.Library; import com.qizx.api.LibraryManager; import com.qizx.api.LibraryManagerFactory; public class ReIndex { public static void main(String[] args) throws IOException, SAXException, QizxExcep tion { if (args.length != 3) { usage(); } File storageDir = new File(args[0]); String libName = args[1]; File indexingFile = new File(args[2]); LibraryManagerFactory factory = LibraryManagerFactory.getInstance(); LibraryManager libManager = factory.openLibraryGroup(storageDir); Library lib = libManager.openLibrary(libNam e); try { verbose("Loading indexing specification s from '" + indexingFile + "'..."); Indexing indexing = loadIndexing(indexi ngFile); lib.setIndexing(indexing); verbose ("Re-indexing library '" + libN ame + "'..."); lib.reIndex();

} finally { shutdown(lib, libManager); } } private static void usage() { System.err.println( "usage: java ReIndex libraries_storage_di r library_name" + " indexing_spec\n" + " libraries_storage_dir Directory contai ning libraries.\n" + " library_name Name of library being que ried.\n" + " indexing_spec File containing the inde xing specification."); System.exit(1); } private static Indexing loadIndexing(File file) throws IOException, SAXException, QizxExcep tion { Indexing indexing = new Indexing(); String systemId = file.toURI().toASCIIStrin g(); indexing.parse(new InputSource(systemId)); return indexing; } private static void shutdown(Library lib, Libra ryManager libManager) throws QizxException { lib.close(); libManager.closeAllLibraries(10000 /*ms*/); } private static void verbose(String message) { System.out.println(message); } }

7.- Gestión de transacciones. 7.1.- Los métodos commit() y rollback(). Los métodos para gestionar transacciones en Qizx los proporciona la interface Library y son: commit(). Confirma la transacción actual. Si este método se completa sin errores, las actualizaciones realizadas en la transacción garantizan la persistencia, y se hacen visibles a otras sesiones. rollback(). Cancela la transacción actual. En Qizx, todas las operaciones de actualización sobre una BD o Library, para que tengan efecto permanente, deben ir seguidas de una confirmación o commit(). Por ejemplo, después de realizar una consulta de actualización, para que permanezcan los cambios es necesario realizar un commit(), tal y como refleja el siguiente código. private static void evaluaExpression(Expression exp r, Library library) throws QizxException { /*ItemSequence results =*/ ItemSequence evaluate = expr.evaluate(); // confirmar cambios en BD library.commit(); } Por otra parte, al cerrar una conexión a una BD es necesario comprobar si hay alguna transacción en curso, de manera que, si hay alguna en curso, para poder cerrar la BD habrá que cancelar la transacción actual. El código que refleja de forma clara este hecho es el siguiente: //Si la base de datos esta inmersa en una transaccion if (bd.isModified()) { //deshace los cambios realizados por la transaccion bd.rollback(); } //cierra la conexion a la base de datos bd bd.close(); 8.- Tratamiento de excepciones. En Qizx, la clase que contiene los diferentes tipos de excecpiones es QizxException del paquete com.qizx.api. Esta clase extiende a Exception, superclase de todas las excepciones del API y sus constructores son: QizxException(String message). Construye un mensaje con la razón o causa de excepción. El código del error es indefinido. QizxException(String message, Throwable cause). Construye un mensaje con la razón o causa de excepción, y la excepción. El código del error es indefinido. QizxException(QName errorCode, String message ). Construye un mensaje con la razón de la excepción y un código de error XQuery. private static void cerrar(Library bd, LibraryManag er bdManager) throws QizxException { bd.close();

Page 63: Acceso a Datos - Resumen

bdManager.closeAllLibraries(10000 /*ms*/); } Tres subclases directas de QizxExcepction son: CompilationException. Excepciones debidas a errores en la compilación de consultas a la BD. DataModelException. Excepciones debidas a manipulación de documentos XML en la BD. EvaluationException. Excepciones debidas a errores en la ejecución de una consulta.

8.1.- Excepciones en el procesamiento de consultas. Estas excepciones las puedes gestionar mediante dos subclases directas de la clase QizxException:

• CompilationException . Se lanza cuando hay errores de compilación en una expresión de consulta. Los mensajes asociados pueden ser: el error, una advertencia o warning, y la descripción o detalle del error. Podemos gestionar esos errores mediante los siguientes métodos o getErrorCount() devuelve el número actual de errores. o getMessages() devuelve una lista de errores o warnings

• EvaluationException. Se lanza cuando hay errores en la ejecución de una expresión XQuery. Por ejemplo, el siguiente código muestra un método que compila una expresión de consulta y controla posibles errores, almacenando en un array de tipo Message los posibles errores de compilación, Observa que en este caso, el tratamiento de la excepción se realiza mediante un bloque try-catch, y que en caso de que se produzca se captura la lista de errores. //Metodo para compilar la consulta controlando errores private static Expression compileExpression(Libra ry bd, String consultaXquery) throws IOException, QizxException { Expression expr; try { expr = bd.compileExpression(consultaXquery); } catch (CompilationException e) { //mensajes de error devueltos tras la compilacion Message[] messages = e.getMessages(); for (int i = 0; i < messages.length; ++i) { System.out.println(messages[i].toString()); } throw e; } //devuelve consulta compilada return expr; } Otro Ejemplo package photoassociation; import java.io.*; import java.net.MalformedURLException; import java.net.URL; import javax.servlet.*; import javax.servlet.http.*; import com.qizx.api.CompilationException; import com.qizx.api.Configuration; import com.qizx.api.Expression; import com.qizx.api.Item; import com.qizx.api.ItemSequence; import com.qizx.api.Library; import com.qizx.api.LibraryManager; import com.qizx.api.LibraryManagerFactory; import com.qizx.api.LibraryMember; import com.qizx.api.Message; import com.qizx.api.Node; import com.qizx.api.QName; import com.qizx.api.QizxException; import com.qizx.api.XQuerySession; import com.qizx.api.XQuerySessionManager; import com.qizx.api.util.DefaultModuleResolver; import com.qizx.api.util.XMLSerializer; public class XQueryServlet extends HttpServlet { private static final long serialVersionUID = -9186875057311859285L; { System.setProperty("javax.xml.parsers.DocumentBuil derFactory", "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl "); System.setProperty("javax.xml.parsers.SAXParserFac tory", "org.apache.xerces.jaxp.SAXParserFactoryImpl"); } public void init(ServletConfig servletconfig) thro ws ServletException { super.init(servletconfig); }

public void doGet(HttpServletRequest httpservletrequest,HttpServletResponse httpservletr esponse) throws IOException { doPost(httpservletrequest, httpservletresponse); } public void doPost(HttpServletRequest httpservletr equest, HttpServletResponse httpservletresponse) throws IOE xception { httpservletresponse.setContentType("text/xml; UTF-8"); PrintStream out = new PrintStream(httpservletresponse.getOutputStream()); XQuerySessionManager manager = null; XQuerySession session = null; String date = httpservletrequest.getParameter("date"); String text = httpservletrequest.getParameter("text"); try { Configuration.set(Configuration.ALLOWED_CLASSES, "java.io.StringReader," + "java.io.StringWriter," + "java.io.Reader," + "java.net.URL," + "java.util.Map," + "java.util.List," + "java.util.HashMap," + "java.lang.String," + "photoassociation.qizx.UtilityFunctions,"+ "java.lang.Math"); manager = Configuration.createSessionManager("http://aphoteg. googlecode.com/hg/src/xquery/"); manager.setModuleResolver(new MyModuleResolver()); session = manager.createSession(); session.getContext().declarePrefix("flickr", "http://www.flickr.com/services/api/");

Page 64: Acceso a Datos - Resumen

session.getContext().declarePrefix("geoplanet", "http://developer.yahoo.com/geo/"); session.getContext().declarePrefix("photoAssociati on", "http://web.tagus.ist.utl.pt/~rui.candeias/"); BufferedReader input = new BufferedReader(new InputStreamReader(this.getServletContext().getResou rceAsStream("/WEB-INF/classes/xquery/xquery2compile.xq"))); String line = null; StringBuffer sBuffer = new StringBuffer(); while ( (line = input.readLine()) != null ) { sBuffer.append(line); sBuffer.append("\n"); } String xqy = sBuffer.toString(); QName[] varNames = { session.getQName("text") , session.getQName("date") }; String[] varValues = { text , date }; Expression expr = session.compileExpression(xqy); for (int i = 0; varNames != null && i < varNames.length; ++i) { expr.bindVariable(varNames[i], varValues[i], null); } ItemSequence results = expr.evaluate(); XMLSerializer serializer = new XMLSerializer(out, "UTF-8"); serializer.setIndent(2); serializer.setOmitXMLDeclaration(true); out.println("<?xml version='1.0' encoding='UTF-8'?>"); out.println("<results>"); while (results.moveToNextItem()) try { Item result = results.getCurrentItem(); if (!result.isNode()) { out.println( "<error>" + result.getString() + "</er ror>" ); } Node node = result.getNode(); serializer.reset(); String xmlForm = serializer.serializeToString(node); out.println(xmlForm); out.flush(); } catch ( Exception ex ) { out.println("<error>"); if (session != null && manager != null && manager instanceof LibraryManager) try { shutdown( (Library)session, (LibraryManager)manager); } catch (Exception e2) { } ex.printStackTrace(out); out.println("</error>"); out.flush(); return; } out.println("</results>"); out.flush(); if (session != null && manager != null && manager instanceof LibraryManager) { shutdown( (Library)session, (LibraryManager)manager); }

} catch (Exception ex) { out.println("<results>\n <error>"); if ( ex instanceof com.qizx.api.CompilationException) { for ( com.qizx.api.Message m : ((com.qizx.api.CompilationException)ex).getMessages () ) { out.println(m.getLineNumber() + ":" + m.getColumnNumber() + " -> " + m.getText()); out.println(); out.flush(); } } if (session != null && manager != null && manager instanceof LibraryManager) try { shutdown( (Library)session, (LibraryManager)manager); } catch (Exception e2) { } ex.printStackTrace(out); out.println("</error>\n </results>"); out.flush(); } } private static void shutdown(Library lib, LibraryM anager libManager) throws QizxException { lib.close(); libManager.closeAllLibraries(10000); } class MyModuleResolver extends DefaultModuleRes olver { public MyModuleResolver ( ) throws MalformedURLException { super ( new URL( "http://aphoteg.googlecode.com/hg/src/xquery/" ) ); }; public URL[] resolve (String moduleNamespaceURI, String[] locationHints) throws MalformedURLException { if (moduleNamespaceURI.equals("http://www.flickr.com/s ervices/api/")) return new URL[]{ new URL("http://aphoteg.googlecode.com/hg/src/xquery/fl ickr.xqy") }; else if (moduleNamespaceURI.equals("http://developer.yahoo. com/geo/")) return new URL[]{ new URL("http://aphoteg.googlecode.com/hg/src/xquery/ge oplanet.xqy") }; else if (moduleNamespaceURI.equals("http://web.tagus.ist.ut l.pt/~rui.candeias/")) return new URL[]{ new URL("http://aphoteg.googlecode.com/hg/src/xquery/ph otoAssociation.xqy") }; else return super.resolve(moduleNamespaceURI,locationHints); } } }