sábado, 1 de junio de 2019

Capítulo 11 - Modelo de aplicación Web

1. JSP y Servlets

1.1 JSPs

La tecnología JavaServer Pages (JSP) pertenece a la plataforma Java Enterprise Edition (Java EE). Esta tecnología permite la generación dinámica de contenidos web, como HTML, DHTML, XHTML y XML.

Un JSP es un documento de tipo texto que describe la manera de procesar una solicitud para crear una respuesta utilizando la plataforma Java, siendo el resultado final de la ejecución de la JSP, un documento con código HTML.

En una JSP se escribe el código HTML combinado con código Java, pero el código Java, normalmente, va encerrado entre "<%" y "%>". 

Los JSP tendrán extensión .jsp, y serán ubicados dentro de un proyecto web. A pesar de parecerse a documentos HTML, detrás del escenario, una JSP se convierte en un programa compilado donde el HTML estático;simplemente se imprime en el stream de salida estándar asociado. Esto normalmente solo se hace la primera vez que se solicita la página y los desarrolladores pueden solicitar la página ellos mismos cuando la instalan, si quieren estar seguros de que el primer usuario real no tenga un retardo momentáneo cuando la página JSP sea traducida y compilada.

La siguiente figura es una página JSP en el servidor web y cuando se ejecute, se generará un documento con contenido HTML que será recibido por el cliente:




La siguiente figura muestra el documento HTML, recibido por el cliente al ejecutarse la JSP:


El cliente verá la ejecución del HTML en su PC de la siguiente forma:


Realmente, el cliente siempre recibirá un código HTML, por eso puede, inclusive, acceder desde un teléfono celular si este le permite navegar por Internet.
En un patrón de desarrollo Model - View – Controller las JSPs estarán en la View y los servlets en el lado de Controller.

Ejemplo:
Crear JSP en NetBeans.
Clic derecho al nombre del proyecto New-JSP.



1.2 Servlet

Un Servlet es un documento que siempre se ejecuta por el lado del servidor y permite atender las peticiones de los clientes. Es el intermediario entre la vista y las operaciones que el cliente solicita se realicen con la base de datos. En los servlets se pueden apreciar claramente los 3 sucesos siguientes:


  • Inicializar un Servlet: Cuando un servidor web carga un Servlet, se ejecuta el método init del Servlet. La inicialización se completa antes de manejar peticiones de clientes y antes de que el Servlet sea destruido. Aunque muchos Servlets se ejecutan en servidores multi-thread, no tienen problemas de concurrencia durante su inicialización. El servidor llama solo una vez al método init, cuando carga el Servlet y no lo llamará de nuevo, a menos que se vuelva a recargar el Servlet. El servidor no puede recargar un Servlet sin primero haber destruido el Servlet llamando al método destroy. 
  • Interactuar con clientes: Después de la inicialización, el Servlet puede manejar peticiones de clientes. Estas respuestas son manejadas por la misma instancia del Servlet, por lo que hay que tener cuidado con acceso a variables compartidas por posibles problemas de sincronización entre requerimientos concurrentes.

1.3 Destruir un Servlet

Los servlets se ejecutan hasta que el servidor los destruye, por cierre del servidor o bien a petición del administrador del sistema. Cuando un servidor destruye un Servlet, ejecuta el método destroy del propio Servlet. Este método se ejecuta una sola vez y puede ser llamado cuando aún queden respuestas en proceso, por lo que hay que tener la atención de esperarlas. El servidor no ejecutará de nuevo el Servlet, hasta haberlo cargado e inicializado de nuevo.

Con los servlets se involucran muchas clases, a continuación, se señalan las clases más importantes de cada paquete. Para entender mejor la relación entre las clases notar que en las líneas punteadas en rojo se encuentran las herencias directas de clases o interfaces, y en negro, se encuentran las relaciones de uso entre clases.



Cuando un Servlet acepta una llamada de un cliente, recibe los dos objetos siguientes:

  • Un HttpServletRequest que encapsula la comunicación desde el cliente al servidor.
  • Un HttpServletResponse que encapsula la comunicación de vuelta desde el Servlet hacia el cliente.

HttpServletRequest y HttpServletResponse son interfaces definidos en el paquete javax.servlet.http.

HttpServletRequest
Permite al Servlet acceder a la información como los nombres de los parámetros pasados por el cliente, el protocolo (esquema) que está siendo utilizado por el cliente, el nombre del host remoto que ha realizado la petición y la del server que la ha recibido.

HttpServletResponse
Proporciona al Servlet métodos para responder al cliente. Ejemplo Request,dopost,doget.



1.4 HttpSession

Una sesión se inicia cuando un cliente ingresa con su navegador al Site y termina cuando se cierra el navegador o se dirige a otro Site. Para esta sesión, existe un objeto Session el cual, permite manejar recursos de memoria, a fin de guardar y recuperar datos en él, además de algunos métodos que facilitan las operaciones.

Usar sesiones en servlets es bastante sencillo: envolver la búsqueda del objeto sesión asociado con la petición actual, crear un nuevo objeto sesión cuando sea necesario, buscar la información asociada con una sesión, almacenar la información de una sesión, y descartar las sesiones completas o abandonadas.

Buscar el objeto HttpSession asociado con la petición actual.

Esto se hace llamando al método getSession de HttpServletRequest. Si devuelve null, se podrá crear una nueva sesión, pero es tan comúnmente usado que hay una opción que crea automáticamente una nueva sesión si no existe una ya. Solo se pasa true a getSession. Así, el primer paso, se parecerá a esta línea:

HttpSession session = request.getSession(true);

Buscar la información asociada con un Sesión

Los objetos HttpSession viven en el servidor, están asociados automáticamente con la petición de los clientes y cada cliente tiene su respectivo objeto Session. Estos objetos sesión tienen una estructura de datos interna, que permite almacenar un número de claves y valores asociados. Por ejemplo: asumiendo que CarritoVentas es alguna clase definida y que almacena información de ítem para su venta.

HttpSession session = request.getSession(true);
CarritoVentas item = (CarritoVentas) session.getAttribute("item");
if (item != null) {
calculosVenta(item);
}

Ejemplo Session


2. Modelo – Vista - Controlador (MVC)

Este patrón fue descrito por primera vez por Trygve Reenskaug en 1979, y la implementación original fue realizada en Smalltalk en los laboratorios Xerox. MVC se basa en la separación de la aplicación en tres capas principales: modelo, vista y controlador.

Se usa (él o alguna de sus variantes) en la gran mayoría de las interfaces de usuario.

Modelo

  • Es la representación específica del dominio de la información sobre la cual funciona la aplicación.
  • El modelo es otra forma de llamar a la capa de dominio.
  • La lógica de dominio añade significado a los datos; por ejemplo, calculando si hoy es el cumpleaños del usuario o los totales, impuestos o portes en un carrito de la compra.

Vista: Se presenta el modelo en un formato adecuado para interactuar, usualmente un elemento de interfaz de usuario.
Controlador: Este responde a eventos, usualmente acciones del usuario e invoca cambios en el modelo y probablemente en la vista.


