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 depersistencia 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.
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 de 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 archivo con setLastModified(). Por ejemplo, se podría hacer: new File("prueba.txt").setLastModified(new Date().getTime()); 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 list() 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 sistema 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 getAbsolutePath(); String getParent(); boolean renameTo( File nuevoNombre )
Comprobaciones: boolean exists(); boolean canWrite(); boolean canRead(); boolean isFile(); boolean isDirectory(); boolean 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.getName() ); System.out.println( "Camino: "+f.getPath() ); if( f.exists() )
{
System.out.print( "Fichero existente " ); 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 longitud del fichero son "+ f.length()+" bytes" );
} else
System.out.println( "El fichero no existe." ); }
} else
System.out.println( "Debe indicar un fichero." ); }
}
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 Filtrar(".txt")); // Comprobamos el número de archivos en el listado
int numarchivos = listadeArchivos.length ; // 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 consola.
else {
for(int conta = 0; conta < listadeArchivos.length; conta++)
System.out.println(listadeArchivos[conta]); }
}
catch (Exception ex) {
System.out.println("Error al buscar en la ruta indicada"); }
} }
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 coinciden con la expresión // regular dada oldSep por la cadena File.separator
return ruta.replaceAll(separador, File.separator); }catch(Exception e){
// Por si ocurre una java.util.regex.PatternSyntaxException
return ruta.replaceAll(separador + separador, File.separator); }
}
2.4.- Creación y eliminación de ficheros y directorios.
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ísicamente
if (fichero.createNewFile())
System.out.println("El fichero se ha creado correctamente"); 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: " + directorio + " creado");
// Crear varios directorios
exito = (new File(varios)).mkdirs(); if (exito)
System.out.println("Directorios: " + varios + " 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.
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.event.ActionEvent evt) {
// TODO add your handling code here:
File archivo = null; FileReader fr = null; BufferedReader br = null; try {
// Apertura del fichero y creacion de BufferedReader 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 como 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.getText()); // 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 DataOutputStream
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()); }
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ámetro
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 de 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( miFileStream );
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( miFileStream );
// 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 BufferReader.
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 BufferedReader(fichero);
// Escribimos el texto usando el metodo readLine()
while ((texto = buffer.readLine()) != null) { 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 secuencial, 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;
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("encontrado"); seguir = false ;
jLabel2.setText("¡¡Encontrado registro!!"); }
// Leer los otros campos
apellidos = archivo.readUTF(); edad=archivo.readInt(); } // Cerrar fichero archivo.close(); }
catch(FileNotFoundException fnfe) { /* Archivo no encontrado */ }
catch (IOException ioe) { /* Error al escribir */ } }
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 IOException { RandomAccessFile miRAFile;
String s = "Cadena a escribir2\n"; // Abrimos el fichero de acceso aleatorio
miRAFile = new RandomAccessFile( "C:\\aleatorio.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ácticos (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 contenido. 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.
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.newInstance("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.unmarshal(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()); }
} }
6.- Librerías para conversión de documentos XML a otros 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 Library…..
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.
Los pasos a seguir para trabajar con JasperReport serían:
Paso 1: Diseñar la plantilla del informe: un fichero .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//DTD Report Design//EN" "http://jasperreports.sourceforge.net/dtds/jasperreport.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 informe.
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.
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.
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.getConnection ("jdbc:odbc:miBD", "miLogin", "miPassword" );
// Ejecuta la consulta
Statement stmt = (Statement) con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT nombre, 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 SQLException3.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 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 acceder a la base de datos: Class.forName("jdbc:odbc:admdb");
realizar la conexión a la base de datos para comenzar a trabajar con ella.
Connection con = DriverManager.getConnection("jdbc:odbc:admdb");
Las consultas que se realizan a una base de datos se realizan utilizando objetos de las clases Statement y PreparedStatement. Estos objetos se crean a partir de una conexión.
Statement stmt = con.createStatement();
La clase Statement contiene los métodos executeQuery y executeUpdate para realizar consultas y actualizaciones. Así por ejemplo, para obtener los nombres de los medicamentos que tenemos en la tabla medicamentos, de la base de datos farmacia.mdb que creamos anteriormente, tendríamos que emplear la sentencia:
ResultSet rs = stmt.executeQuery("SELECT nombre from medicamentos");
El método executeQuery devuelve un objeto ResultSet para poder recorrer el resultado de la consulta utilizando 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 {
// Cargar el driver de Microsoft Access
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
// Cadena de conexión para conectar con Access
String connectionUrl = "jdbc:odbc:admdb" ;
// Obtener la conexión
Connection con = DriverManager.getConnection(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("SELECT * from medicamentos");
// Mientras queden datos
while (rs.next()) {
// Imprimir en la consola
String codigo = rs.getString("codigo"); String nombre = rs.getString("nombre"); String precio = rs.getString("precio"); String pvp = rs.getString("pvp");
String unidades = rs.getString("unidades");
System.out.println(codigo + "---" + nombre + "--" +precio+ "--" +pvp+ "--" +unidades); }
} catch (SQLException e) {
System.out.println("SQL Exception: "+ e.toString()); } catch (ClassNotFoundException cE) {
System.out.println("Excepción: "+ cE.toString()); } }}
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("SELECT * 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("SELECT * 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 base 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 datos. Sería tan fácil como lo que ves en el siguiente enlace: DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `insertaCliente`(IN Cod_Cliente INTEGER, IN Nombre TinyText,IN Telefono TinyText )
BEGIN
INSERT INTO clientes
VALUES (Cod_Cliente, Nombre, Telefono); END
-Crear la clase Java para desde aquí, llamar al procedimiento 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://localhost/test?" + "user=root&password=admin";
// Obtener la conexión
Connection con = DriverManager.getConnection(connectionUrl);
// El procedimiento almacenado tendrá tres parámetros
CallableStatement prcProcedimientoAlmacenado = con.prepareCall("{ call insertaCliente(?, ?,?) }"); // cargar parametros en el procedimiento almacenado
prcProcedimientoAlmacenado.setInt("Cod_Cliente", 765);
prcProcedimientoAlmacenado.setString("Nombre", "Antonio Pérez") ; prcProcedimientoAlmacenado.setString("Telefono", "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.toString()); } }}
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=”12345678-L”; update cuenta set saldo=saldo - 250 where dni=”89009999-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
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 lapersistencia.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 herramientas 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.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 propiedades.
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.
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 persistentes.
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 PERSONAS").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").addEntity(Persona.class);
sess.createSQLQuery("SELECT ID,NOMBRE,EDAD FROM PERSONAS").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.• 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 alumno.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.nota), 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).
public static String CONSULTA_BASADA_EN_NOMBRE="from Actor a where a.firstName like '"; public static String CONSULTA_BASADA_EN_APELLIDO="from Actor a where a.lastName like '";
private void ejecutaConsultaBasadaEnNombre(){
ejecutaHQLConsulta(CONSULTA_BASADA_EN_NOMBRE+txtNombre.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(); } }
private void btConsultaActionPerformed(java.awt.event.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(tableData, 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.