Muchas aplicaciones utilizan un mecanismo de almacenamiento persistente (como puede ser una base de datos) para almacenar los datos. MVC no menciona específicamente esta capa de acceso a datos porque supone que está encapsulada por el modelo.

El objetivo primordial del MVC es la reutilización del código ya implementado. Esta tarea se facilita mucho si a la hora de programar tenemos la precaución de separar el código en varias partes que sean susceptibles de ser reutilizadas sin modificaciones.

Ejemplos

  • Los datos de una hoja de cálculo pueden mostrarse en formato tabular, con un gráfico de barras, con uno de sectores.
  • Los datos son el modelo.
  • Si cambia el modelo, las vistas deberían actualizarse en consonancia.
  • El usuario manipula el modelo a través de las vistas (en realidad, a través de los controladores).

MVC es utilizado con mayor frecuencia en las aplicaciones web, donde la Vista es la página HTML, y el Controlador es el código que reúne la data dinámica y genera el contenido de la página.
El Modelo es representado por el contenido actual, que usualmente se encuentra almacenado en una base de datos o en archivos XML.

Fortalezas del MVC

  • Se presenta la misma información de distintas formas.
  • Las vistas y comportamiento de una aplicación deben reflejar las manipulaciones de los datos de forma inmediata.
  • Debería ser fácil cambiar la interfaz de usuario (incluso en tiempo de ejecución).
  • Permitir diferentes estándares de interfaz de usuario o portarla a otros entornos no debería afectar al código de la aplicación.

3. Objetos de transferencia de datos

En el patrón de arquitectura Model View Controller (MVC), se necesita pasar datos encapsulados entre las diversas capas de programación, para ello se podrá utilizar lo siguiente:

  • Data Transfer Object (DTO)
  • Object Domain (OD)
  • View Object (VO)


3. 1 Data Transfer Object (DTO)

Los objetos de transferencia de datos pueden representar a una tabla de base de datos, vinculando sus atributos con las columnas de la tabla, considerando que los tipos de datos de la tabla coinciden con los tipos de datos de los atributos de la clase DTO. Además, en su desarrollo no presentan ningún comportamiento o procesamiento de datos, simplemente se encuentran los métodos getter y setter.



3.2 Object Domain (OD)

Los objetos de dominio (OD) son similares a los objetos de transferencia de datos (DTO), pero se diferencian en que pueden tener métodos adicionales que desarrollan comportamiento o procesamiento de datos, mientras que los DTO, solo tienen getter y setter.

3.3 Value Object (VO)

Los objetos de valor (VO) al igual que los DTO y OD, transportan datos encapsulados entre las diferentes capas de programación en una arquitectura de desarrollo MVC; pero a diferencia de los DTO y OD, los objetos de valor usan datos que son constantes o fijos. Por ejemplo, el siguiente objeto de valor DiaSemana, podría guardar estos valores fijos: día: domingo, lunes, martes, miércoles, jueves, viernes y sábado id: 0, 1, 2, 3, 4, 5, 6






























sábado, 25 de mayo de 2019

Capítulo 10 Java Database Connectivity (JDBC)

1. Creación de base de datos, tablas y relaciones

Un buen diseño de base de datos permite mejorar el rendimiento de un sistema, por lo tanto, es fundamental darse el tiempo para diseñar la estructura que debe tener la base de datos.

Un análisis del funcionamiento permitirá controlar y corregir los pequeños problemas que puedan surgir en el sistema. Para realizar el análisis, se puede recurrir a diferentes herramientas externas que darán una visión detallada de cuáles son los patrones de funcionamiento de base de datos y centrarse en aquellos apartados más conflictivos.

Gran parte de las cualidades esperadas en una buena base de datos se basan en su diseño inicial, que constituye los cimientos de cualquier sistema de información actual. El diseño de base de datos debe estar bien definido antes de pasar a otra etapa, dada la dificultad y el coste de realizar cambios en el diseño en etapas posteriores. El diseño de bases de datos se compone de dos fases altamente diferenciadas: diseño lógico y diseño físico.

El diseño lógico consiste en analizar los requerimientos de la empresa y cuáles van a ser los componentes de base de datos, como por ejemplo las tablas y las restricciones. El diseño lógico no tiene en cuenta dónde o cómo van a estar almacenados físicamente los datos.

El uso efectivo de cualquier base de datos se basa en un buen diseño normalizado. Por su parte, la normalización consiste en el proceso de evitar las redundancias de los datos, la actualización, modificación y borrado de datos en múltiples posiciones.

Además, al eliminar la repetición innecesaria de información, se reduce la probabilidad que la base de datos se vuelva inconsistente o contenga errores. Las sentencias básicas para trabajar con base de datos son las siguientes: 

  • SHOW DATABASES;

Muestra las base de datos existentes en MySQL.
  • CREATE DATABASE dbdat;

Crea la base de datos de nombre dbdat.
  • USE dbdat;

Activa la base de datos de nombre dbdat.
  • DROP DATABASE dbdat;

Elimina la base de datos dbdat.

Una vez creada la base de datos, en ella se crearán las tablas que son los repositorios de datos, para lo cual se deberán definir los tipos de datos que se asignarán en estos repositorios. 

Estos tipos de datos pueden ser los siguientes:

  • VARCHAR(número): Tiene un número variable de caracteres, el número que se pone es el número máximo de caracteres que puede tener este número va de 1 hasta 255.
  • CHAR(número) Tiene un número fijo de caracteres va de 1 hasta 255.
  • DATE Tipo fecha (YYYY-MM-DD)-( '1000-01-01' a '9999-12-31').
  • DATETIME Tipo fecha y hora (YYYY-MM-DD HH:MM:SS).
  • INTEGER (INT) Tipo numérico entero (-2147483648 a 2147483647)
  • FLOAT(M, D) Número real de coma flotante M es el número y D los decimales. (-3.402823466E+38 a -1.175494351E-38, 0, y 1.175494351E- 38 a 3.402823466E+38.)
  • DOUBLE(M, D) Número real de doble precisión M es el número y D los decimales. (- 1.7976931348623157E+308 a - 2.2250738585072014E-308, 0, y 2.2250738585072014E-308 a 1.7976931348623157E+308)
  • BLOB Para grandes textos, siendo la longitud máxima de 65535. Con este tipo las búsquedas de texto son sensibles a las mayúsculas.
  • TEXT Para grandes textos, siendo la longitud máxima de 65535. Con este tipo las búsquedas de texto NO son sensibles a las mayúsculas.


1.1 Opciones para las columnas

  • NOT NULL El valor no puede ser nulo en el campo.
  • AUTO_INCREMENT Automáticamente incrementa el número del registro anterior.
  • PRIMARY KEY El PRIMARY KEY es un campo que MySQL usa como índice. Este índice puede hacer cosas como:
    • Hallar rápidamente filas que acierten una cláusula WHERE.
    • Regresar filas de una tabla desde otras tablas cuando se realizan uniones.
    • Esto definitivamente ayudará a agilizar peticiones.
  • KEY Crea una llave secundaria. Se puede encontrar mayor información en la siguiente dirección: http://dev.mysql.com/doc/refman/5.0/es/column-types.html


1.2 Tablas

Una tabla es un conjunto de datos sobre un tema concreto para guardar datos de personas, productos, departamentos, cursos entre otros. Si se usa una tabla independiente para cada tema, se evitará la duplicación de datos y la base de datos resultará más eficiente, al mismo tiempo que se reducirá el riesgo de errores en la entrada de datos. En las tablas, los datos están organizados en filas y columnas.

Las sentencias básicas para trabajar con tablas son las siguientes:
  • SHOW TABLES;

Muestra las tablas de la base de datos activa.
CREATE TABLE personas (idpersona int NOT NULL AUTO_INCREMENT,nombres varchar(40) NOT NULL,apellidos varchar(40) NOT NULL,sexo char(1) NOT NULL, -- Masculino/Femeninoemail varchar(40) NULL,login varchar(10) NOT NULL,password varchar(10) NOT NULL,telefono varchar(20) NULL,nacimiento datetime NULL, -- fecha en que naciódatoAdicional text NULL -- dato adicional de persona);
  • ALTER TABLE personas ADD UNIQUE (login, password);

Altera la tabla personas, para que la unión de login con password no se repita en las siguientes filas.
  • DROP TABLE personas; Elimina la tabla personas.

2. Consultas

El lenguaje estructurado de consultas (SQL) proporciona la sentencia SELECT, la cual permite hacer consultas a una base de datos. La siguiente tabla explica cada una de las partes de la sentencia Select.
  • SELECT

Palabra clave que indica que la sentencia de SQL que se desea ejecutar es de selección.

Ejemplos:
SELECT * FROM tabla – muestra el contenido de toda la tabla
SELECT col1, col2 FROM tabla -- muestra el contenido de columna 1 y 2

  • FROM

Indica la tabla (o tablas) desde la que se desea recuperar los datos. En el caso de que exista más de una tabla se denomina a la consulta "consulta combinada" o "join".
En las consultas combinadas es necesario aplicar una condición de combinación, a través de una cláusula ON. En las consultas combinadas surge el concepto de llave foránea.
Una llave foránea llamada también llave externa es uno o más campos de una tabla que hacen referencia al campo o campos de clave principal de otra tabla, una clave externa indica cómo están relacionadas las tablas.
Los datos en los campos de clave externa y clave principal deben coincidir, aunque los nombres de los campos no sean los mismos. Ejemplos:

SELECT * FROM tabla – muestra el contenido de tabla.

– Muestra el contenido de tabla1 y tabla2.

Si queremos mostrar el contenido de dos tablas.

Siendo estas:

tabla1
campo 1: idtabla1
campo 2:colx
tabla 2
campo 1: idtabla2
campo 2: idtabla1 ( llave foránea)
campo2 coly
SELECT tabla1.colx, tabla2.coly FROM tabla1
INNER JOIN tabla2
ON tabla1.idtabla1= tabla2.idtabla2 (unión realizada por la llave foránea)

  • WHERE

Especifica una condición que debe cumplirse para que los datos sean
devueltos por la consulta. Además, admiten los operadores lógicos
AND y OR.
Ejemplos:
– muestra fila de id=5
SELECT * FROM tabla WHERE id=5
– muestra filas cuyas edades estén entre [18, 65]
SELECT * FROM tabla WHERE edad>17 AND edad<66

  • GROUP BY

Especifica la agrupación que se da a los datos. Se usa siempre en combinación con funciones agregadas.
Ejemplo:
Tabla alumnos
campo1: idalumno
campo 2: nombre
campo 3: apellido
Tabla notas
campo 1: idnota
campo 2: idalumno
campo 3: tipo nota
– muestra lista de alumnos y promedios
SELECT alumnos.alumno, AVG(notas.nota)
FROM alumnos
INNER JOIN notas
ON alumnos.idalumno= notas.idnota
GROUP BY alumnos
  • HAVING

Especifica una condición que debe cumplirse para que los datos sean devueltos por la consulta. Su funcionamiento es similar al de WHERE, pero aplicado al conjunto de resultados devueltos por la consulta. Debe aplicarse siempre junto a GROUP BY y la condición debe estar referida a los campos contenidos en ella.

Ejemplo:
– Muestra lista de alumnos y promedios mayor o igual a 11.
SELECT alumnos.alumno, AVG(notas.nota)
FROM alumnos
INNER JOIN notas
ON alumnos.id= notas.id
GROUP BY alumnos
HAVING AVG(notas.nota)>=11

  • ORDER BY

Presenta el resultado ordenado por las columnas indicadas. El orden puede expresarse con ASC (orden ascendente) y DESC (orden descendente). El valor predeterminado es ASC.
Ejemplo:
– Lista de alumnos y promedios mayor o igual a 11 en orden de
mérito. 
SELECT alumnos.alumno, AVG(notas.nota)
FROM alumnos
INNER JOIN notas
ON alumnos.id= notas.id
GROUP BY alumnos
HAVING AVG(notas.nota)>=11
ORDER BY 2 DESC

3. Inserción, eliminación y actualización

INSERT

La sentencia INSERT permite insertar valores dentro de una tabla y tiene dos formas importantes a considerar.
Ejemplo:
Insertamos datos en la tabla Alumnos.
Tabla Alumnos
campo 1: nombre
campo 2: dirección
campo 3: teléfono
campo 4: nacimiento
campo 5 : categoría (acepta null)
campo 6: estado ( default A)

Insert Alumnos(Nombre, Direccion, Telefono, Nacimiento) values ('Juan Perez','Av. Arequipa 3025', GetDate())

Si no se especifican columnas es porque se van ingresar datos a todas las
columnas, en correspondencia a la estructura de la tabla.

Insert Alumnos values ('Juan Perez','Av. Arequipa 3025', GetDate(), 'B', 'Activo')

También, es posible insertar datos desde otra tabla, en caso la otra tabla cumpla con la estructura de la tabla destino.

Insert into Alumnos 
select * from Alumnos2;


UPDATE

Es posible modificar el contenido de las columnas de una tabla con la sentencia UPDATE.
Ejemplo:

update nombretable set campo1="dato1" where condicion;

Nota:
Si no se colocara la condición (Where ...), entonces todas las filas serían cambiadas a este teléfono.

También, es posible actualizar una tabla desde otras tablas. 
Por ejemplo:

update nombretable set campo1= (select campo from tabla2 where id=1) where condicion;


DELETE

Con esta sentencia se puede eliminar una o más filas de una tabla.

delete tabla where condicion;

Ejemplo:

delete alumnos where telefono is null;

También se puede borrar todo:

delete alumnos

También, es posible retirar filas de una tabla, según características de otra
tabla

delete nombretable where  campo1 in (select campo from tabla2 where id=1) ;

4. Conectividad con Java

La conectividad de la base de datos de Java (JDBC, Java Database Connectivity) es un marco de programación para los desarrolladores de Java que escriben los programas que tienen acceso a la información guardada en bases de datos.

JDBC, se utiliza comúnmente para conectar un programa del usuario con una base de datos por “detrás de la escena”, sin importar qué software de administración o manejo de base de datos se utilice para controlarlo. En Java existen 4 formas de usar JDBC para conexión con base de datos:

4.1 El puente JDBC-ODBC

Se usará, inicialmente, para facilitar la comunicación con el gestor SQL Server. Para esto se necesita del ODBC (Open Database Connectivity) de Microsoft, a través del cual se creará un DSN (Data Source Name) que permitirá crear una cadena de conexión de información sobre la base de datos.

4.2 Driver de Java parcialmente nativo

Esta forma está integrada por controladores que se comunican con el servidor de base de datos en el protocolo nativo del servidor. Por ejemplo, para el gestor DB2 se necesitaría un driver nativo de DB2 de IBM. Para Informix, se requerirá de un driver nativo de Informix de Unix.

4.3 Driver JDBC-Net de Java puro

En esta forma los drivers están hechos en Java puro; sin embargo, utilizan protocolos estándares, como por ejemplo HTTP con servidor de base de datos. El servidor traduce el protocolo de red. Para el caso de Windows, puede usar ODBC.

4.4 Driver de protocolo de Java puro

En esta última forma, conformada por drivers de java puro, la comunicación es a través de un protocolo específico para la marca de base de datos que se usa. Para mostrar un ejemplo del driver de protocolo de Java puro, se debe crear un proyecto en NetBeans y agregar al nodo Libraries, el driver de MySQL.



Luego, cree una clase Java con el siguiente contenido:



Si se tiene esta salida, entonces ya se tendrá conexión a la base de datos test de MySQL.


5. El paquete java.sql

Es una implementación de la API JDBC (usada para acceder a bases de datos SQL) donde se agrupa una serie de objetos como: Connection, Statement, PreparedStatement, ResulSet, ResultSetMetaData y otros.

5.1 Connection

Permite la conexión a la base de datos. Origina un canal entre una aplicación y la base de datos. Será siempre imprescindible en una aplicación que quiere acceder a una base de datos.}

Ejemplo de su uso:
Connection cn=DriverManager.getConnection("jdbc:mysql://localhost:3306/appweb", "root", "java");

5.2 Statement

Este objeto permitirá ejecutar una sentencia SQL para una base de datos, por ejemplo: select, insert, update y delete.

Ejemplo de su uso:
Sring sql = “SELECT * FROM provincias“;
Connection cn = db.getConnection();
Statement st = cn. createStatement();
ResultSet rs = st. executeQuery(sql);

5.3 PreparedStatement

Este objeto permitirá ejecutar una sentencia SQL para una base de datos usando parámetros de envío; por ejemplo: select, insert, update y delete.

Ejemplo de su uso:
Sring sql = “SELECT * FROM provincias LIMIT ?, ?“;
Connection cn = db.getConnection();
PreparedStatement ps = cn.prepareStatement(sql);
ps.setLong(1, 50);
ps.setLong(2, 50);
ResultSet rs = ps.executeQuery();

5.4 ResulSet

Si el objeto Statement ejecuta una sentencia Select del SQL, entonces, este devuelve un conjunto de resultados. Este conjunto de resultados es asignado y manipulado por un objeto ResulSet.

Ejemplo de su uso:
Sring sql = “SELECT * FROM provincias“;
Connection cn = db.getConnection();
Statement st = cn. createStatement();
ResultSet rs = st. executeQuery(sql);

Tabla de métodos Clase Connection-Resumen de método:
  • void: clearWarnings() Limpia todos los warnings reportados por el objeto Connection.
  • void close() Libera la base de datos de este objeto Connection y de los recursos consumidos por JDBC.
  • void commit() Ejecuta todos los cambios hechos desde el previo commit/rollback y libera a la base de datos de actuales bloqueos por este objeto Connection.
  • Statement createStatement() Crea un objeto Statement para enviar una sentencia SQL a la base de datos. 
  • Statement createStatement(int resultSetType, int resultSetConcurrency) Crea un objeto Statement, el cual generará objetos ResulSet con un tipo y concurrencia dada.
  • String getCatalog() Retorna el nombre del actual catálogo o base de datos.

Clase Connection-Resumen de métodos
  • DatabaseMetaData getMetaData() Recupera un objeto DatabaseMetaData, el cual contiene metadata de la base de datos con la que este objeto Connection ha establecido la conexión.
  • SQLWarning getWarnings() Recupera el primer warning reportado por la ejecución de este objeto Connection. 
  • boolean isClosed() Retorna true si el objeto Connection fue cerrado.
  • boolean isReadOnly() Retorna true si el objeto Connection es de modo read-only.
  • void rollback() Deja sin efecto todos los cambios hechos en la actual transacción y libera bloqueos en la base de datos retenidos por este objeto Connection.
  • void setCatalog(String catalog) Establece el nombre del catálogo o de la base de datos con la cual trabajará este objeto Connection. 
  • void setReadOnly(boolean readOnly) Pone esta conexión en modo read-only habilitando al driver para optimizaciones sobre la base de datos.

Clase Statement - Resumen de métodos

  • void addBatch(String sql) Adiciona un comando SQL dado a la actual lista de  comandos de este objeto Statement.
  • void cancel() Cancela este objeto Statement, si tanto el DBMS y el driver soportan abortar una sentencia SQL.
  • void clearBatch() Limpia el objeto Statement de la actual lista de comandos SQL.
  • void clearWarnings() Limpia todos los warnings reportados por este objeto Statement.
  • void close() Libera la base de datos de este objeto Statement y de los recursos consumidos por JDBC.
  • boolean execute(String sql) Ejecuta una sentencia SQL y retorna true, si retorna resultados.
  • int[] executeBatch() Ejecuta un lote de comandos SQL sobre una base de datos. Si la ejecución fue satisfactoria, retorna un arreglo de actualizaciones incluidas.
  • ResultSet executeQuery(String sql) Ejecuta una sentencia SELECT del SQL y retorna un único ResulSet.
  • int executeUpdate(String sql) Ejecuta una sentencia SQL como un INSERT, UPDATE o DELETE; o una sentencia SQL que no retorne nada como una sentencia DDL del SQL.
  • int getQueryTimeout() Recupera el número de segundos que el driver esperará para que un objeto Statement se ejecute.
  • ResultSet getResultSet() Recupera el actual resultado de un objeto ResultSet.
  • SQLWarning getWarnings() Recupera la primera warning reportada por la llamada a este objeto Statement.
  • void setCursorName(String name) Establece un nombre de cursor SQL, el cual puede ser usado por posteriores métodos del objeto Statement.
  • void setQueryTimeout(int seconds) Establece el número de segundos que el driver esperará para que un objeto Statement se ejecute.
Clase ResultSet - Resumen de métodos

  • boolean absolute(int row) Mueve el cursor al número de fila determinada en este objeto 
  • ResultSet. void cancelRowUpdates() Cancela los cambios realizados en la fila actual de este objeto ResultSet.
  • void close() Libera base de datos este objeto ResultSet JDBC y recursos inmediatamente en lugar de esperar a que esto suceda cuando se cierra automáticamente.
  • void deleteRow() Elimina la fila actual de este objeto ResultSet y de la base de datos subyacente.
  • int findColumn(String columnName) Asigna el nombre de la columna de resultados a su índice de la columna de resultados.
  • boolean first() Mueve el cursor a la primera fila de este objeto ResultSet.
  • boolean getBoolean(int columnIndex) Recupera el valor de la columna designada en la fila actual de este objeto ResultSet como un booleano en el lenguaje de programación Java.
  • boolean getBoolean(String columnName) Recupera el valor de la columna designada en la fila actual de este objeto ResultSet como un booleano en el lenguaje de programación Java.
  • byte getByte(int columnIndex) Recupera el valor de la columna designada en la fila actual de este objeto ResultSet como un byte en el lenguaje de programación Java.
  • byte getByte(String columnName) Recupera el valor de la columna designada en la fila actual de este objeto ResultSet como un byte en el lenguaje de programación Java.
  • String getCursorName() Recupera el Nombre del cursor SQL utilizada por Objeto ResultSet. 
  • Date getDate(int columnIndex) Recupera el valor de la columna designada en la fila actual de este objeto ResultSet como objeto java.sql.Date en el lenguaje de programación Java.
  • Date getDate(String columnName) Recupera el valor de la columna designada en la fila actual de este objeto ResultSet como objeto java.sql.Date en el lenguaje de programación Java.
  • double getDouble(int columnIndex) Recupera el valor de la columna designada en la fila actual de este objeto ResultSet como un doble en el lenguaje de programación Java.

6. Metadatos

En ocasiones, se necesitan hacer aplicaciones en Java que trabajen contra una base de datos desconocida, de la que no se sabe qué tablas tiene, ni cuáles son sus columnas en cada tabla. Es, por ejemplo, el caso de aplicaciones que permiten visualizar el contenido (o incluso modificar el mismo) de una base de datos cualquiera, de una forma genérica. En este tipo de aplicaciones, a veces también se deja que el usuario escriba sus propias sentencias SELECT que el programa luego ejecuta y debe mostrar los resultados.

Las clases DataBaseMetaData y ResultSetMetaData permiten, respectivamente, analizar la estructura de una base de datos (qué tablas tiene, qué columnas cada tabla, de qué tipos, etc.) o de un ResultSet de una consulta, para averiguar cuántas columnas tiene dicho ResulSet, de qué columnas de base de datos proceden, de qué tipo son, etc.

ResultSetMetaData
Un objeto de esta clase tiene información meta sobre el conjunto de resultados, como por ejemplo: cuántas columnas tiene la consulta, los nombres de las columnas, los tipos de datos que guarda cada columna, cuántas filas, etc. Ejemplo de su uso:

Sring sql = “SELECT * FROM provincias“;
Connection cn = db.getConnection();
Statement st = cn. createStatement();
ResultSet rs = st. executeQuery(sql);
ResultSetMetaData rsmd = rs.getMetaData();
int ctascols = rsmd.getColumnCount();

7. Retorno y vista de consulta en el browser

Para mostrar una consulta en el browser, se debe considerar lo siguiente:

  • Los datos se muestran en una tabla HTML, por lo tanto, deben tener los estilos de tablas para que esta sea cebra, es decir, filas pares de un color y pares de otro color. 
  • El otro aspecto a considerar es la lectura de la base de datos para obtener la información a mostrar al cliente, por lo tanto, se debe considerar lo siguiente: 
  • El driver a usar, según la base de datos. 
  • La conexión de Java con el respectivo driver para acceder a la base de datos.
  • Tener una instrucción Statement para ejecutar una sentencia SQL. 
  • Tener una instrucción ResultSet para recoger la información leída de la base de datos. 
  • Tener una instrucción ResultSetMetaData para que informe de la metadata recogida por la consulta.
  • Finalmente, un objeto List que encapsule la información y se lo envíe al cliente.














sábado, 18 de mayo de 2019

Capítulo 9 Archivos y serialización

1. Conceptos sobre archivos

Los archivos también denominados ficheros son una colección de información (datos relacionados entre sí), localizada o almacenada como una unidad en alguna parte de la computadora. Los archivos son el conjunto organizado de informaciones del mismo tipo, que pueden utilizarse en un mismo tratamiento; como soporte material de estas informaciones.

Los archivos como colección de datos sirven para la entrada y salida a la computadora y son manejados con programas. Los archivos pueden ser contrastados con Arrays y registro; lo que resulta dinámico y, por esto, en un registro se deben especificar los campos, el número de elementos de un arrays (o arreglo), el número de caracteres en una cadena; por esto se denotan como "Estructuras estáticas".

En los archivos no se requiere de un tamaño predeterminado; esto significa que se pueden hacer archivos de datos más grandes o pequeños, según se necesiten. Cada archivo es referenciado por su identificador (su nombre). 

Las principales características de los archivos son:

  • Independencia de las informaciones respecto de los programas.
  • La información almacenada es permanente.
  • Un archivo puede ser accedido por distintos programas en distintos momentos.
  • Gran capacidad de almacenamiento.


Hay dos aspectos importantes en que difieren las organizaciones de archivos: la secuenciación de registros y el conjunto de operaciones para manipularlos. 

Por ello tenemos como modos básicos de acceso a:

  • Los archivos secuenciales o de texto
  • Los archivos de acceso aleatorio o binarios

2. Archivos secuenciales


La forma más sencilla de almacenar un conjunto de registros en un archivo es mediante la organización secuencial. En este tipo de archivos, los registros son escritos consecutivamente cuando el archivo es creado, por lo tanto, deben ser accesados de ese modo cuando se consultan. Es la forma básica de organizar un conjunto de registros, que forman un archivo, utilizando una organización
secuencial.

En un archivo organizado secuencialmente, lo registros quedan grabados consecutivamente cuando el archivo se utiliza como entrada. En la mayoría de los casos, los registros de un archivo secuencial quedan ordenados de acuerdo con el valor de algún campo de cada registro. Semejante archivo se dice que es un archivo ordenado; el campo, o los campos, cuyo valor se utiliza para determinar el ordenamiento es conocido como la llave del ordenamiento. Un archivo puede ordenarse ascendente o descendentemente con base en su llave de ordenamiento.

La característica más importante de esta técnica de organización de archivos es que solo permite el acceso secuencial, es decir, para acceder al registro k, se deben recorrer los k-1 registros anteriores.
Esta característica impide que el archivo secuencial se use en procesos en línea debido a que no se pueden consultar rápidamente los registros; sin embargo, muestra buen rendimiento en procesos en lote donde se aprovecha la capacidad para acceder al siguiente registro rápidamente. Otra ventaja de
esta organización de archivos radica en que la dirección del registro está implícita en el sistema; es decir, no se pierden registros por la desaparición de direcciones.

Estructura de la organización secuencial
Archivo secuencial es la forma más simple de almacenar y recuperar registros de un archivo. En un archivo secuencial, se almacenan los registros uno tras otro. El primer registro almacenado se coloca al principio del archivo. El segundo se almacena inmediatamente después (no existen posiciones sin
uso), el tercero después del segundo, etc. Este orden nunca cambia en la organización secuencial.
Una característica de los archivos secuenciales es que todos los registros se almacenan por posición: de primer registro, segundo registro etc. 

Ventajas y desventajas
Ventajas:
Los archivos secuenciales proveen la mejor utilización de espacio y son rápidos cuando los registros son accedidos secuencialmente. Los archivos con poca volatilidad, gran actividad y tamaño variable son altamente susceptibles de ser organizados secuencialmente. La ventaja más importante de la técnica de organización secuencial de archivos es la capacidad de acceso al "siguiente" registro rápidamente, mientras que el patrón de acceso a un archivo secuencial se conforme al ordenamiento de registros en el archivo, los tiempos de acceso serán muy buenos.

Sin embargo, si el patrón de acceso al programa no se conforma al patrón de ordenamiento de los registros, entonces la eficiencia del programa puede ser terrible. Otra ventaja de los archivos de organización secuencial es que son muy sencillos de usar y aplicar.

Desventajas:
El acceso a un registro es pobre, la localización de un determinado registro no se puede hacer individualmente no de manera rápida, y el acceso aleatorio es impráctico.
Además, en los archivos secuenciales la dirección de registro está implícita y están vulnerables a fallas del sistema.

Escritura de los archivos secuenciales
En estos archivos, la información solo puede leerse y escribirse empezando desde el principio del archivo.



Los archivos secuenciales tienen algunas características que hay que tener en cuenta:
1. La escritura de nuevos datos siempre se hace al final del archivo.
2. Para leer un dato concreto del archivo hay que avanzar siempre hasta donde se encuentre dicho dato. Si el dato requerido se encuentra antes del dato en que se está posicionado el archivo en un momento dado, será necesario regresar al comienzo del archivo y avanzar hasta el dato necesario.

Almacenamiento de archivos secuenciales
Los archivos secuenciales pueden almacenarse en dispositivos de acceso serial o directo. Con frecuencia los dispositivos de acceso serial son considerablemente menos caros que los dispositivos de acceso directo en un sistema de cómputo, pero de hecho, los dispositivos de almacenamiento de acceso directo en una computadora siempre proporcionan mayor capacidad de almacenamiento y acceso más rápido que los dispositivos de acceso serial. Para leer un archivo secuencial, el sistema siempre comienza al principio del archivo y lee un registro a la vez hasta llegar al registro deseado.

Por ejemplo, si ocurre que el registro particular es el décimo en un archivo, el sistema comienza en el primer registro y lee hacia delante un registro a la vez hasta llegar al décimo.

Actualización en archivos secuenciales
Un archivo maestro representa el punto estático de algún aspecto de alguna organización en un tiempo dado. Los cambios en la organización se reflejan en el archivo maestro, y para llevar a cabo la actualización del archivo maestro se tendrán que realizar los tipos de actualización: 
  • Insertar un nuevo registro
  • Borrar un registro
  • Modificar un registro
Al estar usando un archivo secuencial como archivo maestro, el realizar las operaciones de actualización se llevará con el auxilio de un archivo de transacciones, debido a que se realizará el proceso en lote para que sea más eficiente.

Creación de archivos secuenciales
La creación de un archivo secuencial se realiza agregando registros al final del archivo, no importa el medio de entrada de datos. El archivo secuencial puede ser almacenado en cintas o en discos magnéticos. Un archivo secuencial puede tener registros fijos o variables, la declaración del archivo y la definición del registro dependerá del lenguaje de programación que se vaya a usar.

Clasificación de los archivos secuenciales
Normalmente el uso de los archivos secuenciales se da en procesos en lote, donde se ha hecho notar que son eficientes cuando se llevan a cabo diversas operaciones sobre una gran cantidad de registros o de todo el archivo. Esta eficiencia se logra con una acción: la clasificación, proceso que no es exclusivo de los archivos secuenciales, pero sí necesaria para diversas operaciones.

La clasificación es el proceso de examinar los registros en un archivo y ponerlos en una secuencia ascendente o descendente basada en el valor de uno o más campos del registro. 

Recuperación de archivos secuenciales
Como se mencionó anteriormente la recuperación de información de los archivos se da como consulta (modo interactivo) o como la generación de reporte (modo lote). También se indicó la desventaja de acceder a un solo registro de un archivo secuencial.

Es ineficiente el uso de estos archivos para realizar la consulta de un registro, pero es óptimo su uso para la generación de un reporte de secuencia lógica en que se encuentra el archivo, el reporte podrá ser de varios tipos:
  • Selectivo
  • Resumen y sumarización
  • Clasificado por uno o más campos
Consideraciones de los archivos secuenciales el uso más común para los archivos secuenciales es para el procesamiento de lotes, tales como respaldo de datos, generación de reportes, transmisión física de datos, etc., archivo de nómina. 

Ventajas: los archivos secuenciales proveen la mejor utilización de espacio y son rápidos cuando los registros son accedidos secuencialmente. Los archivos con poca volatilidad, gran actividad y tamaño variables son altamente susceptibles de ser organizados secuencialmente.

Desventajas: el acceso a un registro es pobre, la localización de un determinado registro no se puede hacer individualmente ni rápidamente, el acceso aleatorio es impráctico. El archivo a causa de inserciones y supresiones, tiene que ser reescrito periódicamente. 

Uso de las Clases FileReader y FileWriter
Las clases FileReader y FileWriter permiten leer y escribir, respectivamente, en un fichero.

Lo primero que debemos hacer es importar estas clases y las que controlan las excepciones.

Después debemos crear un objeto de alguna de estas clases. Se pueden construir con un objeto File, FileDescriptor o un String.

Al crear un objeto de estas clases, deben estar dentro de un try-catch, para poder manejar las excepciones.

Cuando creamos un objeto, abrimos un stream entre nuestro programa y el  exterior, cuando debemos de usarlo debemos cerrar el stream con el método close().



Ejemplo de creación de los objetos
FileWriter fw=new FileWriter("D:\\fichero1.txt");
FileReader fr=new FileReader("D:\\fichero1.txt");

Al instanciar las clases escribimos la ruta del fichero. En el caso de FileWriter si la ruta del fichero que no existe este la crea, para FileReader sí debe existir el fichero, ya que si no existe lanzará una excepción. Usamos doble barra (\\) porque es un carácter de escape, para poner \.

Ejemplo:
Escribir y leer texto en un fichero.
Para escribir, usaremos el método write de FileWriter, este método puede usar como parámetro un String con lo que queremos escribir o un número que se corresponderá un carácter de la tabla ASCII.

Para leer, usaremos el método read de FileReader, este método no tiene parámetros pero devuelve un número que si le hacemos un casting a char este será legible por nosotros. Esto lo podemos mostrar por pantalla o incluso pasarlo a otro fichero (crear otro objeto). Cuando se termina el fichero, el método read devuelve -1.

import java.io.*;
//Importamos todas las clases de java.io
public class FicheroTextoApp {
public static void main(String[] args) {
try{
//Abro stream, crea el fichero si no existe
FileWriter fw=new FileWriter("D:\\fichero1.txt");
//Escribimos en el fichero un String y un caracter 97 (a)
fw.write("Esto es una prueb");
fw.write(97);
//Cierro el stream
fw.close();
//Abro el stream, el fichero debe existir
FileReader fr=new FileReader("D:\\fichero1.txt");
//Leemos el fichero y lo mostramos por pantalla
int valor=fr.read();
while(valor!=-1){
System.out.print((char)valor);
valor=fr.read();
}
//Cerramos el stream
fr.close();
}catch(IOException e){
System.out.println("Error E/S: "+e);
}
}
}

La idea es abrir un fichero, escribir o leer y cerrar el fichero. No es necesario que hagamos nada para mover el puntero del fichero, cuando leemos un carácter automáticamente el puntero se mueve (acceso secuencial).

3. Archivos de acceso aleatorio

Permiten situarse en cualquier punto del archivo para actualizar ciertos valores.
La clase Java RandomAccesFile del paquete java.io implementa un archivo de acceso aleatorio, puede ser utilizado tanto para lectura como escritura de bytes.

Todo objeto, instancia de RandomAccesFile , soporta el concepto de puntero que indica la posición actual dentro del archivo. Es un entero que empiza en 0 y mide en bytes.

Los archivos de acceso secuencial son muy útiles, pero a veces son necesarios archivos de acceso aleatorio que permiten acceder a sus contenidos en forma aleatoria o no secuencial.

La clase Java RandomAccessFile del paquete java.io implementa un archivo de acceso aleatorio, puede ser utilizado tanto como para lectura como escritura de bytes.

Todo objeto, instancia de RamdomAccessFile, soporta el concepto de puntero que indica la posición actual dentro del archivo. Es un entero que empieza en 0 y se mide en bytes.

RamdomAccessFile provee comandos para operar sobre el puntero.

Los constructores de la clase son:
RamdomAccessFile(String path, String modo);
RamdomAccessFile(File objetoFile, String modo);

Lanzan una excepción FileNotFoundException.
El argumento modo indica el modo de acceso en el que se abre el fichero.

Los valores permitidos para este parámetro son:
  • "r": Abre el fichero en modo solo lectura. El fichero debe existir.  Una operación de escritura en este fichero lanzará una excepción IOException.
  • "rw": Abre el fichero en modo lectura y escritura. Si el fichero no existe se crea.

Ejemplo: abrir un fichero aleatorio para lectura
Se abre el fichero clientes.dat para lectura usando el primer constructor.
RandomAccessFile fichero = new
RandomAccessFile("/ficheros/clientes.dat", "r");

Ejemplo: abrir un fichero aleatorio para lectura/escritura
Se abre el fichero personas.dat para lectura/escritura usando el segundo
constructor. Si el fichero no existe se crea.
File f = new File ("/ficheros/personas.dat");
RandomAccessFile fichero = new RandomAccessFile(f, "rw");

Acceso a datos en ficheros aleatorios
Para acceder de forma aleatoria a los datos contenidos en el fichero, la clase RandomAccessFile dispone de varios métodos. Entre ellos:

long getFilePointer();
Devuelve la posición actual del puntero del fichero. Indica la posición (en bytes)
donde se va a leer o escribir.

long length();
Devuelve la longitud del fichero en bytes.

void seek(long pos);
Coloca el puntero del fichero en una posición determinada. La posición se da como un desplazamiento en bytes desde el comienzo del fichero. La posición 0 indica el principio del fichero. La posición length() indica el final del fichero.

Además, dispone de métodos de lectura/escritura:

public int read()
Devuelve el byte leído en la posición marcada por el puntero. Devuelve -1 si alcanza el final del fichero. Se debe utilizar este método para leer los caracteres de un fichero de texto.

public final String readLine()
Devuelve la cadena de caracteres que se lee, desde la posición marcada por el puntero, hasta el siguiente salto de línea que se encuentre. 

public xxx readXxx()
Hay un método read para cada tipo de dato básico: readChar, readInt, readDouble, readBoolean, etc.

public void write(int b)
Escribe en el fichero el byte indicado por parámetro. Se debe utilizar este método para escribir caracteres en un fichero de texto.

public final void writeBytes(String s)
Escribe en el fichero la cadena de caracteres indicada por parámetro.

public final void writeXxx(argumento)
También existe un método Write para cada tipo de dato básico:
writeChar, writeInt, writeDouble, writeBoolean, etc.


4. La Interfaz serializable


La serialización es la transformación de un objeto en una secuencia de bytes que pueden ser posteriormente leídos para reconstruir el objeto original.

El objeto serializado puede guardarse en un fichero o puede enviarse por red para reconstruirlo en otro lugar. Puede crearse en un sistema Windows y enviarlo, por ejemplo, a otro sistema que utilice Linux.

Guardar objetos de forma que existan cuando la aplicación haya terminado se conoce como persistencia. Para poder transformar el objeto en una secuencia de bytes, el objeto debe ser serializable.

Un objeto es serializable si su clase implementa la interface Serializable. La interface Serializable se encuentra en el paquete java.io. Es una interface vacía. No contiene ningún método.

Public interface Serializable {
}

Sirve para indicar que los objetos de la clase que lo implementa se pueden serializar. Solo es necesario que una clase la implemente para que la máquina virtual pueda serializar los objetos.

Si un objeto contiene atributos que son referencias a otros objetos estos a su vez deben ser serializables.


Todos los tipos básicos Java son serializables, así como los arrays y los Strings.




5. Logging: niveles, formatos y configuraciones

Los objetos LOG son usados para enviar mensajes sobre las excepciones, advertencias o información sobre ocurrencias del sistema. En Java la funcionalidad para operaciones LOG esta implementada en el paquete java.util.logging.

Los mensajes log pueden ser enviados a diversos dispositivos, como a consola, archivos log, base de datos, y otros. 

Niveles
Existen varios niveles de log, como muestra la siguiente lista:

SEVERE: Describe eventos que son de mucha importancia para el sistema a fin de que este no tenga un colapso severo.
WARNING: Describe mensajes de peligro o advertencia de sucesos de más bajo nivel que SEVERE.
INFO: Describe mensaje solo de información.
FINE: Describe sucesos sobre fallas menores (recuperables).


Ejemplo de uso de logging.

Formatos
Los formatos de los mensajes log están expresados a través de los parámetros que se desean incluir en el mensaje, como por ejemplo:

Object[] parametros = new Object[]{5, "hola", 8f};
LOGGER.log(Level.INFO,
"\np 0: {0}"
+ "\np 1: {1}"
+ "\np 2: {2}"
+ "\np 1: {1} (2da. vez)", parametros);

El arreglo parámetros de objetos contiene los datos a insertar en el mensaje:
"\np 0: {0}"
+ "\np 1: {1}"
+ "\np 2: {2}"
+ "\np 1: {1} (2da. vez)", parametros);

Entonces, el primer valor del arreglo se inserta en {0}, el segundo en {1} y así sucesivamente. También se puede repetir un dato como muestra el ejemplo al repetir el segundo valor, de la siguiente forma:

"\np 1: {1} (2da. vez)"

Configuraciones
Por defecto los mensajes log se pueden mostrar en consola, pero mediante un archivo properties (log. properties) se puede especificar que estos se almacenen en el archivo en el disco duro. Observe el contenido de un archivo de configuración:











Capítulo 8 Hilos y Concurrencia

1. Hilos

Al arrancar un programa Java, este se ejecuta por defecto sobre un único hilo de ejecución, es decir, por ejemplo, cuando se programa un evento listener en un botón cuya ejecución resultase muy pesada, tardando un tiempo prolongado en procesarse, provocaba que la ventana quedase bloqueada
no pudiendo recibir más eventos hasta que un evento listener se terminase de ejecutar. Para estos casos, es necesario ejecutar el evento en otro hilo en paralelo al hilo principal.

Java posee mecanismos para ejecutar ciertas clases abriendo un nuevo hilo (thread). A la hora de crear hilos, tenemos que tener presente que en el sistema operativo solo existirá un único proceso de ejecución (el programa java.exe que lanzó la aplicación en el caso de SO Windows), por lo tanto,
todos los threads abiertos sobre este proceso compartirán ciertos recursos asignados por el sistema operativo, por ejemplo, la cantidad de memoria atribuida al proceso tendrá que ser administrada para todos los hilos. Para tener claro el concepto de thread, podemos imaginar la propiedad de
auto salvado de la mayoría de procesadores de texto. Esta funcionalidad se ejecuta en segundo plano según se va introduciendo texto en el documento, es decir la operación de auto salvado no bloquea al usuario, permitiéndoles que siga escribiendo mientras el archivo se está guardando.

2. Clase Thread e Interfaz Runnable

Un thread o un hilo de ejecución en Java, generalmente se abrirá a través de la clase java.lang.thread, que a su vez implementa la interfaz java.lang.runnable.

2.1 ¿Qué es un Thread?


Los principales métodos del objeto Thread son:

La clase Thread también dispone de una serie de métodos estáticos:



Entre los métodos de las dos tablas anteriores, no se ha hecho referencia a dos funciones de vital importancia en el manejo de hilos. Estos son los métodos wait() y notify(), que derivan de la superclase Object. El método wait dejará el hilo actual en un estado de espera, hasta que se invoque al método notify que volverá a activar el hilo.

2.2 Ciclo de vida de un thread


Como vemos en la figura anterior un thread puede tener los
siguientes estados:
  • Ready: El thread está listo para ser ejecutado.
  • Running: El thread está siendo ejecutado.
  • Waiting: El thread está esperando a que ocurra un evento para continuar con su ejecución.
  • Sleeping: El thread se encuentra detenido.
  • Blocked: El thread está esperando a que se liberen los objetos necesarios para poder continuar.
  • Dead: Finalización de un thread.

2.3 Creando un thread



Existen dos formas básicas para implementar un thread, tal y como se refleja en la figura.
La primera técnica que veremos para crear un nuevo hilo de ejecución será extendiendo la propia clase Thread.
Clase que extiende de Thread.

En el ejemplo anterior hemos extendido la clase Thread sobrescribiendo el método run que será el que se ejecute en el nuevo hilo cuando se invoque al método start de la clase, tal y como lo hace la main del listado. En ambas clases hemos llamado al método sleep, para que los hilos se queden dormidos durante un corto período de tiempo y permitirnos ver mejor como las dos clases se ejecutan en threads distintos, de ahí la razón que al invocar a la clase main, podamos observar cómo se entremezclan en la consola de salida, los System.out de la clase Hilos y ThreadExample, esto quiere decir que se están ejecutando en paralelo.

La otra opción que tenemos a la hora de crear threads es implementar la interfaz Runnable, que al igual que en el caso anterior, su método principal tendrá el nombre run (). Para ejecutar la implementación de Runnable como un nuevo hilo, pasaremos una instancia de la misma al constructor de la clase Thread.

Por ejemplo:
new Thread (new
MiClaseQueImplemetaRunnable()).start().

Así pues, podemos cambiar los listados para construir el thread
implementando a la interfaz Runnable.
Clase que implementa Runnable.

Clase que se ejecuta en un thread principal y abre uno nuevo.

2.4 Sincronización de métodos

Cuando trabajamos con múltiples hilos, tal vez necesitamos que un método no pueda ser ejecutado al mismo tiempo por varios threads, por ejemplo, tal vez tengamos una función que escribe en un fichero, resultando imposible que varios hilos intenten a la vez, escribir en él. Para estos casos existe el modificador synchronized, que utilizaremos en la definición del método que queremos que sea sincronizado entre distintos hilos. Si un thread intenta acceder a un método sincronizado que está siendo ejecutado por otro hilo, se quedará bloqueado hasta que el primer hilo termine de ejecutarlo. 

Esto puede ser peligroso ya que puede ocurrir que un método sincronizado invoque de forma incorrecta a otro que también lo es, dejando los hilos bloqueados. Para ver cómo funcionan los métodos sincronizados utilizaremos los ejemplos anteriores, pero en vez de escribir trazas a través de la instrucción System.out, lo haremos a través de un método de una nueva clase EscribeTrazas:

Clase EscribeTrazas
La clase anterior dispone de dos métodos estáticos que escriben 100 veces el texto pasado como parámetro. La única diferencia entre ellos es que uno es sincronizado. Comenzaremos primero modificando  a clase Hilos y RunnableExample para invocar al método no sincronizado.


Clase RunnableExample invocando a EscribeTrazas.


Creando un nuevo hilo e invocando a EscribeTrazas.


 Si ejecutamos el ejemplo anterior, obtendremos las trazas entremezcladas, ya que tanto el hilo de la main y el de RunnableExample, escriben a través de un método no sincronizado.

Si cambiamos y utilizamos el método escribeSyn en lugar de escribe, la salida de las trazas cambiará, escribiendo las 100 líneas del primer hilo que lo ha ejecutado y a continuación las 100 del otro.

Finalmente, añadir que la interfaz Runnable es considerada como una interfaz funcional, por lo tanto se pueden aplicar expresiones Lambda. Por ejemplo, podríamos simplificar los programas de la  siguiente manera.

new Thread (()-> EscribeTrazas.escribeSyn("Ejecutando
Thread")).start();
        EscribeTrazas.escribeSyn("Main Thread:");