Java y Eclipse
Java y Eclipse
exporttype=1
Java y Eclipse
Desarrolle una aplicación con Java y Eclipse
Este libro sobre Java y Eclipse se dirige a los desarrolladores y estudiantes de informática. Enlaza los
conocimientos teóricos y prácticos apoyándose en el desarrollo de una aplicación de gestión.
Desde la comprensión de los conceptos de POO pasando por el análisis, se guía al lector paso a paso en la
construcción de la aplicación. Para la parte de desarrollo que constituye lo esencial de este libro, podemos resumir
los puntos fuertes en la explotación de una base de datos con varias tablas con MySQL y JPA, la codificación
de las principales clases después de un acercamiento mediante ingeniería software basada en UML, la
utilización del patrón MVC, la creación de los tests unitarios con JUnit y una especial atención a la
internacionalización de una aplicación.
A medida que avance en el libro y en la realización del proyecto, el lector descubrirá las principales novedades
de Java 8 como son las funciones lambdas, los streams y la gestión de fechas, los pasos prácticos y los
conocimientos teóricos indispensables al desarrollo de una aplicación profesional, y se le propondrá diferentes
alternativas para alcanzar mayor conocimiento de la programación Java.
El entorno de desarrollo se basa en Java 8, Eclipse IDE for Java Developpers, Xampp para el servidor,
WindowBuilder para realizar vistas bonitas y por fin JasperReports para la edición de los informes y
generación de gráficos. Aunque el libro se haya escrito con la versión 4.4 de Eclipse (llamada Luna), su contenido
así como el proyecto desarrollado son compatibles con la versión 4.5 de Eclipse (llamada Mars).
Al final, la aplicación de gestión incluye las funcionalidades esenciales en una aplicación profesional:
gestión de clientes, de artículos y de pedidos edición y exportación de los informes a los principales formatos
(html, pdf,...) , creación de gráficos vistas multi ventanas con actualización simultánea.
A la vez formador, ingeniero y profesor de informática, Henri LAUGIÉ aúna conocimientos y experiencia tanto
técnicos como pedagógicos. En este libro, en el que se privilegia el aprendizaje mediante la práctica, el autor se
centra en lo esencial con cuidado de explicar lo más claramente posible todos los conceptos necesarios para el
desarrollo de aplicaciones, desde los más básicos hasta los más avanzados.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Introducción
Java 8 está disponible desde el 18 de Marzo de 2014. El reconocido lenguaje de programación orientado a objetos
de SUN, comprado por Oracle en Abril de 2009, es utilizado hoy en día por alrededor de nueve millones de
desarrolladores en el mundo e instalado en más del 95% de los ordenadores.
Para los desarrolladores, varias versiones están disponibles en el sitio web de Oracle (o los que dependen de Oracle:
openjdk.java.net, java.net) para cubrir las principales plataformas: Linux, Solaris y Windows. Apple tiene
disponible, en la actualidad, su propia implementación del JDK. Este libro utiliza el JDK 8 versión 64 bits para
Windows.
El programa utilizado para desarrollar con Java es Eclipse IDE for Java Developers. Esta versión incluye
WindowBuilder, que permite crear fácil y rápidamente interfaces HMI (interfaces hombremáquina) bonitas. Para
crear informes (también llamados reportes) personalizados y gráficos, se ha elegido la herramienta de reporting
JasperReports. Aunque el libro haya sido escrito con la versión 4.4 de Eclipse (llamada Luna), su contenido así
como el proyecto desarrollado son compatibles con la versión 4.5 de Eclipse (llamada Mars).
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Titulada Fases de preparación, esta primera parte empieza por la instalación y la configuración del entorno de
desarrollo, seguidas del aprendizaje del IDE (Integrated Development Environment). Se repasan los conceptos
básicos de la POO (Programación Orientada a Objetos) antes de usar y profundizar en la escritura del código. Se
presenta después el proyecto. El objetivo es entregar una aplicación de gestión de pedidos próxima a una solución
profesional que responda a un documento de requisitos determinado. Los puntos fuertes de la aplicación final, sin
tener en cuenta las acciones de introducción, eliminación, actualización y búsqueda de datos, son la edición y
exportación de informes a los formatos principales (PDF, HTML...), y la elaboración de estadísticas y gráficas. Las
fases de preparación finalizan con una presentación somera del análisis basado en UML (Unified Modeling
Language), y la instalación de la base de datos MySQL con el servidor XAMPP.
La parte dedicada al Desarrollo de la aplicación, que constituye la parte más importante de este libro, empieza
por la maquetación, la escritura de las entidades o clases con sus clases CRUD (CreateReadUpdateDelete) y la
gestión de la conexión con JPA (Java Persistence API). Continúa con la utilización del patrón de diseño MVC
(ModelViewController) a través de la gestión de los clientes. Concepto esencial en la creación de interfaces HMI
modernas, se detalla MVC en profundidad. A continuación es posible crear una aplicación multi ventanas con
actualización simultánea de diferentes vistas sobre los mismos modelos de datos. Las siguientes etapas del
desarrollo con la gestión de los artículos y las facturas permite abordar la dimensión multi tabla, presente en toda
aplicación de gestión.
Los enlaces URL referenciados en el libro se corresponden, esencialmente, con recursos open source. Pueden
cambiar rápidamente en el tiempo. Conociendo los recursos a utilizar, basta con buscar en su motor de búsqueda
favorito para obtener los nuevos enlaces para la descarga. En este caso, se trata a menudo de una nueva versión.
Los desarrolladores o los editores pueden también decidir dejar inaccesibles ciertos recursos (aplicaciones, plugins,
librerías,...). Esto tiene que ver habitualmente con versiones que ya no son actualizadas o que se han convertido
en versiones de pago. Deberá entonces buscar soluciones open source similares (a nivel lógico y conceptual) o
pagar la licencia. El lector puede, a continuación, probar sin gran dificultad los ejemplos propuestos. Recordamos
que el libro está destinado a informáticos, estudiantes de informática o verdaderos autodidactas.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Bajo Windows, según las versiones, puede proceder de diferentes maneras. La más sencilla es utilizar
alguno de los sitios dependiente de Oracle, por ejemplo: http://java.com/es/download/installed.jsp
Si no dispone todavía de la versión 8 para desarrolladores, puede descargarla en la web de Oracle. El acceso a los
recursos Java no es demasiado práctico, le dejamos la URL de acceso directo actualmente:
http://www.oracle.com/technetwork/java/javase/downloads/jdk8downloads2133151.html
Acepte el acuerdo de licencia y elija la versión del JDK que corresponde a su sistema operativo: Windows,
Linux, Solaris o Mac OS.
Para desarrollar el proyecto bajo Windows 8 64 bits, la versión Windows x64 es la elegida.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Proceda con la instalación del JDK haciendo doble clic sobre el ejecutable.
Puede utilizar las opciones por defecto del instalador, están bien adaptadas para el uso de Eclipse.
Proceda a su instalación y haga doble clic en el archivo index.html en la carpeta tutorial para consultarla en
modo desconectado (offline).
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Descargue la versión Eclipse IDE for Java Developers después de haber elegido la versión correspondiente
a su sistema operativo.
La versión Windows 64 bits es la que debe elegir para trabajar bajo Windows 8 64 bits.
En la primera ejecución, Eclipse propone una carpeta por defecto llamada workspace (o espacio de
trabajo en español) en la que se guardan los proyectos. Puede crear y seleccionar otra carpeta más
personal.
Seleccione la opción Use this as the default and do not ask again para abrir este workspace por defecto.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
En la primera ejecución, Eclipse presenta también una página de bienvenida que permite obtener información
acerca del IDE, repartida en cuatro secciones.
Overview: permite acceder rápidamente a la sección de ayuda en línea correspondiente a la sección seleccionada.
Tutorials: permite acceder a asistentes que proponen, bajo la forma de tutoriales, realizar aplicaciones simples o
plugins.
What’s New: permite acceder rápidamente a la parte de ayuda en línea sobre las novedades de Eclipse.
Estas son las características de Eclipse Luna propuestas por la ventana About Eclipse.
Ahora hay que verificar que Eclipse está bien configurado para trabajar con el JDK 1.8.
Haga clic en Windows Preferences, elija Java Compiler y asegúrese de que se trata de la versión 1.8
en la lista desplegable.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Arriba a la izquierda de las preferencias de Eclipse, existe un campo de búsqueda a su disposición para acelerar su
elección entre dichas preferencias.
Después consulte el apartado Installed JREs. Verifique que la referencia al JDK 1.8 está realmente
presente.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Descubriendo el IDE
Acceda sin más dilación al workbench o plan de trabajo.
1. Perspectiva Vista
Eclipse propone un entorno de desarrollo por defecto llamado perspectiva, compuesto de varias pestañas (vistas
en la terminología de Eclipse). Más precisamente, se trata de la perspectiva Java.
Existen otras perspectivas accesibles en el menú Windows Open Perspective y se utilizan dependiendo de las
necesidades del desarrollador. Para no saturar el espacio de memoria del IDE, es mejor cerrar las que no se
utilizan.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/7
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Cada perspectiva está constituida de un número determinado de elementos llamados vistas (views) que no tienen
por qué estar todas abiertas (visibles). Igual que para las perspectivas, abra únicamente las vistas que le resulten
realmente útiles, en caso contrario se arriesga a trabajar en una parte de la pantalla tan grande como un sello
postal.
Para añadir por ejemplo la vista Console a la perspectiva Java, elija la opción del menú Window Show
View Console.
Para mostrar una vista en todo el espacio disponible, haga doble clic en su pestaña. Vuelva a hacer doble clic
en la pestaña para restaurar la vista con su tamaño inicial.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/7
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Para restaurar una perspectiva, haga clic con el botón derecho en el nombre de la pestaña situado arriba a la
derecha de la pantalla, y elija Reset.
2. Editor de código
Es posible elegir entre dos editores de código, Java Editor y Text Editor.
Una instrucción puede ser larga y sobrepasar el ancho de la zona de código de la pantalla. Para escribirla en varias
líneas, basta con efectuar un salto de línea en el lugar donde quiera cortar la línea.
Ejemplo:
laVentana.getJLab_Action().setIcon(new ImageIcon(
Ventana.class.getResource("/imagenes/gestion/modificar.png")));
Por el contrario, para cortar cadenas, debe obligatoriamente utilizar el operador +. Dicho esto, Eclipse se encarga
de hacerlo por usted en cuanto realiza un salto de línea.
Como ejercicio, sitúe el cursor en medio de una cadena de caracteres, y pulse sobre [Intro].
Ejemplo:
...
getResource("/imagenes/gestion" +
"/modificar.png")));
Para beneficiarse del autocompletado, teclee las primeras letras seguidas de la combinación de teclas [Ctrl]
[Espacio].
Eclipse completa lo tecleado o le propone una lista. Puede parametrizar el autocompletado modificando
las opciones disponibles aquí: Window Preferences Editor Content Assist.
Para beneficiarse de una ayuda conceptual, sitúe por encima de una palabra del código con el cursor del
ratón.
Ejemplo:
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/7
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Para provocar voluntariamente la indentación, seleccione la o las líneas de código contiguas afectadas y
utilice el atajo de teclado [Ctrl] I o haga clic con el botón derecho y elija Source Correct Indentation.
Notará que en el menú contextual los comandos disponibles están acompañados a su derecha de los atajos de
teclado asociados.
Para formatear el código (espacios, indentación...), utilice el atajo de teclado [Ctrl][Shift] F, o haga clic
derecho y seleccione Source Format.
Para formatear solamente una parte del código, seleccione el código y efectué la operación anterior.
Para desactivar el corrector ortográfico que actúa sobre sus comentarios, elija Window Preferences
General Editors Text Editors Spelling y desactive la opción Enable spell checking.
Para añadir los números de las líneas, desarrollar o reducir el código, efectúe un clic derecho en la banda
vertical izquierda ligeramente gris.
3. Depurador
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/7
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
El desarrollo necesita numerosos test. Para controlar la correcta ejecución de una parte de un programa que
contiene estructuras condicionales e iterativas, el uso del depurador resulta en muchas ocasiones indispensable.
El descubrimiento del depurador de Eclipse se hará a través de un programa que muestra las tablas de
multiplicación.
Haga un clic derecho en el margen gris delante del número de la línea 20 y elija Toggle Breakpoint.
Pruebe el programa pulsando esta vez únicamente en la tecla [F11] para lanzar el depurador.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/7
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Esto tiene como resultado abrir esta perspectiva; se añade un botón en la barra principal de los iconos del IDE
para permitir acceder a ella rápidamente.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 6/7
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
El depurador permitirá verificar la exactitud de los datos controlando paso a paso los valores de las variables.
Estando el punto de interrupción en la línea 20, el recorrido se efectúa en el bucle for interno. A la siguiente
iteración, la variable fila debe contener el número 1 y las variables columna y numero deben permanecer con
su valor anterior.
Para seguir con las pruebas y leer los valores de las variables, presione de nuevo en [F5].
Para volver a la perspectiva Java, haga clic en el botón Java arriba a la derecha de la ventana principal del
IDE.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 7/7
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Primer proyecto
Para familiarizarse con el workbench, se crea una clase muy sencilla.
Primero debe crear un proyecto y después un package, toda clase de una aplicación que se considere profesional
debe pertenecer a un package.
Como recordatorio, un package es físicamente una carpeta, y simbólicamente un espacio de nombre que permite
reagrupar clases con la misma funcionalidad.
Si no reagrupa sus clases en un package, Eclipse la guardará en el package por defecto. Está desaconsejado usar el
package por defecto.
También es la ocasión de proponer una estructura para los archivos del proyecto, con el fin de mejorar la legibilidad
y testeabilidad de las aplicaciones.
Como preámbulo a este trabajo, y para facilitar la apertura de los archivos que provengan de sistemas operativos
diferentes, es conveniente indicar la codificación de los archivos.
En el menú, elija Window Preferences. Diríjase al apartado General Workspace y elija el valor Other
UTF8 en la sección Text file encoding.
Este parámetro permite que los archivos con caracteres acentuados en Eclipse puedan leerse correctamente, ya
sea en Windows, Linux o Mac.
Los acentos así como los caracteres especiales están permitidos en los nombres de variables, pero no se recomienda
su uso. Lo entenderá el día en el que abra su proyecto en un ordenador Linux, por ejemplo, o cuando tenga que
trabajar en un proyecto con los nombres de variables en chino, japonés, hebreo o farsi.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
En el menú elija File New Java Project, o haga un clic derecho en la vista y después New Java
Project.
Aparece la ventana de diálogo de creación de proyecto. Permite dar un nombre al nuevo proyecto y elegir dónde
guardarlo en el disco duro donde almacenaremos los archivos del proyecto (por defecto, los proyectos se
almacenan en la carpeta del espacio de trabajo elegido durante la instalación de Eclipse).
Introduzca el nombre del proyecto y haga clic en el enlace Configure default junto a la opción Create
separate folders for sources and class files.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Esta acción permite definir las carpetas en las cuales guardaremos los archivos fuente y los archivos binarios
compilados. De manera general, estos dos tipos de archivos deben estar separados. Esto permite por ejemplo
copiar o eliminar fácilmente los binarios.
Los archivos fuente en un proyecto Java tienen muchas veces varios tipos: existen archivos fuente para las clases
de la aplicación, archivos fuente para las clases de test, archivos de recursos tales como imágenes o
configuraciones...
La siguiente acción permite definir las distintas carpetas para cada uno de estos tipos. Estas carpetas están
inspiradas de los proyectos Maven (http://maven.apache.org/) y Gradle (https://gradle.org/), que son potentes
herramientas para la gestión de proyectos informáticos, y permiten lanzar desde la línea de comandos o mediante
una interfaz gráfica la compilación de archivos fuente en binarios, la realización de test unitarios, la transformación
de clases en archivos jar, el empaquetado en un instalador y el despliegue en un servidor, por citar solamente
algunas de las funcionalidades disponibles.
Maven aplica el refrán «cada cosa en su sitio, y un sitio para cada cosa» y define la carpeta src/main/java como
la carpeta para los archivos fuente, src/main/resources para los archivos de configuración y las imágenes,
src/test/java para los archivos fuente de test y src/test/resources para los archivos de configuración de los
test. En cuanto a los archivos binarios, se guardan en la carpeta target/classes.
Modifique los nombres de las carpetas como en la siguiente imagen, y presione OK.
Para renombrar un proyecto, haga clic con el botón derecho en su nombre y elija Refactor Rename. El
procedimiento es el mismo para cualquier contenido incluido dentro de la vista Package Explorer del workbench.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Primer package
Seleccione su proyecto en la vista explorador de packages, y elija Package en el menú File New.
Introduzca el nombre tomaDeContacto.primero utilizando una minúscula como primer carácter del
nombre respetando así las convenciones de escritura del lenguaje Java y haga clic en Finish.
Si mira las carpetas creadas en el disco duro (por ejemplo con el explorador de Windows o eligiendo Navigator en
el menú Navigate Show In), obtendrá una estructura de carpetas similar a la que sigue:
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Los archivos y carpetas que empiezan por un punto (como .classpath, .project y .settings) son gestionados por
Eclipse. Está fuertemente desaconsejado modificarlos o eliminarlos.
Con esta acción, se han creado dos packages: tomaDeContacto y primero, situado en el package
tomaDeContacto, ya que se ha utilizado el carácter punto como separador de packages. Un package puede contener
otros packages, al igual que las carpetas pueden contener subcarpetas.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Primera clase
Seleccione el package creado.
Elija el menú File New Class o haga clic con el botón derecho y elija New Class.
Introduzca el nombre para la clase MiPrimerPrograma empezando por una mayúscula (para respetar las
convenciones de nombres de Java) y seleccione la opción public static void main(String[] args). Esta
opción permite crear el método que sirve de punto de entrada a la ejecución del programa.
En las convenciones de Java, solo las clases empiezan por una mayúscula. Los packages, atributos y variables
empiezan por una minúscula.
Los caracteres especiales (%, ?,...) y los números no pueden utilizarse como primer carácter.
Se ha añadido la clase al package y se puede ver el código generado automáticamente por Eclipse en el
editor de código.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
package tomaDeContacto.primero;
}
}
Una de las reglas de programación del autor de estas líneas es empezar siempre declarando las propiedades o
métodos como privados. Si es necesario acceder desde el exterior de la clase, se modifica la visibilidad. Esto permite
una mejor encapsulación de los datos y se puede resumir con la máxima de «primero prohibir, luego permitir».
Elija en el menú contextual Source Generate Getters and Setters.... Después seleccione la opción
nombre (las demás opciones debajo de la variable nombre son elegidas automáticamente) y valide las
elecciones.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
this.nombre = nombre;
}
Añada un constructor para esta clase haciendo clic con el botón derecho en el editor de código y eligiendo la
opción Source Generate Constructor using Fields....
Eclipse inicializa automáticamente la propiedad nombre ya que se seleccionó la opción de la variable en la ventana
de diálogo anterior. Los nombres de las propiedades y de la variable son las mismas: Para diferenciarlas, se prefija la
propiedad de la clase con this seguido de un punto.
Para añadir comentarios en la clase, añada dos barras (/) al inicio de la línea o desde donde quiera comentar el resto
de la línea. Todo lo que es comentario no será tomado en cuenta por el compilador.
Si se escriben las palabras TODO o FIXME en un comentario, se indexará la línea en Eclipse. Es una manera práctica
de dejar indicaciones sobre lo que queda por hacer o corregir, al final de una dura jornada de trabajo.
Eclipse añade la línea TODO Autogenerated method stub cuando se genera un método automáticamente. Es mejor
quitarlo o substituirlo por sus propios comentarios.
Es posible añadir comentarios especiales que comienzan por /** y terminan por **/. Se usan estos comentarios
llamados javadoc para generar la documentación de la aplicación.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Ya encontramos numerosas nociones propias a la programación orientada a objetos (POO) en estas pocas líneas:
package, propiedad, constructor, getter, setter,...
Haremos un recordatorio de estas nociones en el capítulo Conceptos básicos de la POO y en la realización del
proyecto.
Se consideran las minúsculas y mayúsculas como caracteres diferentes: Java distingue las minúsculas de las
mayúsculas.
Toda clase debe estar dentro de un package. Un package puede contener otros packages.
En general, la case posee un constructor explícito. Este constructor permite crear instancias (hablamos de
instanciación) de la clase, siendo estas instancias llamadas también objetos.
Todos los métodos provienen obligatoriamente de una clase: ya sea una clase creada por un desarrollador, una
clase obtenida de la API Java, o una clase obtenida de una librería creada por otros desarrolladores.
El método principal main() permite ejecutar la clase. Es el punto de entrada de la aplicación: siempre se le llama el
primero.
Una clase empieza por una mayúscula, una variable por una minúscula.
Nada impide crear varias clases que dispongan del método main(). Entonces es necesario designar aquella cuyo
método main() se ejecuta, siendo por lo tanto las demás ignoradas.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Intente crear solamente un método main() por proyecto. Esto permite evitar las ambigüedades.
Para entender mejor la organización de los packages y de sus clases puede acceder a los archivos fuente de Java
navegando a la carpeta del JDK en el disco duro.
A continuación puede ver como ejemplo una parte de las clases del package java.lang:
Es posible visualizar la misma organización dentro de Eclipse, pero esta vez con los archivos binarios.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 6/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Primera ventana
Crearemos ahora una pequeña aplicación con una interfaz gráfica (GUI, del inglés Graphical User Interface, HMI
para interfaz hombremáquina en español).
Una de las novedades de Eclipse desde la versión Indigo es poner a disposición un editor de interfaz gráfica
proveniente del mundo Google, WindowBuilder. Existen otros pero teniendo en cuenta la calidad de
WindowBuilder, utilizaremos este para el proyecto. En cuanto al plugin Visual Editor, su desarrollo ha sido
abandonado.
WindowBuilder puede configurarse mediante el menú Window Preferences. Para el proyecto usaremos las
opciones por defecto.
WindowBuilder es un plugin de Eclipse que permite crear rápidamente interfaces gráficas WYSIWYG (What You
See Is What You Get: lo que ve es lo que obtiene).
Si desea que el código generado automáticamente tenga un formato distinto, puede parametrizar WindowBuilder
dirigiéndose al menú Window Preferences WindowBuilder.
Todos los editores de interfaces gráficas tiene la misma característica: permiten crear rápidamente componentes
gráficos. Por el contrario, no dan casi nunca directamente lo que desea: será por lo tanto necesario intervenir
después en el código generado.
1. Creación de la ventana
El objetivo es crear una ventana muy sencilla apoyándose en WindowBuilder.
Esta ventana contendrá un campo de introducción de texto en el que el usuario podrá introducir palabras. Con
un clic en el botón OK de la ventana, aparecerá lo que se ha introducido en otra ventana.
Haga clic con el botón derecho en el proyecto en el explorador de packages y elija New Other.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Un JFrame es una ventana gráfica autónoma, susceptible de contener otros elementos gráficos. Se trata del
contenedor gráfico de esta aplicación.
Es posible crear otros contenedores gráficos, en particular JDialog que utilizaremos para crear ventanas de diálogo
y JPanel que es un contenedor de elementos gráficos que no tiene representación en ventana.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Al igual que para la clase PrimerPrograma, se genera automáticamente el código y aparece en la ventana del
editor de código.
Debajo del código del editor, aparecen dos pestañas: Source y Design. Source sirve para visualizar y editar el
código fuente en modo texto, Design sirve para visualizar y modificar la ventana en modo gráfico.
Si las pestañas no aparecen, puede forzar la apertura de la clase con el editor de WindowBuilder de la siguiente
manera:
Seleccione la clase PrimeraVentana en el explorador de packages, haga clic con el botón derecho y
seleccione Open With WindowBuilder Editor.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
La ventana creada contiene un componente particular de tipo JPanel, que es un contenedor en el cual es
posible situar otros componentes gráficos tales como botones, campos de introducción de datos, tablas, listas u
otros JPanel.
Este componente JPanel es la base gráfica de la aplicación, cargándose la ventana antes de incluirla en el sistema
de ventanas del sistema operativo y permitiendo a la aplicación disponer de un icono, un título, botones de
maximización, minimización, de cierre, y definiendo una posición y un tamaño en la pantalla del ordenador.
Haga clic en el centro de la pequeña ventana del editor. Esto selecciona el panel principal.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Para acceder al código del componente seleccionado, haga clic en la pestaña Source.
2. Añadir componentes
Una ventana vacía no resulta muy útil, ni atrayente. Vamos a añadirle componentes gráficos (llamados también
widgets), así como algunos pequeños elementos para vestir la ventana, como:
un título,
un icono,
un botón de validación.
Elija en su disco duro una imagen, si es posible que sea de 32 píxeles por 32 pixeles.
Cree una carpeta que contenga sus recursos gráficos, haciendo clic con el botón derecho y seleccionando
New Source Folder. Introduzca src/main/resources como nombre de carpeta.
Eclipse (y Java) reconoce automáticamente el carácter / como separador del nombre de la carpeta, ¡y eso en todos
las plataformas!
Cree un package tomaDeContacto.grafico en esta nueva carpeta. Debe tener el mismo nombre que el
package de la clase gráfica.
La sección Properties situada debajo se actualiza para mostrar las propiedades de la ventana.
Haga clic en el pequeño botón con puntos suspensivos junto a la propiedad iconImage.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Haga clic en el radio botón Classpath resources y abra la carpeta resources en la sección Parameters
hasta que aparezca su imagen. Haga doble clic en ella.
En la sección Properties, deslice verticalmente las propiedades hasta ver la propiedad title. Haga clic junto
al nombre de la propiedad para cambiar su valor.
El panel es un contenedor gráfico, y todo contenedor debe saber cómo ubicar lo que contiene en su interior.
Java permite especificar las reglas de ubicación con la ayuda de un layout, o disposición. El layout por defecto de
un Panel es el BorderLayout.
Modifique este layout por Absolute layout con la ayuda de la propiedad del panel Properties.
Este layout permite ubicar los elementos del panel con precisión de pixel.
Este layout es útil pero debería estar reservado a algunos casos particulares, ya que no gestiona bien el
redimensionamiento de las ventanas.
De la misma manera, seleccione, sitúe y redimensione un componente JTextField para añadir un campo
de introducción de datos a la ventana.
Seleccione, sitúe y redimensione un componente JLabel. Este componente permite mostrar textos no
editables e imágenes.
Modifique sus propiedades: fuente, tamaño de carácter, alineación (por ejemplo centrado), color,...
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 6/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Acción de un botón
El objetivo de esta primera ventana es mostrar en un cuadro de diálogo el mensaje que el usuario de la aplicación
haya introducido.
De momento, la construcción de la ventana permite al usuario introducir este mensaje con la ayuda del
JTextField. El JLabel da indicaciones sobre lo que el usuario puede hacer. El JButton permite validar la
introducción y mostrar el cuadro de diálogo.
Sin embargo, si hace clic en el botón, no ocurre nada. En este punto, es totalmente lógico. Ahora debe codificar la
acción ejecutada al hacer clic en el botón.
En modo Design, seleccione y haga clic con el botón derecho sobre Validar en la visualización previa.
Después seleccione la siguiente opción Add event handler action actionPerformed.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
En el bloque del método actionPerformed(ActionEvent e), por lo tanto dentro de las llaves, añada la
siguiente línea:
JOptionPane.showMessageDialog(btnValidar,
"Ha introducido \"" +textField.getText() +"\"");
¿Cómo añadir unas dobles comillas en una cadena de caracteres? Debe precederlas del carácter de escape backslash
(o barra oblicua inversa: \).
btnValidar.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(btnValidar,
"Ha introducido \"" +textField.getText() +"\"");
}
});
Aparece un error en Eclipse, materializado por un pequeño icono rojo situado a la izquierda de la línea.
Esto indica que la clase PrimeraVentana no conoce de momento ninguna clase JOptionPane. JOptionPane es
una clase que permite crear fácilmente ventanas de diálogo.
Para corregir rápidamente el error, haga clic en el icono rojo. Aparece entonces un menú contextual que
propone soluciones. Cada solución muestra la visualización previa de su resultado por un tooltip con fondo
amarrillo.
Los números de línea no tienen por qué coincidir en la clase que está modificando.
Guarde las modificaciones con el atajo de teclado [Ctrl] S y pruebe el programa con la combinación de teclas
[Ctrl][F11].
Con las modificaciones anteriores, se ha creado un listener para un evento de tipo Action. Este listener posee un
código que se ejecuta cada vez que se realiza una acción sobre el botón Validar, es decir cada vez que un usuario
hace clic en él.
Volveremos a ver este proceso con más detalle en los siguientes capítulos.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Creación de un menú
Crearemos ahora un menú en esta primera ventana, muy rápidamente gracias a WindowBuilder.
Una inscripción verde debe aparecer para indicar dónde ubicar la barra de menú.
Seleccione y deposite el componente JMenu desde la paleta de WindowBuilder hacia la barra de menús que
acaba de crear.
Haga más grande la ventana para poder insertar los demás menús.
Deposite otro componente JMenu a la derecha del menú Archivo que acaba de crear.
Se trata ahora de añadir elementos (también llamados ítems) para cada menú.
Seleccione y deposite un componente JMenuItem desde la paleta hacia la zona designada (Add items
here).
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Seleccione un componente JMenu en la paleta y deposítelo dentro del menú Cliente. El menú Cliente se
abre si pasa el ratón por encima, para permitir depositar el componente en la zona (Add items here).
Cambie el nombre del submenú que acaba de crear por Gestión, tecleando este nombre en la visualización
previa o modificando la propiedad text en la sección Properties.
La línea gris entre Buscar y Eliminar se obtiene depositando un componente JSeparator como ítem del menú.
Seleccione el ítem Salir en el menú, haga clic con el botón derecho y seleccione la opción Set Action New.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Haga clic en la pestaña Source para mostrar el código fuente de la clase Java.
La clase de esta acción se llama SwingAction (se trata de un nombre generado automáticamente), que deriva del
tipo AbstractAction.
Cuando el usuario hace clic sobre el ítem Salir, se ejecuta el código del método actionPerformed.
El método dispose() permite liberar los recursos gráficos de la ventana y eliminarla de la pantalla. Como se trata
de la única ventana de nuestra primera aplicación, el programa finalizará después.
Existe un comando más rápido para terminar un programa Java: System.exit(0);. Este comando permite además
dar códigos de retorno de programa pero se puede abusar fácilmente de su uso. De una manera general, tenga un
punto de salida único para su aplicación, lo que permite cerrar limpiamente las conexiones a las bases de datos por
ejemplo.
Pruebe la ventana ejecutando el programa con [Ctrl][F11] y verifique que haciendo clic sobre SwingAction
se cierra bien la ventana.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
En las aplicaciones gráficas profesionales, los atajos de teclado son esenciales ya que permiten a los usuarios
ejecutar las acciones más habituales rápidamente, ganar tiempo y aumentar su productividad.
La siguiente etapa permite introducir estos atajos de teclado cambiando las propiedades de la acción.
public SwingAction() {
putValue(NAME, "Salir");
putValue(SHORT_DESCRIPTION, "Cierra la aplicación");
putValue(MNEMONIC_KEY, KeyEvent.VK_Q);
putValue(ACCELERATOR_KEY,
KeyStroke.getKeyStroke(KeyEvent.VK_Q,
InputEvent.CTRL_DOWN_MASK));
}
Rectifique los imports pulsando simultáneamente sobre las teclas [Ctrl][Shift] O (no es el cero, sino la letra O).
Es el atajo de teclado de Eclipse para reorganizar los imports de la clase y hacer que sepa quiénes son las
clases KeyStroke y InputEvent.
Pruebe la ventana ejecutando el programa, verificando que el texto del ítem ha cambiado, que la letra Q está
subrayada, y que se muestra CtrlQ junto a la acción.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Primer ejecutable
Los fuentes de una aplicación son esenciales para los desarrolladores. Para los usuarios, lo importante es el binario
(el ejecutable) sobre el que hacen doble clic para lanzar la aplicación.
La creación del ejecutable de la clase PrimeraVentana se realiza gracias a las siguientes etapas.
Elija la opción Export en el menú File de Eclipse. Se abre una ventana de diálogo.
Después elija el nombre del archivo que será generado, la única obligación es que el nombre del archivo tenga
la extensión .jar, haciendo clic en el botón Browse....
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Puede ocurrir que aparezca una ventana de diálogo de alerta. No tiene por qué ser muy grave, pero acostúmbrese a
leer los mensajes haciendo clic en Details.
Para acabar, verifique que su aplicación funciona haciendo doble clic sobre el archivo .jar que acaba de
generar.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Introducción
La Programación Orientada a Objetos, o POO, corresponde a un paradigma la representación de un sistema o un
dominio, para simplificar particular de la programación informática donde el objeto es el concepto central. En este
capítulo se presentan los conceptos fundamentales de la POO enfocándose en lo esencial y apoyándose en el
lenguaje Java. La mayoría de ellos están realizados y profundizados en un proyecto Luna, facilitando así su
comprensión.
El interés de la programación orientada a objetos es proporcionar medios para modelizar diferentes dominios
necesarios para su aplicación.
Este paradigma permite también minimizar y racionalizar los impactos debidos a modificaciones más o menos
grandes de las aplicaciones durante su ciclo de vida, extender más fácilmente sus funcionalidades, aislar las
responsabilidades del código, manipular abstracciones sin conocer necesariamente sus tipos concretos, y por lo
tanto más generalmente proponer programas más robustos y abiertos al cambio.
Cuando creamos un programa con programación orientada a objetos, tratamos de modelizar informáticamente
distintas nociones para poder reutilizarlas en otros programas. No se trata de modelizar todo lo que ocurre en el
universo físico, a menos que se disponga de un plazo de desarrollo MUY MUY extenso y querer crear Matrix, sino
focalizarse sobre las nociones pertinentes e indispensables al funcionamiento robusto de esta modelización,
dejando a su vez abierta la aplicación a futuras modificaciones.
Los ejemplos presentados en este capítulo tienen vocación de permitir entender de manera más sencilla las nociones
subyacentes. Su adecuación a una aplicación realista necesitarían primero formalizar el objetivo y los límites de dicha
aplicación.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Objeto
En el paradigma de programación orientada a objetos, todo es un objeto. Una ventana, un botón pero también
una factura, un cliente, etc. Más concretamente, un objeto puede considerarse como una entidad que posee
características y posibles acciones. Por ejemplo, un coche tiene una forma y un color y puede arrancar y parar.
Un gato también tiene varias características: el número de patas, un color determinado de pelaje, un nombre y
varios comportamientos: comer, ronronear, correr sobre las paredes, esconderse, tirar entre las piernas...
En la programación orientada a objetos, se representa Minino, vuestro gato, por un objeto, una instancia de la
clase particular Gato. Este objeto tiene por lo tanto propiedades específicas a su gato: su nombre, su color, su
edad, para empezar. También tiene propiedades comunes a todos los gatos, como las cuatro patas, los dos ojos, la
sangre caliente...
Un gato y todos los gatos es también un mamífero y más generalmente un animal, y más aun genéricamente
un ser vivo. La programación orientada a objetos intenta formalizar estas distintas categorizaciones y
características para obtener una abstracción, una modelización informática del objeto que nos interesa.
Cuando escuche la palabra Objeto, piense en un ejemplar determinado de este objeto: el gato Minino, el coche que
posee desde hace cuatro años, la factura que está editando...
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Clase
La creación espontánea no existe. El coche que conduce, la silla en la que está sentado, han sido fabricadas. Todos
estos objetos vienen de una «fábrica» o de una planta. Para una primera introducción en la noción de clase,
podemos retener este último término. También existen plantas de aviones, barcos, juguetes, etc.
Otra particularidad de las clases es el principio de agrupamiento. Evitamos fabricar en una misma planta coches,
barcos o juguetes y ordenadores al mismo tiempo. Los objetos fabricados están por lo tanto ligados a categorías que
son las clases en POO. Una clase es una especie de planta que fabrica categorías objetos que tienen características
comunes (los barcos tienen un casco, los coches una carrocería, los gatos un nombre).
Para definir una clase Gato en Java, basta con utilizar la palabra clase class.
class Gato {
// ... continuará
}
Observe las llaves abiertas y cerradas: todo lo que se encuentra entre estas llaves pertenece a la misma clase, y todo
lo que pertenece a la clase Gato debe encontrarse entre estas llaves. Se aplica el mismo principio para los métodos.
Para crear (también se dice instanciar) un nuevo objeto de tipo Gato que llamaremos miGato en la aplicación, use
el siguiente código con la palabra clave new:
Si la palabra fábrica parece aún abstracta, una metáfora pastelera puede ser más apropiada: la clase es el molde
pastelero, el objeto es el pastel en sí. Una clase permite crear objetos, al igual que un molde permite crear pasteles
concretos, todos únicos y diferentes los unos de los otros. Sin embargo todos provienen del mismo molde.
En el universo Java, todo o casi todo es clase y objeto. Cada elemento unitario de su modelización debe estar
incluido en un archivo único, llamado con el mismo nombre que la clase descrita en su interior.
En su proyecto, habrá por lo tanto un archivo Gato.java, que contendrá el código ejemplo descrito más arriba, y
formalizará la existencia de una clase Gato en la aplicación.
Como todo es un objeto en el paradigma de la programación orientada a objetos, todos los objetos que son
utilizados en la aplicación tendrán una clase común como punto de partida de su jerarquía: la clase Object. Se
trata de una clase que describe los atributos y comportamientos mínimos para todos los objetos que modelizará.
Se desarrollará todo lo referente a los aspectos jerárquicos de las clases en la sección que versa sobre la herencia.
Y como en Java todo o casi todo es objeto y clase, ¡existe también una clase Class! No hablaremos más de esta
clase, ya que se trata de un concepto más avanzado que se refiere a lo que llamamos los mecanismos de
introspección (reflection en inglés).
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Atributo
Volvamos a los que nos ocupa, es decir a nuestros gatos.
La primera versión de la clase Gato que se propuso no es muy interesante. Solo se ha formalizado el concepto de
un Gato. Para ir más allá, se completa la clase en función de las necesidades de cada uno. Suponiendo que se trata
de caracterizar los gatos por su nombre, color y edad, debe entonces añadir estas características (también llamadas
propiedades o atributos) a la clase.
class Gato {
nombre;
color;
fechaDeNacimiento;
En el estado actual de la clase, esta genera errores ya que las propiedades no están todavía tipadas.
Un gato tiene (posee) por lo tanto un nombre, un color y una edad, ya que es el objetivo de la modelización.
¿Por qué?
Imagine que este programa se ejecuta sin problemas durante varios años. Si para cada vez que cumpla años
debemos actualizar la edad de todos los gatos afectados, se complica la cosa. Sin embargo una fecha de nacimiento
es fija y no será nunca modificada, salvo en casos excepcionales.
La edad es de hecho lo que llamamos un atributo derivado, que se puede obtener conociendo la fecha de
nacimiento y la fecha actual.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Tipo de datos
Todo atributo debe imperativamente estar tipado: long para la fecha de nacimiento y cadena de caracteres para los
demás en nuestro ejemplo. El primer tipo es llamado simple. Hablamos de tipos elementales o primitivos. Tendrá
ocasión al abordar el desarrollo de utilizar tipos más enriquecidos, especialmente tipos de clases, como la clase
String.
class Gato {
String nombre;
String color;
long fechaDeNacimiento;
}
En las aplicaciones muchas veces aparece el problema de conversión (casting) de datos. Por ejemplo, las solicitudes
de introducción de datos mediante las ventanas de diálogo devuelven por defecto valores de tipo String. Para el
tratamiento de fechas o de números debe entonces convertir estos valores con el riesgo de provocar el
detenimiento repentino de la aplicación.
Quien puede con más, puede con menos. Considere el siguiente código:
int numeroEntero = 5;
double numeroDecimal = 0;
numeroDecimal = numeroEntero;
numeroDecimal = 5.5;
numeroEntero = (int) numeroDecimal;
A un double (que puede ser un número con signo muy grande con decimales) se le puede asignar un entero.
Hablamos de cast implícito. La operación inversa no es verdad. Debe antes convertir (hacer un cast explícito) el
double a entero, gracias al tipo indicado entre los paréntesis. Perderá entonces toda la información presente detrás
de la coma. Retendremos que para los números, las conversiones implícitas solo afectan a las que se hacen sin
pérdida de información.
Para conversiones de tipos totalmente diferentes, por ejemplo de un número a una cadena de caracteres, debe
hacer uso de clases particulares llamadas wrapper. Estas envuelven los datos tipados y los convierten gracias a
métodos apropiados a los tipos esperados.
Ejemplo:
Java 8 permite escribir un número con un separador underscore (_) para una mejor legibilidad.
Ejemplo:
e inversamente:
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Observe que la condición if utilizada arriba no es óptima, para entender mejor su mecanismo. Una forma más
correcta sería:
if (eleccion) {
System.out.println("Soy una booleano.")
}
Para las fechas, las conversiones son más complejas ya que hay que formatear y convertir las fechas en función de
la región. Java propone numerosas clases y métodos de las cuáles algunas serán utilizadas para nuestro proyecto.
Aquí tiene un ejemplo no óptimo de un método estático que convierte la fecha del día al formato díamesaño.
Java 8 propone ahora una nueva gestión de fechas con las clases del package java.time. En el capítulo siguiente
dispone de una explicación más completa de su utilización. Si no utiliza Java 8 para el desarrollo de su aplicación,
tendrá que gestionar estas fechas «a la antigua» o utilizar librerías externas para simplificar esta gestión.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Método
Los métodos representan las acciones y los comportamientos que pueden llevar a cabo los objetos provenientes de
una clase. Definen el aspecto dinámico o de comportamiento de la clase. Los gatos pueden dormir, comer, maullar,
etc.
class Gato {
// propriedades
String nombre;
String color;
long fechaDeNacimiento;
// métodos
void dormir(){
return ;
}
String maullar(){
String elMaullido = "miau";
return elMaullido;
}
}
Los métodos que no devuelven ningún valor corresponden a procedimientos. Siempre van precedidos de la palabra
clave void.
Los métodos que devuelven un valor corresponden a funciones. Siempre van precedidas del tipo de datos devuelto.
Los métodos pueden evidentemente tener parámetros que deben estar tipados.
La palabra clave return permite salir del método y volver al método del que se ha llamado
Ejemplo:
El código presentado aquí arriba conlleva acciones de autoboxing implícitas, noción que se detalla en el próximo
capítulo.
El nombre de un método y los tipos ordenados de sus parámetros constituye su firma (el tipo devuelto del método
es exclusivo de la firma, así como los nombres de los parámetros). Dentro de una clase dada, solo existe un único
método por firma.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
}
// el compilador no acepta este método
int calcular(int c, int d) {
return c + d ;
}
}
Entre los métodos, algunos tienen la función de devolver los valores de las propiedades de la clase y otros
modificarlos. Son llamados accesores y mutadores (getters y setters).
Ejemplo:
int getEdad () {
// devuelve la edad calculada a partir de la
// fecha de nacimiento
}
long getFechaDeNacimiento () {
return fechaDeNacimiento ;
}
El término de servicios se utiliza a veces para designar los métodos y el de operación para la implementación de los
métodos.
Los métodos pueden prefijarse con la palabra clave static. En este caso ya no son específicos al objeto en sí sino a
su clase. Esto permite muchas veces crear servicios auxiliares, y más comúnmente el punto de entrada de una
aplicación.
Ejemplo:
Los métodos estáticos son muy útiles pero tienen la desventaja de « romper » el paradigma de programación
orientada a objetos, y de hacer el código más complicado de modificar. Se desaconseja crearlos salvo que se tenga
una justificación para ello.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Accesibilidad
La POO añade el principio de accesibilidad (visibilidad) que consiste en controlar el acceso a las clases y a sus
miembros (propiedades y métodos).
El término de accesibilidad se utiliza también para designar las técnicas de uso de una aplicación para usuarios
minusválidos, por lo que en esta sección usaremos el término de visibilidad.
Clases :
public: las clases con visibilidad pública son accesibles por todo el mundo (todas las clases de todos los
packages).
<nada>: el hecho de no indicar la visibilidad de la clase hace a esta inaccesible fuera de las clases de su propio
package.
private: este nivel solo concierne las clases internas. Son clases definidas dentro de una clase y que solo son
utilizadas por esta.
Miembros :
public: los miembros (propiedades y métodos) son accesibles desde el exterior de la clase. Para proteger las
propiedades de modificaciones no autorizadas o equivocadas, se aconseja que nunca se declaren públicas.
protected: los miembros son accesibles por las clases hijas (ver la noción de herencia más adelante) pero
también con Java a las demás clases del mismo package.
<nada>: si no se específica la visibilidad, los atributos y métodos son accesibles únicamente para las clases
pertenecientes al mismo package.
private: es el nivel más elevado de restricción. Las propiedades y métodos solo son accesibles desde la propia
clase. En cuanto a las propiedades, pueden ser accedidas fuera de la clase pero únicamente mediante sus
mutadores y accesores declarados visibles.
Los miembros (atributos y métodos) de una clase pueden también calificarse con palabras claves especiales:
static: los miembros declarados static pertenecen a la clase y no a los objetos provenientes de la clase. No se
pueden heredar por las clases hijas.
final: los atributos declarados final no pueden ser modificados una vez inicializados. Deben también ser
inicializados en la construcción de los objetos. Los métodos declarados final no pueden redefinirse en las clases
hijas.
Otras palabras clave permiten caracterizar las clases y sus miembros en otros dominios diferentes al de la visibilidad
como la concurrencia o la optimización. Los lectores curiosos buscarán los usos de volatile, synchronized y
transient.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
// otros métodos
public void dormir(){
}
public void comer(Object comida){
}
public String maullar(){
String elMaullido = "miau";
return elMaullido;
}
El atributo derivado edad, discutido anteriormente, se define con el uso del método getEdad(). No se corresponde
con ninguna propiedad almacenada en el objeto, sino a un cálculo donde interviene otro atributo.
Toda aplicación debe disponer de una clase con un método determinado encargado de su ejecución. Se trata del
método main() que hemos visto en el capítulo de Toma de contacto de Eclipse. Su sintaxis y estructura son
siempre las mismas:
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Se trata de un método público y estático. Como hemos visto anteriormente, es por lo tanto accesible desde fuera
de la clase y pertenece a esta y no a sus instancias.
Ejemplo:
En la única línea que constituye el cuerpo del programa, System designa una clase Java cuya madre es Object.
Consultando la ayuda, es posible ver la jerarquía de las clases.
Estas dos clases se guardan en el package lang, él mismo almacenado en el package java.
La palabra out designa una propiedad estática de la clase System, siendo out de tipo PrintStream cuya jerarquía
con sus clases madres se ve a continuación.
Consultando la clase PrintStream, se constata que println es un método de esta. Este método se puede usar en
la clase System con la propiedad estática out. El método println muestra el valor literal que se encuentra
delimitado por las comillas dobles y realiza un retorno de línea.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Encapsulación
El hecho de agrupar en el seno de una misma clase propiedades y métodos y definir el nivel de accesibilidad
constituye la encapsulación, uno de los conceptos esenciales de la POO.
Una clase va a actuar sobre los datos privados que le son propios, con la garantía que nada interferirá con el
tratamiento. Un desarrollador sabrá muy rápidamente qué parte del código es responsable (o infractora), ya que
una sola clase está habilitada para modificar el dato en cuestión. Solo son visibles los miembros públicos, lo que
refuerza la seguridad de los datos y el mantenimiento de los programas. Además, la implementación de los métodos
es enmascarada. Finalmente, con el concepto de encapsulación, la clase solo presenta un acceso compuesto por los
miembros públicos. Se esconde lo que es privado a los ojos del resto del mundo.
Retomando el ejemplo anterior, para modificar el nombre del gato, debe obligatoriamente usar el método
setNombre(), lo que permite efectuar eventuales controles y tratamientos adicionales. Tomando esto como
principio, puede aplicarlo a otros casos como el débito de una cuenta que no se puede realizar sin el método
apropiado y habilitado. Los riesgos de error se ven así reducidos y la seguridad aumenta. Sobre todo, la modificación
del código que se ocupa del maullido de un gato o del débito de una cuenta no tiene ninguna incidencia en el
programa, realizando este uso de un método mediante su firma.
Ejemplo:
// método modificado
public String maullar(){
// un gato que no para de maullar
String elMaullido = "miau miauuuu miauuuu ...";
return elMaullido;
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Constructor
Para poder instanciar objetos de una clase, hace falta un constructor. Es un método especial, con el mismo nombre
que la clase y escrito sin valor de retorno, que permite crear objetos. Si se omite un constructor, Java los insertará
al realizar la compilación del programa creando un constructor básico del tipo NombreDeLaClase(), sin
parámetros (se trata del constructor por defecto). Es posible declarar varios constructores en una clase, como en
el siguiente ejemplo.
private Gato() {
super();
}
El primer constructor Gato() es privado. No puede utilizarse fuera de la clase Gato. Llama al constructor de la clase
madre con la ayuda de la instrucción super().
El segundo constructor Gato(long) es privado. Llama al primer constructor con la ayuda de la instrucción this().
El tercer constructor Gato(String, String, long) espera tres parámetros, lo que le permite crear un gato con un
nombre, un color y una fecha de nacimiento. Utiliza el segundo constructor privado, con la ayuda de la instrucción
this(laFechaDeNacimineto). Es público y puede por lo tanto ser utilizado en toda su aplicación y por programas
externos.
...
Gato elGato = new Gato("Minino", "pellirojo", 2);
...
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Herencia
Las costumbres del pensamiento humano utilizan habitualmente las nociones de jerarquía y de clasificación, como
por ejemplo para la zoología o para un árbol genealógico.
La POO intenta restituir esta manera de pensar gracias a la noción de herencia, con la que las clases «hijas»
recuperan las propiedades y comportamientos de su clase «madre», reservándose la posibilidad de poder
completarlas y/o modificarlas.
Retomando el ejemplo de Minino, un Mamífero es un concepto más general al de Felino que es un concepto más
general que Gato, del cual Minino es una instancia específica. En términos de programación orientada a objetos, es
posible crear una jerarquía de clases transponiendo esta manera de pensar. Existe por lo tanto un objeto Minino
construido desde una clase Gato que hereda de la clase Felino que hereda de la clase Mamífero.
De una manera más general, todos los felinos, cánidos y cetáceos heredan las características comunes de la clase
Mamífero: animal de sangre caliente, la hembra amamanta a sus crías, etc. Minino pertenece a una gran familia.
Va a heredar de todas las propiedades y métodos de sus clases ancestrales, dejando de lado los elementos
específicos de los cánidos y cetáceos. Para tener en cuenta esto, hay que revisar la definición de la clase Gato y
añadir las de las clases madres. Para hacerlo sencillo, aquí tiene un ejemplo limitado a las clases Felino y Gato.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
La palabra clave extends indica que la clase derivada (hija o subclase) Gato extiende de la clase base (madre o
super clase) Felina. La llamada del constructor de la clase madre se realiza con la palabra clave super().
Esto permite crear espacios en la aplicación para poder implementar características y componentes comunes a la
modelización. Cuando lo solicite la evolución de la aplicación, será posible crear una clase Tigre que heredará
también de la clase Felino y tendrá por lo tanto desde su creación todo los que caracterice a un felino: el código se
reutiliza, los comportamientos comunes son factorizados, y en términos globales la estructura de la aplicación
mejorada. Si se modifica el código de la clase Felino, todos los felinos de la aplicación se beneficiáran
inmediatamente de esta modificación indirectamente.
Resulta tentadora la sobre utilización en una aplicación, sobre todo para los desarrolladores que descubren esta
noción. Los maestros del pensamiento del Gang Of Four (Gamma, Helm, Johnson y Vlissides) recomiendan sin
embargo favorecer la composición de objetos (es decir combinar objetos simples para crear otros más complejos con la
ayuda de atributos) frente a la herencia de clase, ya que esta última técnica tiende a romper la encapsulación y crear
sistemas que son difíciles de modificar.
Aquí la clase Gato hereda los métodos getColor(), setColor() y getEspecie() de su clase madre sin necesidad de
reescribirlas.
El concepto de herencia está ligado a los comportamientos externos y no a una representación interna. Un pingüino
que no puede volar no debería heredar de una clase Pájaro donde se define el vuelo como un comportamiento.
A veces es útil indicar que una clase no puede ser instanciada directamente para crear objetos, cuando deseamos
crear una clase «sencilla» de base que será enriquecida por las clases hijas. En este caso, debe añadir la palabra
clave abstract. La clase Mamífero está definida a continuación como abstracta. No se podrá crear ninguna
instancia de esta clase, deberá extender la clase Mamífero para poder crear objetos.
Esto es coherente con la modelización. Aunque todos los gatos sean mamíferos, « un mamífero como tal » no
existe, y no debería tener la posibilidad de existir en la aplicación.
Para afinar el concepto de herencia, es posible precisar que algunas propiedades y métodos se pueden declarar
como protected.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
}
}
Ahora, solo las clases hijas de Mamifero pueden heredar de sus miembros y utilizarlos. Solo las clases
pertenecientes al mismo package pueden utilizar estos mismos miembros.
Solo las clases hijas de Felino pueden heredar estos miembros definidos como protected.
Para que funcione, esta clase debe encontrarse en el mismo package de las demás clases.
Todo esto resulta muy interesante para la modelización, pero este código no es necesariamente muy robusto. Si se
modifica la clase anterior ligeramente por:
¡Se ha obtenido un mamífero de sangre fría! Lo cual no se corresponde con lo que queríamos obtener...
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Con el fin de securizar más la modelización con respecto a este problema, podemos utilizar la palabra clave final.
Esto impedirá cualquier modificación del valor del atributo sangreCaliente, pudiendo utilizarlo en todo momento.
La palabra clave final puede también utilizarse con las clases. Suponiendo que la clase Gato no se puede derivar y
con el fin de formalizar este hecho, se añade la palabra clave final a la definición de la clase como sigue:
Se podrá como siempre usar la clase Gato como anteriormente, pero ahora es un punto final dentro de la jerarquía
de clases: no se puede formalizar el concepto de raza de Gato en la aplicación.
Se obtiene un ventaja más con el uso de la palabra clave final: sabiendo el compilador que no se puede cambiar este
valor o clase, el bytecode generado estará (un poco) más optimizado.
A diferencia de otros lenguajes de POO, la noción de herencia múltiple no existe en Java: una clase hija no puede
extender varias clases madres. Para paliar esta limitación, el lenguaje Java introduce la noción de interfaz.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Interfaces
Una interfaz es un medio para crear un contrato para las clases y por lo tanto los objetos, contrato que permitirá
definir los comportamientos obligatorios para todas las clases implementando esta interfaz.
El principal interés de una interfaz es factorizar comportamientos comunes para una utilización estándar. Si,
durante el ciclo de desarrollo de una aplicación se percata de que alguno de sus objetos puede tener una mejor
implementación (como un coche con mejor motor), y si usa una interfaz para comunicarse con este objeto, puede
modificar su código con más facilidad substituyendo su antigua clase con la nueva que integrará exactamente la
misma interfaz.
Volvamos al ejemplo del coche: cada modelo de coche es único. Se construye cada modelo siguiendo un esquema
diferente, con tecnologías diferentes. Sin embargo, la interfaz de cada modelo de coche es siempre más o menos la
misma: un volante, pedales para el freno y el acelerador, las palancas para los intermitentes... El volante siempre
está delante suyo, los pedales se encuentran en el mismo sitio sea cual sea el coche que conduzca, como las
palancas de intermitentes... ¡Felicidades! Conoce bien la interfaz para conducir un coche y no tiene que volver a
examinarse del permiso de conducir cada vez que se sienta en un coche diferente del suyo, como un coche inglés.
Por lo tanto ya ha aprendido a dominar una interfaz y no una implementación determinada, y eso en el mundo
real. Es posible transponer este dominio en una aplicación Java con la ayuda de interfaces que se declaran de la
siguiente manera:
interface Animal {
void comer(Object comida);
}
Una interfaz solo puede contener generalmente firmas de métodos públicos y abstractos, por lo tanto sin ninguna
implementación (sin llaves), y propiedades públicas, estáticas o finales.
Se ha subrayado anteriormente que la herencia múltiple no existe en Java. Gato no puede por lo tanto heredar
simultáneamente los comportamientos y características de varias clases.
Una interfaz puede extenderse (derivarse) con otras interfaces mediante la palabra clave extends, como con una
clase. La diferencia principal respecto a una clase reside en el hecho de que una interfaz puede ella misma heredar
de varias interfaces. Las interfaces permiten ente otras cosas estructurar el código de manera más segura. En
efecto, cuando una clase implementa una interfaz con la palabra clave implements, debe obligatoriamente
redefinir todos los métodos de estas. Esto puede parecer restrictivo pero la aplicación tiene así la certeza de que los
servicios o contratos esperados por la interfaz implementada serán respetados.
Aquí, cualquier clase que desee implementar la interfaz Animal deberá obligatoriamente crear un método público
void comer(Object comida), además de los métodos presentes en las interfaces SerVivo y Movible.
En el interior de Java, se utiliza a menudo el mecanismo de interfaces. Permite crear modularidad y facilidades de
substitución de código. ¡Y Java permite usar y crear esta modularidad y esta facilidad en su aplicación!
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Se aconseja evitar crear interfaces con demasiados métodos y por lo tanto contratos. Esto perjudica globalmente a la
modelización y al ciclo de vida de la aplicación.
Para terminar, Java 8 permite definir implementaciones por defecto en las interfaces, con la ayuda de la palabra
clave default.
interface Animal {
default void comer(Object comida) {
System.out.println(this + " come " + comida);
}
}
Si un método de una interfaz tiene una implementación por defecto, no es necesario implementar este método: se
utilizará la implementación por defecto.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Polimorfismo
El polimorfismo es una noción importante en POO, y significa etimológicamente «que puede presentarse en
diferentes formas».
El polimorfismo es la capacidad del código para elegir dinámicamente en la ejecución el método a ejecutar, e implica
que una misma llamada a un método pueda tener comportamientos diferentes, ya sea en función de los
parámetros de esta llamada, o del objeto con el que se efectúa la llamada.
Desde Java 1.5, también existe un polimorfismo llamado paramétrico, usando los genéricos, que se estudiarán en el
siguiente capítulo.
1. Por sobrecarga
El polimorfismo de sobrecarga corresponde a la capacidad de un objeto de elegir en tiempo de ejecución
(dinámicamente) el método que corresponde mejor a sus necesidades entre sus propios métodos o los de la clase
madre.
Otra particularidad de este polimorfismo es que todos los métodos afectados llevan el mismo nombre. En el seno
de una clase, se diferencian obligatoriamente y únicamente por el nombre de los parámetros o el tipo de estos
últimos.
Recordatorio: una firma de un método solo se puede implementar una única vez en una misma clase.
La notación List<Object> define un conjunto de datos de tipo Object sobre los cuales se puede iterar (recorrer
todos los elementos). Estas nociones serán desarrolladas en el próximo capítulo en las secciones Colecciones y
Genéricos.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
2. Por redefinición
Los métodos de la clase madre pueden redefinirse en las clases hijas o subclases con una operación de
subtipado. Se trata en realidad de especialización. En el sentido inverso, es una generalización que permite
factorizar los comportamientos comunes.
Desde un punto de vista práctico, los métodos de las clases hijas se reescriben para obtener el comportamiento
deseado. Al usarlo, se tiene la impresión de llamar el método de la clase madre, pero en realidad el código que se
ejecuta es el de la clase hija.
Un ejemplo clásico es el método toString() de la clase Object, presente en TODAS las clases de la aplicación.
Permite proveer una descripción en texto del objeto instanciado. Se puede redefinir este método en las clases de
la aplicación para dar más información contextual sobre los objetos.
Retomando el caso de los animales, todos los mamíferos se alimentan pero los gatos domésticos comen
croquetas, lo cual no ocurre necesariamente en el caso de los tigres.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
La línea @Override es una anotación, que se explicará en el próximo capítulo. Significa que el método anotado (el
situado justo debajo) debe redefinirse obligatoriamente y llevará a un error en caso contrario.
Ejemplo en un programa:
elGato.comer("croquetas") ;
elTigre.comer("croquetas") ;
Como croquetas
Un tigre no come esto
Para detener este mecanismo de redefinición, se puede utilizar la palabra clave final. En este caso, los métodos
no podrán redefinirse (reescribirse).
Ejemplo:
3. Por interfaz
El polimorfismo por interfaz permite tener métodos con el mismo nombre y tipos (hablamos de firmas
idénticas) en clases diferentes (sin ningún enlace de herencia entre ellas), cuya implementación puede también
ser diferente.
Para conservar un enlace semántico entre estos métodos, es importante crear una interfaz que incluya este
método, e implementar esta interfaz en las distintas clases. Esto permite llamar a este método a nivel de interfaz
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Ejemplo de código:
El código anterior crea objetos con las clases concretas PrimerOyente y SegundoOyente, y los almacena en
una variable de tipo Oyente. El tipo concreto del objeto desaparece por lo tanto en este momento. Sin embargo,
llamando al mismo método algoHaSucedido(), los comportamientos de ambos objetos difieren.
No es polimorfismo de redefinición, ya que las clases no tienen un jerarquía común. Se basa aquí el polimorfismo
en el contrato que deben respetar las clases que implementan la interfaz.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Principios SOLID
Para obtener una modelización y un desarrollo más fiables y robustos en programación orientada a objetos, es
posible utilizar cinco principios básicos agrupados bajo el acrónimo SOLID (en inglés). Estos principios vinculados
entre sí permiten arrancar un desarrollo sobre bases sanas que permitirán simplificar modificaciones posteriores.
1. Single Responsibility
El principio de responsabilidad única define que todas las clases de una aplicación deben responsabilizarse de
una de las funciones de la modelización, y que esta funcionalidad debe definirse en la clase. De esta forma, cada
clase solo tiene una única razón para modificarse, lo que permite guardar el control de su complejidad, mejorar la
legibilidad y posibilitar un mejor trabajo en equipo como ventajas inmediatas.
Un contra ejemplo de este principio es una clase que agrupa varias funcionalidades, como una clase Comando que
modeliza los datos, permite el acceso directo a la base de datos y gestiona ella misma la impresión. Esto crea una
clase compleja, cuyas distintas funcionalidades se encuentran enlazadas entre ellas, y que será sensible a
cualquier cambio, ya que no se puede garantizar que una modificación de la parte de impresión no impactará al
acceso a las bases de datos.
En vez de utilizar una herramienta multifunción cuyos elementos resultan mediocres en cuanto a su uso, es
preferible disponer de una caja con diferentes herramientas, cada una dedicada a un uso específico.
2. Open/Closed
El principio de abierto/cerrado enuncia que un programa debe ser abierto a extenderse pero cerrado a la
modificación. El objetivo de este principio es conseguir una modelización donde podrá añadir nuevos
comportamientos sin modificar la mecánica interna.
Un contra ejemplo de este principio es un módulo de generación de informes con distintos formatos: PDF,
imagen,… Suponiendo que un método de impresión tenga la forma:
if (formato == PDF) {
// generar un PDF
}
if (formato == JPEG) {
// generar una imagen JPEG
}
El día que los usuarios pidan un nuevo formato de impresión, tendrá que modificar este método para añadir una
nueva condición sobre la variable de formato (y es probable que tenga que añadir nuevas condiciones en otros
métodos).
Una de las posibilidades que respeta el principio abierto/cerrado es crear una clase que modelice el formato, que
contenga un método generar(). El día que deba crear un nuevo formato, basta con extender la clase, sin
modificar la mecánica interna de generación.
No es necesario pasar por cirugía el día en el que nos ponemos una nueva camisa.
3. Liskov Substitution
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
El principio de substitución de Liskov postula que todo código que manipule una clase madre debe poder usar
una de sus clases derivadas sin conocer su uso: la clase base define un contrato que toda subclase debe
obligatoriamente respetar. El principio recíproco es que las clases derivadas deben poder sustituirse por su clase
base.
Un ejemplo clásico de violación de este principio es un método que evalúe una condición sobre el tipo (la clase) de
un objeto para efectuar operaciones sobre él, como en el seudocódigo que sigue:
Una de las claves para comprender este principio es que la relación de herencia (A es una especie de B) está
ligada a un comportamiento externo y no a un comportamiento o atributos internos.
Por ejemplo, una primera aproximación para modelizar un cuadrado es expresar esta noción como un caso
particular de un rectángulo con un ancho y un alto iguales: crea entonces una clase Cuadrado, que hereda de
una clase Rectangulo. Se modifican los métodos mutadores para expresar la restricción adicional.
Si se crea una condición genérica de los rectángulos para verificar que los atributos ancho y alto se modifican
correctamente, ¡la condición no será verdad para la clase Cuadrado! Contrariamente a la aceptación popular, en
términos de modelización un cuadrado no se comporta como un rectángulo.
Si esto se parece a un pato, cloquea como un pato, pero necesita pilas para funciones, no es un pato.
4. Interface Segregation
El principio de segregación de interfaces enuncia que un programa no puede depender de los métodos que no
usa. En una modelización por contrato, este contrato debe poder separarse en pequeñas interfaces.
A la inversa, si una funcionalidad define muchos métodos en su contrato, como generar(), imprimir(),
enviarFax(),..., aporta demasiados conocimientos. ¿Realmente necesita un usuario de la función de impresión
conocer la función de fax? Además, cuando tenga que crear un nuevo sistema de impresión, ¿es necesario que
tenga que recrear el mecanismo de fax?
Es menos costoso reemplazar un destornillador que volver a comprar una herramienta multifunción de la que solo
utilizamos el destornillador.
5. Dependency Inversion
El principio de inversión de dependencias postula que un módulo de alto nivel no debe depender de la
implementación de un módulo de bajo nivel pero que los dos niveles deben estar ligados por una abstracción.
Además, una abstracción no debe depender de los detalles, pero los detalles deben depender de las abstracciones,
pudiendo materializarse la abstracción en una clase base o una interfaz.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
El día en el que el protocolo de datos del servidor cambie, deberá modificar el cliente con todos los riesgos de
incompatibilidad que acontezcan.
Un mejor diseño siguiendo el principio de inversión de dependencias consistiría en intercalar entre ambos una
noción de protocolo (mediante una interfaz o una clase abstracta), protocolo implementado en una clase distinta.
Este cambio de diseño permite minimizar el impacto derivado del cambio de protocolo, y permite potencialmente
al sistema generar varios protocolos a la vez.
Soldar una lámpara a un enchufe eléctrico no resulta generalmente una buena idea.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
El credo personal del autor en relación a este principio es: cree una vez, cópielo la segunda, factorice la tercera.
Aquí debe evitar la complejidad innecesaria del código, sobre todo en cuanto a optimizaciones que solo deberían
llevarse a cabo cuando una versión sencilla de la aplicación es operacional.
Una aplicación simple resulta más sencilla de comprender y mantener, pero no necesariamente más sencilla de
desarrollar. O citando a Leonardo da Vinci: «La simplicidad es la sofisticación suprema».
Siempre es tentador crear código que no se va a usar directamente porque «podríamos necesitarlo algún día».
Sin embargo, siempre que este código no esté implementado, no está sistemáticamente probado, su escritura ha
consumido tiempo que podría dedicarse a otras funcionalidades inmediatamente indispensables, y añade
complejidad al sistema.
¿Necesita ahora de una lógica ternaria (Sí, No, Tal vez) en su código?
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Genéricos
Java introdujo otro tipo de polimorfismo desde Java 1.5, llamado paramétrico, que permite tipar y restringir las
clases. Estas restricciones se expresan mediante un notación entre los símbolos < y >.
Retomando el ejemplo de los animales, y suponiendo que la aplicación concierne a un zoo, la modelización incluirá
corrales que agrupan diferentes animales. En estos corrales, se debe evitar a toda costa mezclar carnívoros con
herbívoros. Los genéricos permiten establecer este tipo de seguridad.
Ejemplo:
El ejemplo anterior define una noción de corral para cualquier tipo de animales, gracias a la notación entre los
símbolos < y >. Esta notación define un tipo particular, con el alias T, que se usará para todos los objetos que
extienden la clase Animal. Se puede entonces usar este alias en el interior de la clase en substitución del tipo.
La clase Corral puede después instanciarse con el operador diamante desde Java 1.7 como sigue:
Los genéricos ofrecen una protección a la compilación. Si una parte del código intenta enlazar un objeto que no
tiene el tipo adecuado, un error aparece.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Colecciones
Cuando se modeliza un programa con la ayuda de clases, es necesario crear abstracciones de datos. Sin embargo,
igual de importante es poder utilizar esos datos como conjunto.
Pero los vectores no son muy flexibles: no se pueden redimensionar (se dice que tienen un tamaño fijo), no tienen
métodos, etc. Para aprovechar todas estas características, una colección es una herramienta muy potente.
El concepto de colección es simple. Corresponde a las colecciones de sellos, conchas, etc. Los objetos de la misma
naturaleza se almacenan en el interior de una colección. Java creó desde su primera versión en el package
java.util clases e interfaces que dan una ayuda para modelizar estos conjuntos de datos, y pone a disposición del
programador varios tipos de colecciones que difieren en sus características: ordenadas o no, datos repetidos o no,
valores null aceptados o no, ordenación por defecto o no, etc. Todas tienen la capacidad de redimensionarse
automáticamente al añadir o suprimir objetos.
Una instancia de una colección es un objeto como toda instancia de una clase.
Java define varias interfaces que permiten especificar el contrato y el comportamiento de estos conjuntos de datos.
Contamos muchos, pero tradicionalmente existen dos que sirven de base:
La interfaz Map representa el contrato de una estructura donde cada dato es referenciado por una clave.
La interfaz Collection representa el contrato base para todas las demás estructuras de datos, entre las cuales
encontramos especializaciones derivadas:
La interfaz List representa el contrato funcional para todas las estructuras de datos ordenados (en las que se
puede encontrar un elemento determinado gracias a su orden de inserción, es decir su índice en la lista).
La interfaz Set representa el contrato de una estructura donde cada elemento es único en ella. Estos datos no
tienen por qué estar ordenados.
La interfaz Queue representa el contrato de una estructura de datos ordenados, pero en la que la adición se hace
al final de la estructura y la extracción al principio. Un poco como una fila de espera en un supermercado, en el
peaje de la autopista, etc.
Estos contratos base se especializan después con interfaces que los extienden, como SortedSet, Deque,
NavigableMap, etc.
Java proporciona varias implementacione base de estas interfaces, que se pueden elegir en función de las
necesidades y objetivos de la modelización.
Se implementa la interfaz List con las clases ArrayList, LinkedList, CopyOnWriteArrayList, Vector, Stack,...
Cada estructura de datos es genérica, lo que significa que es posible crear conjuntos de datos que tengan todos un
contrato común en referencia a su contenido.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
...
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Por lo tanto debe en todos los programas aprender a gestionar los datos incorrectos introducidos por un usuario
(introduce letras en vez de números para un precio), a gestionar los errores técnicos (la conexión a la base de datos
está desconectada o la red se ha caído), a crear estrategias de compensación (reintentar una connexión al menos
tres veces antes de abandonar, etc.).
Para ganar en claridad, Java proporciona un mecanismo de gestión de los errores por excepción. Se trata de
ejecutar una parte del código susceptible de generar excepciones (por lo tanto errores) englobándola y proporcionar
instrucciones alternativas si se produce alguna excepción. Una conexión se puede entonces reintentar, se pueden
indicar valores por defecto, ...
Una excepción es por lo tanto un evento que interrumpe la secuencia normal de la aplicación y que permite
ejecutar un código alternativo.
try {
// acceder a la red
// tratar los datos recuperados
} catch (IOException e) {
// el acceso a la red no se ha podido realizar
// el tratamiento de los datos no será efectuado
Uno de los intereses de la gestión de los errores por excepción es que si alguna sección de código produce un error,
el resto del código en el bloque try no se ejecutará. Otro de sus intereses es claramente separar el funcionamiento
normal del código y el tratamiento de los errores.
Se proponen las excepciones en muchos lenguajes de programación diferentes. Antes de su creación, los
desarrolladores se basaban en los códigos de retorno, de los cuales se tenían que vigilar todos sus valores posibles y
actuar en función de ellos. Esto provocaba la creación de pirámides de if ... else, para los que su lectura era... laboriosa.
Y si un desarrollador olvidaba un valor de retorno, aparecían agujeros en la gestión de errores.
Java define una jerarquía de objetos que describen los posibles errores que aparecen en un programa, de los cuales
la clase base es Throwable.
Se especializa esta clase en dos subclases: Error y Exception. Error se refiere a problemas muy raros, como una
clase que no puede ser leída, o menos raros como un desbordamiento de pila (en inglés: stack overflow), mientras
que Exception se refiere a problemas más típicos de la ejecución de un programa, como la falta de una clase, la
falta de red o una variable usada cuando no ha sido inicializada.
Las excepciones se subdividen a continuación en dos categorías: las excepciones verificadas (checked) y no
verificadas (unchecked). Estas últimas heredan todas de la clase RuntimeException.
La principal diferencia entre estos dos tipos de excepciones está ligada a su declaración:
Si un método es susceptible de lanzar una excepción verificada, solo existen dos maneras de tratarlas:
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
O declarando el método como susceptible de lanzar esta excepción con la palabra clave throws en su firma.
Traslada la responsabilidad del tratamiento de la excepción a los métodos situados más arriba en la pila de
llamada.
void llamada() {
try {
metodoConExcepcion();
} catch (MiExcepcion e) {
// ... gestionar la excepción
}
}
Es tentador tratar de inmediato una excepción. Muchas veces resulta más interesante dejar que lo haga el código que
invoca, sobre todo en el caso de la creación de una librería.
Si un método puede lanzar una excepción no verificada, no es obligatorio tratarla. Sin embargo puede aparecer,
como muchos desarrolladores verifican cada día. Ejemplos de este tipo de excepciones son las
NullPointerException, UnsupportedOperationException, ArithmeticException...
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Boxing/Unboxing
Dentro de Java, todo son objetos... o casi: existe lo que se llaman los tipos primitivos, como int, double, float, char,
boolean.
Cada uno de estos tipos primitivos tiene su equivalente en objeto: Integer, Double, Float, Character, Boolean.
Para facilitar la codificación, el lenguaje Java puede realizar una conversión automática del tipo primitivo a su
equivalente en objeto: se trata del mecanismo de boxing.
Para hacerlo explícitamente, utilice el método estático valueOf() de las clases Boolean, Integer, Double, Float o
Character.
La operación inversa, llamada unboxing, permite por su parte transformar objetos Java en su tipo primitivo.
System.out.println(entero);
System.out.println(decimal);
System.out.println(comaFlotante);
// resultado
true
1
2.0
3.0
Existe una trampa con esta facilidad del lenguaje Java: si se usa el mecanismo de unboxing con un valor no
inicializado (la célebre palabra clave null), se produce una excepción, que no será muy explícita, y que puede ser
dificil de localizar.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Enums
Un enum es un tipo especial de datos que proporciona un juego de constantes predefinidas
Es perfectamente posible crear un enum personalizado: se define como una clase o una interfaz en su propio
archivo, con la palabra clave enum.
Por ejemplo:
Puede también darle atributos, métodos y un constructor. La restricción principal es entonces que el constructor
debe ser privado o pertenecer a un package privado: no es posible crear un valor enum directamente, estos valores
solo pueden ser los declarados en el enum. Esta restricción prohibe también cualquier herencia de enum.
Esto permite limitar los valores posibles de una variable con un vocabulario controlado y poder usar esta variable en
un switchcase, por ejemplo.
default:
break;
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Java proporciona varios enums como por ejemplo DayOfWeek y Month del package java.time. Estos enums
permiten formalizar los conceptos Lunes, Martes, etc. y los de Enero, Febrero, etc. y evitar tener que realizar un
ejercicio inútil (quitar 1 al número del mes...).
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Antes de la aparición de Java 8, crear una fecha, es decir un instante preciso de una línea temporal, se hacía con la
ayuda de la clase java.util.Date. Por ejemplo:
El problema era ante todo conceptual: un objeto Date representa realmente un instante con una precisión de
milisegundos, podríamos decir un tiempo máquina, calculado por el número de milisegundos transcurridos desde el
epoch Java: el 1 de Enero de 1970 a la medianoche UTC (Coordinated Universal Time). Sin embargo, si utilizamos
el método toString() del objeto Date:
obtenemos una descripción de la fecha realizada para ser leída por humanos, sobre todo por la presencia del huso
horario (CEST significa Central European Summer Time, o sea la hora de verano en el huso horario de Madrid).
Estas dos nociones de un tiempo máquina y de un tiempo humano son conceptualmente diferentes, ya que un
tiempo humano utiliza siempre el concepto de huso horario y de horario de verano/invierno mientras que un
tiempo máquina es esencialmente un sello de tiempo (un timestamp en inglés). De la misma manera, un niño
nacido en Madrid el 1 de Julio de 2016 a las 15h55 y un niño nacido el mismo día a la misma hora y mismo minuto
en Katmandú no tienen exactamente la misma edad: tienen algunas horas de diferencia (3 horas y 45 minutos
para ser precisos).
La clase Date era tan errónea que casi todos sus métodos se han marcado como deprecados en la siguiente versión
de Java, con la introducción de una clase Calendar, que también sufría de problemas conceptuales.
Para obtener el año desde un objeto Date, hay que añadirle 1900, ¡y el mes 0 es el mes de Enero!
Java 8 introduce una revisión completa de la gestión del tiempo, y proporciona las clases del package java.time.
Sus principales clases son: Instant, LocalDate, LocalTime, LocalDateTime, ZoneId y ZonedDateTime. Las
clases Date y Calendar son también compatibles con estas nuevas clases.
Si crea un nuevo programa con Java 8, priorice siempre las clases del package java.time. La librería Joda
(http://www.joda.org/jodatime/) resulta de una ayuda preciosa para cualquier desarrollo con una versión menos reciente
de Java.
Instant representa una instante preciso en el tiempo. Esta clase no tiene ninguna noción de huso horario. Se
trata del equivalente más próximo a la clase java.util.Date.
La clase LocalDate representa un día preciso, sin indicación de hora y sin noción de huso horario: se trata de la
representación Java del día de un calendario de pared.
La clase LocalTime representa una hora precisa, sin noción de huso horario: se trata de la representación Java de
un reloj de pared.
La clase LocalDateTime es una combinación de las dos anteriores clases y representa por lo tanto un día y una
hora precisa sin noción de huso horario.
ZoneId representa un huso horario, con su eventual diferencia horaria de verano/invierno. ZoneDataTime
representa una transcripción de un instante en el tiempo en ese huso horario en particular.
Se ilustra su funcionamiento con un pequeño programa que permite mostrar sus diferencias.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Dentro de este método main(), escriba el siguiente código y ejecutelo con [Ctrl][F11]:
// resultado
Fecha: Mon Jun 01 14:09:38 CEST 2015
Instante: 2015‐06‐01T12:09:38.321Z
La fecha muestra una descripción del instante preciso cuando se ejecuta el método main() ¡en inglés! El instante
Java 8 que corresponde con el método toInstant() se muestra de forma normalizada con números, una T para
indicar la hora y la letra Z para describir el huso horario Zulu, utilizado por los militares y la aviación.
Ambos resultados tienen una diferencia horaria de dos horas, que corresponden a la diferencia de la hora de verano
en Madrid: un instante siempre se muestra en el huso horario militar.
Es posible con Java 8 conocer el instante preciso del inicio de un día en particular en el calendario, en distintas
zonas del mundo.
// resultado
Hoy:2015‐06‐01
Medianoche:2015‐06‐01T00:00
Medianoche en Madrid:2015‐06‐01T00:00+02:00[Europe/Paris]
Un instante en Madrid:2015‐05‐31T22:00:00Z
Medianoche en Katmandú:2015‐06‐01T00:00+05:45[Asia/Katmandu]
Un instante en Katmandú:2015‐05‐31T18:15:00Z
Para obtener un instante a partir de un objeto LocalDate, es necesario añadirle una hora: esto se hace aquí
gracias al método atStartOfDay(), que devuelve un objeto de tipo LocalDateTime. Esto se podría hacer también
con el método atTime(hora, minuto), u otro de los métodos atTime(...).
Esta forma de trabajar permite evaluar medianoche en Madrid y en Katmandú, estando estas dos medianoches
situadas de distinta manera en el tiempo.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
En el ejemplo, el 1 de Junio de 2015 a medianoche, que únicamente representa una fecha en un calendario con
una hora determinada de un reloj, corresponde en Madrid con el instante situado el 31 de Mayo de 2015 a las 22
horas UTC (o GMT, aunque sea una amalgama confundir UTC y GMT), y en Katmandú con el instante del 31 de
Mayo de 2015 a las 18 horas y 15 minutos UTC.
Los objetos del package java.time son inmutables: ¡una vez creados no pueden ser modificados! Es posible por el
contrario crear nuevos objetos a partir de estos con la ayuda de los distintos métodos, empezando generalmente por
«at» o «with».
Examinemos ahora cómo, a partir de un instante preciso y único en el tiempo, es posible obtener la hora de las
distintas partes del mundo.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
+ aBsAs.toInstant());
// resultado
Instante: 2015‐06‐01T19:42:14Z
Fecha local Aquí: 2015‐06‐01T21:42:14
Fecha en la zona Aquí: 2015‐06‐01T21:42:14+02:00[Europe/Paris]
Ahora Aquí: 2015‐06‐01T21:42:14+02:00[Europe/Paris]
Un instante Aquí: 2015‐06‐01T19:42:14Z
Este ejemplo muestra que el 1 de Junio de 2015 a las 19 horas y 42 minutos UTC, que es un instante único en el
tiempo, corresponde al 1 de Junio de 2015 a las 21 horas y 42 minutos en el huso horario de Madrid (se dice más
habitualmente de Madrid hora local), al 2 de Junio de 2015 a la 1 y 27 de la mañana en el huso horario de
Katmandú, y al 1 de Junio de 2015 a las 16 horas y 42 minutos en Buenos Aires.
Estas pocas líneas ilustran la diferencia entre los conceptos de tiempo máquina y tiempo humano: el tiempo
humano incluye además del tiempo una noción de espacio (el huso horario) y una noción de compensación (la
diferencia horaria de verano/invierno).
Si se incluye una gestión del tiempo en su aplicación, siempre intente hacerlo con los objetos de tipo Instant. Esta
clase es la piedra angular en base a la cual se articulan las demás clases del package java.time.
Además existen maneras simples y elegantes para calcular el número de minutos o de semanas entre dos
instantes dados, para añadir dos días al inicio del próximo mes,... El package java.time es una de las grandes
novedades de Java 8.
Por ejemplo, ¿cómo calcular la hora local de llegada de un avión volando de Madrid a Katmandú? Aquí tiene una
propuesta de cálculo:
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
// resultado
Salida de aquí: 1 de julio de 2015 19:42:00 CEST (Europe/Madrid)
Llegada hora local: 2 de julio de 2015 13:47:00 NPT (Asia/Katmandu)
Llegada hora de aquí: 2 de julio de 2015 10:02:00 CEST (Europe/Madrid)
Duración del vuelo: PT15H05M PT15H05M
Aquí tiene una manera para formatear una fecha al formato español usando este nuevo package:
La matriz es el formato de la fecha, dd significa las dos cifras del día, MM las dos cifras del mes, yyyy el año. La
sintaxis del formato es amplia, consulte la documentación de la clase DateTimeFormatter para una explicación
extensa.
El código obtiene un objeto que formateará la fecha en función de la matriz gracias al método estático
ofPattern(...) de la clase DateTimeFormatter.
El huso horario actual (como está configurado en el ordenador) se obtiene del método estático systemDefault()
de la clase ZoneId.
El método atZone() de la clase Instant convierte un instante en un objeto temporal de zona, es decir que se
convierte el tiempo según el huso horario en parámetro.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Se formatea después este objeto temporal de zona como cadena de carácteres gracias al método format.
También es posible realizar la operación inversa, que consiste en encontrar un instante partiendo de una cadena de
caracteres que describe un día determinado: una efeméride.
DateTimeFormatter formato =
DateTimeFormatter.ofPattern("dd/MM/yyyy");
Como se trata de un día, sin indicación de la hora, se utiliza el método estático parse de la clase LocalDate. El
método devuelve un objeto de tipo LocalDate.
Después se trata de añadir una hora a este objeto. Para ello, el método de instancia atStartOfDay() devuelve un
objeto temporal del día concreto a medianoche, o sea al primer segundo del inicio del día.
Se obtiene ahora un tiempo situado en un huso horario, que permite obtener su instante preciso.
Antes de Java 8, la clase SimpleDateFormat permitía convertir una fecha en cadena, pero su uso presentaba
problemas sobre todo de acceso concurrente (ver la sección Threads).
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 6/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Eventos
Toda aplicación que incluya interfaces gráficas debe ser capaz de gestionar eventos: se trata de un concepto
esencial de las interfaces hombremáquina (o HMI).
Un evento es una señal que se envía desde las capas base del sistema operativo o de Java para notificar al
programa de que algo notable acaba de suceder: el usuario ha hecho clic sobre un botón, teclea con el teclado, pasa
por encima de una determinada zona de la interfaz gráfica con el ratón,...
Desde el punto de vista del desarrollador, los componentes gráficos generarán eventos de varios tipos, y tendrá que
programar secuencias de código para reaccionar a la aparición de estos eventos.
Existen realmente muchos tipos de eventos. Aquí tiene, por ejemplo, una parte de los que están disponibles para
un simple componente JButton (un botón en el que se puede hacer clic).
Solo para el ratón, existen cinco eventos posibles de base, más dos para los desplazamientos del ratón, y uno para
la ruedecilla.
Para programar acciones en respuesta a estos eventos, debe añadir lo que se llama un escuchador de eventos, un
listener en terminología Java.
Como recordatorio, en el capítulo Toma de contacto de Eclipse, codificó lo siguiente para responder al clic del botón:
Se creó un escuchador de acción para el botón, es decir un código que se ejecuta cuando el usuario hace clic
encima con el ratón, o presiona la tecla [Espacio] o [Intro] cuando el botón tiene el foco (cuando está subrayado en
la interfaz gráfica).
En una aplicación gráfica, se selecciona un solo componente en un instante T. Es el principio de focalización y se dice
que el componente tiene el foco. Por ejemplo un cuadrado aparece alrededor de un botón que tiene el foco, y el
cursor parpadea en una zona de introducción de datos que a su vez tiene el foco.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Depués se añade este accionador, que hereda de la interfaz ActionListener, como escuchador sobre el botón con
la ayuda del método addActionListener(). Es posible desasignarlo de los escuchadores con el método
removeActionListener(). El mecanismo para añadir y suprimir escuchadores se basa en Java en los métodos
addXXXListener y removeXXXListener, donde XXX es el nombre del evento a escuchar.
El escuchador de una acción es una clase interna anónima, es decir una clase sin nombre definida dentro de otra
clase.
El código del método actionPerformed es capaz de usar variables locales definidas en el código que realiza la
llamada, con la condición de que estas variables estén anotadas con final o no sean modificadas.
Antes de Java 8, era necesario marcar estas variables como final. Java 8 suprime esta necesidad, pero sin embargo
sigue siendo necesario que no se modifiquen estas variables.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Lambdas
Las expresiones lambda son otra de las novedades de Java 8.
En una aplicación, particularmente en su parte gráfica, es muy habitual programar pequeñas clases llamadas
internas anónimas (ya que son tan pequeñas que no tienen nombre, y están codificadas dentro de una clase).
En realidad creó una clase anónima que hereda de la interfaz ActionListener y codificó la acción resultante del clic
en el botón dentro del método actionPerformed.
Esta interfaz ActionListener es muy sencilla: solo tiene un método que implementar. Es lo que llamamos una
interfaz funcional.
La escritura puede entonces simplificarse gracias a las expresiones lambda de la siguiente manera:
btnValidar.addActionListener(
(ActionEvent e) ‐> {
System.out.println("¡Acción!");
}
);
Las expresiones lambda permiten una simplificación de escritura y lectura de estas interfaces funcionales.
Como el método actionPerformed() solo tiene un parámetro, la escritura puede por lo tanto aligerarse omitiendo
el tipo del parámetro y sus paréntesis:
btnValidar.addActionListener(
e ‐> {
System.out.println("¡Acción!");
}
);
Y como el código de la acción solo contiene una única línea, es posible continuar con esta simplificación de la
sintaxis suprimiendo las llaves:
btnValidar.addActionListener(
e ‐> System.out.println("¡Acción!") // ¡sin punto y coma!
);
WindowBuilder no tiene (todavía) en cuenta las expresiones lambda al generar código de la interfaz gráfica.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Clases gráficas
Java proporciona varios componentes gráficos básicos, gracias a los packages java.awt y javax.swing. Los
componentes AWT son históricamente los primeros en aparecer y han sido enriquecidos más tarde con los
componentes Swing.
De una manera más general, utilice los componentes Swing. Disponen de más posibilidades de interacción y de
riqueza gráfica.
Explicamos estos componentes en su versión por defecto: tal vez la apariencia gráfica no es la más agradable, pero
Java proporciona un mecanismo de modificación gráfica con el sistema Look and Feel, que se explicará un poco más
adelante.
Los siguientes apartados realizan una pequeña visita guiada de los componentes más habituales: los que se
encuentran casi en cada aplicación.
Sepa primero que todo componente gráfico presentado aquí tiene un estado activado o no (en inglés: enabled). No
se puede interaccionar con un componente desactivado.
1. Botones
Los botones permiten al usuario realizar acciones o elegir entre opciones.
a. JButton
La clase JButton permite mostrar un botón muy sencillo con un texto y eventualmente un ícono.
Se trata de uno de los tipos más sencillos de componente gráfico de interacción con el usuario: hace clic en el
botón (es decir que lo selecciona), se ejecuta una acción, el botón vuelve a su estado inicial (no seleccionado).
b. JCheckBox
La clase JCheckBox permite al usuario realizar una elección binaria, es decir una elección entre dos opciones
con exclusión mutua: sí o no, verdadero o falso, caliente o frío,...
La casilla a marcar se queda en el estado en el que el usuario la ha dejado. Tiene un efecto de memoria, como
los botones radio.
c. JRadioButton
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
La clase JRadioButton permite al usuario elegir entre varias opciones (por lo tanto potencialmente más de dos)
con exclusión mutua: elegir un color entre rojo, verde o azul, elegir entre el lector de CD o la radio de Internet,
elegir entre una tarta, un pastel o una crema catalana,...
El nombre de estos botones proviene de las antiguas radios: cuando se apretaba un botón, los demás botones
saltaban. De la misma manera, en un grupo de JRadioButton, cuando se selecciona uno, los demás son
deseleccionados.
Un botón radio solo tiene interés si está asociado a otros botones radio, con la ayuda de un grupo formalizado
con la clase ButtonGroup.
d. JToggleButton
La clase JToggleButton es una combinación de las clases JButton y JCheckBox. Se utiliza con muy poca
frecuencia.
2. Introducción de texto
Estos componentes gráficos permiten al usuario introducir texto.
El texto puede introducirse en cualquier idioma y escritura, al utilizar Java Unicode para todas las cadenas de
caracteres. Es también posible modificar el sentido de lectura y escritura de estos componentes para adaptarse al
farsi (y los idiomas árabes en general), al japonés,...
a. JTextField
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
b. JPasswordField
La clase JPasswordField permite al usuario introducir texto ocultando su valor. Un uso típico de esta clase es
la introducción de una contraseña.
c. JFormattedTextField
La clase JFormattedTextField permite al usuario introducir texto y a la aplicación mostrar datos complejos de
manera textual.
Esta clase se encarga por ejemplo de convertir una cadena de caracteres en fecha y de convertir una fecha en
cadena de caracteres, o también de formatear números con tres decimales exactos.
Java no proporciona componentes gráficos para introducir y mostrar fechas por defecto. Otros desarrolladores
proponen librerías para disponer de su propio componente gráfico. Pruebe JCalendar (http://toedter.com/jcalendar/),
JDatePicker (http://jdatepicker.org/), JXDatePicker (https://swingx.java.net/), ¡o cualquier otra librería que le parezca
atractiva!
d. JTextArea
La clase JTextArea permite introducir varias líneas de texto bruto (sin decoraciones, ni estilos de caracteres: no
hay negrita, ni itálica por ejemplo).
e. JEditorPane y JTextPane
La clase JEditorPane permite mostrar texto con estilos: es decir caracteres en negrita, subrayados en amarillo,
etc.
¡Esta clase permite hasta mostrar páginas web con formato HTML, o documentos con formato RTF (Rich Text
Format)! También es posible crear sus propios formatos con la ayuda de un objeto EditorKit.
JTextPane es una especialización de la clase JEditorPane que permite crear y mostrar sus propios
documentos con estilo.
Estas dos clases son relativamente complejas de usar, y por lo tanto no serán explicadas con más detalles en
este libro.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
3. Introducción de números
En vez de utilizar campos de texto simple para introducir números enteros, Java proporciona dos componentes
gráficos dedicados.
a. JSpinner
La clase JSpinner permite introducir números enteros, ya sea por introducción directa mediante el teclado en
un campo de texto, o manipulando pequeñas flechas situadas junto al campo de texto para disminuir o
aumentar el valor introducido.
b. JSlider
La clase JSlider provee un cursor gráfico que permite introducir un número. Este componente gráfico no posee
una zona para mostrar el valor introducido.
Sin embargo es capaz de mostrar los valores mínimo y máximo posibles, así como los valores intermedios (los
ticks).
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
4. Visualización de información
Es importante en una aplicación gráfica poder mostrar información no editable por el usuario, por ejemplo el total
de un pedido, un pequeño texto junto a un campo de texto que describa lo que debe introducir o un porcentaje.
a. JLabel
Es posible elegir la posición relativa del texto y de la imagen, así como el color del texto.
Un JLabel también puede utilizarse para mostrar una imagen sin texto asociado.
Alineando un JLabel junto a un JTextField y modificando la fuente de caracteres a negrita, se puede crear una
entrada de formulario como la que sigue:
b. JProgressBar
La clase JProgressBar permite mostrar un porcentaje en forma gráfica, con una barra que se llena en función
del porcentaje.
Esta barra puede mostrar el porcentaje, con orientación horizontal o vertical. Esta barra también puede estar
indeterminada: aparece entonces una animación.
Este componente resulta muy útil para indicar al usuario el avance de una operación potencialmente larga.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
c. JSeparator
La clase JSeparator permite trazar líneas finas horizontales o verticales en la aplicación, para separar
visualmente los diferentes componentes gráficos.
Si se sitúa un separador entre un botón de Buscar y otro de Eliminar, se crea una división lógica y gráfica
entre las dos funcionalidades: el usuario será un poco menos propenso a confundirlos y a hacer clic en el botón
erróneo.
Un separador puede ser vertical u horizontal. Se utiliza con mayor frecuencia en los menús o en las barras de
herramientas en su versión horizontal.
Los componentes que se van a presentar en esta sección son más complejos en su codificación: necesitan objetos
adicionales de los cuales uno de ellos proveerá datos que mostrar (se le llama modelo) y otro el aspecto gráfico
(se le llama renderer).
Estos componentes están basados en una variante de la arquitectura MVC (ModeloVistaControlador), que se
explicará en el capítulo Modelo MVC.
a. JComboBox
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 6/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
La clase JComboBox permite mostrar una lista desplegable de posibles opciones. El usuario puede entonces
modificar su elección, que se mostrará en consecuencia.
Es posible hacer esta lista desplegable editable, lo que permite entonces al usuario poder introducir un dato
libremente, que no está restringido a las opciones del combo box, lo que permite entonces orientar y facilitar la
introducción de datos.
b. JList
Al igual que la clase JComboBox, la clase JList permite mostrar una lista de opciones o información y poder
elegir dentro de los valores de esta lista.
Sin embargo, un JList permite realizar selecciones múltiples, lo que resulta imposible con un JComboBox.
Además, un JComboBox necesita dos clics para realizar la selección: un clic para desplegar la lista, un clic para
seleccionar. Un JList permite realizar esta selección con un solo clic.
6. Datos estructurados
Muchas veces es importante en una aplicación mostrar datos estructurados con sus detalles.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 7/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Tome por ejemplo el explorador de archivos de su sistema operativo (favorito o no). Se ven clásicamente dos tipos
de datos: la lista de carpetas y de sus subcarpetas que son datos de tipo árbol y la lista de archivos de una
carpeta con su nombre, su tipo, su fecha de modificación... que son datos tabulados (se le llama tabla).
Java proporciona componentes gráficos que permiten visualizar e interactuar con estos dos grandes tipos de datos
estructurados.
De este modo, resulta perfectamente viable construir su propio explorador de archivos multiplataforma, como
ejercicio.
a. JTree
Cada elemento del árbol se llama nodo (node en Java), y cada elemento terminal (que no tenga subelementos)
se llama hoja (leaf en Java).
b. JTable
La clase JTable permite mostrar datos tabulados, en líneas y en columnas: cada línea representa un dato, cada
columna representa un detalle de este dato.
Se puede especificar un encabezado de la tabla, que proveerá los nombres de las columnas por ejemplo.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 8/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
7. Contenedores
Casi ha terminado el recorrido por los componentes elementales. Ahora es tiempo de interesarse en el
emplazamiento de estos componentes.
En efecto, una aplicación nunca se compone de un único elemento gráfico, sino más bien de una multitud de
componentes. Estos componentes deben organizarse para proporcionar un uso intuitivo de la aplicación.
Para ello, puede utilizar paneles, que permiten agrupar gráficamente componentes.
a. JPanel
Un JPanel es uno de los contenedores más sencillo de Java. ¡Es tan sencillo que generalmente es invisible al
usuario!
Sin embargo es posible proveer un borde o un color de fondo a este panel, para ponerlo de relieve.
Un panel va por lo tanto a acoger componentes gráficos. Para saber dónde mostrarlos, este panel necesita de un
sistema que permita especificar la disposición entre los distintos componentes. Se llama a esta disposición
layout, y un JPanel necesita de un gestor de disposición, el LayoutManager.
La primera ventana que se creó usaba un JPanel con un layout absoluto, es decir que se disponían los
componentes con una precisión de pixel. El único problema con esta manera de trabajar aparece si se
redimensiona la ventana: los componentes se quedan en su sitio y no se adaptan al espacio disponible.
Se presentarán los diferentes layouts disponibles un poco más adelante en este capítulo.
b. JScrollPane
Puede ocurrir que un componente gráfico no tenga el espacio necesario para ser mostrado en su totalidad
dentro del espacio que le ha sido asignado por el LayoutManager.
Entonces es necesario dar al usuario un medio gráfico para poder recorrer el contenido y acceder a la totalidad
del componente.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 9/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
La clase JScrollPane permite realizar este recorrido. Puede ser horizontal, vertical o de ambos tipos a la vez, lo
que representa el caso más frecuente.
Gráficamente, aparecen dos barras situadas a la derecha y debajo del componente, con ascensores que
permiten realizar este recorrido.
c. JSplitPane
La clase JSplitPane permite mostrar dos componentes gráficos uno al lado del otro.
Estos dos componentes pueden presentarse horizontal o verticalmente. Una barra de separación permite variar
sus respectivas dimensiones.
Observe las dos pequeñas flechas de la barra de separación. Es posible configurar botones que permitan ocultar y
mostrar uno de los componentes con un solo clic de ratón.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 10/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
d. JTabbedPane
La clase JTabbedPane permite mostrar varios componentes separados, cada uno en una pestaña dedicada.
Cuando el usuario hace clic en una pestaña, se muestra el componente asociado, los demás componentes se
ocultan.
e. JToolbar
La clase JToolbar permite mostrar componentes (generalmente botones) en una barra de herramientas para
permitir un acceso rápido a acciones habituales, por ejemplo.
Se puede reposicionar esta barra de herramientas en la aplicación o desanclarla en una pequeña ventana
adjunta.
8. Ventanas
Todos estos componentes gráficos deben obligatoriamente insertarse en el sistema de ventanas del sistema
operativo. Una ventana está compuesta por un borde, un título, un icono y pequeños botones para minimizar,
maximizar y cerrar. Este borde y la fuente de los caracteres del título (hablamos de la decoración de la ventana)
son generalmente definidos por el sistema operativo: es sin embargo posible modificarlos desde Java.
Cada ventana posee también un tamaño y una posición en pantalla. Estos parámetros pueden modificarse desde
Java. Es posible también impedir el redimensionamiento de una ventana, aunque no sea generalmente la mejor
idea.
a. JFrame
Un JFrame es una ventana de alto nivel, que constituye habitualmente el punto de entrada de una aplicación
clásica.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 11/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Es posible especificar la acción correspondiente al botón cerrar (arriba a la derecha del borde en Windows): no
hacer nada, minimizar la ventana, finalizar la aplicación o liberar los recursos gráficos del JFrame (se elimina
entonces la ventana de la pantalla).
¡La liberación de los recursos gráficos de un JFrame no significa necesariamente que la aplicación va a finalizar!
b. JDialog
Un cuadro de diálogo, o más comúnmente diálogo, es una ventana independiente, ligada generalmente a una
ventana principal. La mayoría de los cuadros de diálogo consisten en la presentación de mensajes de error, de
información o de alerta, pero también pueden mostrar imágenes o componentes gráficos.
Un cuadro de diálogo puede declararse modal: en este caso, se suspende la ejecución del código del thread
gráfico hasta que se cierre el cuadro de diálogo.
dialog.setModalityType(ModalityType.APPLICATION_MODAL);
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 12/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
c. JOptionPane
La clase JOptionPane es un medio práctico para crear cuadros de diálogo estándar y simples.
JOptionPane.showMessageDialog(null,
"Mensaje destinado al usuario");
JOptionPane.showMessageDialog(null,
"Alerta destinada al usuario",
"¡Cuidado!",
JOptionPane.WARNING_MESSAGE);
JOptionPane.showMessageDialog(null,
"Ha ocurrido un error",
"¡¡Error!!",
JOptionPane.ERROR_MESSAGE);
JOptionPane.showMessageDialog(null,
"¿Puede contestar?",
"¿Pregunta?",
JOptionPane.QUESTION_MESSAGE);
JOptionPane.showMessageDialog(null,
"Un diálogo muy estándar",
"Estándar",
JOptionPane.PLAIN_MESSAGE);
JOptionPane permite también crear cuadros de diálogo de confirmación con un código de retorno que permite
conocer la decisión del usuario.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 13/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
}
if( confirmacion == JOptionPane.NO_OPTION) {
}
confirmacion = JOptionPane.showConfirmDialog(null,
"Confirme la acción",
"Confirmación requerida",
JOptionPane.YES_NO_CANCEL_OPTION);
d. JFileChooser
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 14/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
e. JColorChooser
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 15/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
9. Menús
Los menús son elementos omnipresentes en una aplicación moderna y permiten acceder a diferentes partes de la
aplicación, ejecutar diversas acciones...
En una aplicación, se presenta la barra de menús en la zona superior de la ventana principal. Observe en su
ordenador, por ejemplo en Eclipse. Puede ver las palabras File, Edit, Source... Project, Run, Window, Help. Estas
palabras corresponden a menús enlazados a la barra de menús principal.
Este menú consiste en ítems de menú, como Welcome, Help Contents, Install New Software...
Si hace clic en alguno de estos ítems provoca una acción, lo más habitual la apertura de una ventana o un cuadro
de diálogo que permite realizar acciones, ajustar parámetros, etc.
a. JMenuBar
La clase JMenuBar es el componente gráfico correspondiente a la barra de menús. Si esta barra está vacía, no
existe ninguna representación gráfica.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 16/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
b. JMenu
La clase JMenu es el componente gráfico del menú y del botón que permite abrir este menú.
Se pueblan los menús con submenús (que son JMenu) e ítems de menú, que ejecutarán la acción asociada.
c. JMenuItem
La clase JMenuItem corresponde a un elemento final en el menú. Si hacemos clic en este ítem, se ejecuta una
acción, como la visualización de otra ventana, la apertura de un navegador web, el almacenamiento de un
archivo...
En el ejemplo presentado arriba, se ha creado un ítem para el menú, que se llama una «acción».
Si examina atentamente podrá ver que la letra a está subrayada. Se trata de un elemento mnemotécnico que
permite seleccionar rápidamente el ítem del menú asociado.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 17/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Esta combinación abre el menú de primer nivel asociado con la F, es decir File. La asociación es visible ya que la
letra F está subrayada en la palabra File presente en el menú.
Presione la tecla P.
Volviendo al anterior ejemplo, otro elemento característico es la presencia del texto «MayúsA». Se trata de un
acelerador.
Un acelerador es una combinación de teclas que permite efectuar rápidamente una acción.
Por ejemplo [Ctrl] S sirve clásicamente para guardar algo en prácticamente todas las aplicaciones. [Ctrl] C sirve
para copiar, [Ctrl] V para pegar, ...
Las combinaciones [Ctrl] +... no son habituales para los usuarios de Mac. Java permite tener en cuenta estas
especificidades.
d. JPopupMenu
Existe otro tipo de menú que puede encontrar en casi todas las aplicaciones. Se trata del menú contextual, que
obtenemos haciendo clic con el botón derecho. También se le llama menú popup.
La clase JPopupMenu permite crear un menú contextual. Basta después con añadirle JMenu y JMenuItem.
El ejemplo anterior ilustra el hecho de que es posible insertar casillas de verificación y botones radio en los menús
con la ayuda de las clases JCheckBoxMenuItem y JRadioButtonMenuItem.
10. Layouts
Los layouts permiten organizar los componentes gráficos en el interior de un JPanel. Aquí tiene los más habituales
provistos por Java.
a. FlowLayout
La clase FlowLayout permite disponer los componentes en una línea dimensionada según el tamaño preferido.
Si el panel es demasiado pequeño para mostrarlos en una única línea, este layout utiliza varias líneas.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 18/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
b. BoxLayout
El BoxLayout sitúa los componentes en una única línea o una única columna. Los componentes están
dimensionados según su tamaño preferido.
c. BorderLayout
El BorderLayout solo permite situar cinco componentes a la vez: uno en el centro, los demás en los
alrededores.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 19/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
d. GridLayout
El GridLayout sitúa los componentes en una rejilla. Se organizan, por lo tanto, en filas y en columnas. Todas
las filas tienen la misma altura, todas las columnas tienen la misma altura y los componentes ocupan todo el
espacio disponible en su celda.
e. GridBagLayout
El GridBagLayout es uno de los layouts más sofisticados. Los componentes se sitúan en filas y columnas, pero
algunos pueden ocupar varias filas o columnas. Pueden ocupar todo el espacio disponible o redimensionarse con
su tamaño preferido.
Cada componente puede además tener un margen (un espacio vacío) a su alrededor.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 20/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Desde este punto de vista, se divide cada tipo de componente gráfico en dos categorías: los J (JButton, JList,...) y
los UI (ButtonUI, ListUI,...). Los UI son objetos a los que los J delegan las operaciones de apariencia gráfica.
Java propone varios Look and Feel básicos: en primer lugar el Look and Feel del sistema que corresponde a la
apariencia gráfica del sistema operativo (con algunas variantes), después el Look and Feel multiplataforma
(comúnmente llamado Metal) que da un aspecto uniforme en Windows, Apple o Linux. Existe también el Look
and Feel Synth que permite a los desarrolladores crear su propia apariencia mediante archivos de configuración
XML o Nimbus, que se introdujeron con la aparición de la versión Java 6.
Para especificar un Look and Feel determinado, es posible utilizar alguna de las siguientes líneas de código:
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 21/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Estas líneas deben utilizarse antes de que se muestre la primera ventana gráfica.
SwingUtilities.updateComponentTreeUI(ventanaOComponente);
JFrame.setDefaultLookAndFeelDecorated(true);
Menos mal que los Look and Feel no se limitan a los de Java. Como puede crear el suyo propio, varios
desarrolladores han seguido este camino y proponen gratuitamente el suyo. ¡Incluso algunos los venden!
UIManager.setLookAndFeel(
"de.javasoft.plaf.synthetica.SyntheticaStandardLookAndFeel");
UIManager.setLookAndFeel(
new com.jgoodies.looks.plastic.Plastic3DLookAndFeel());
UIManager.setLookAndFeel(
new com.jgoodies.looks.plastic.PlasticLookAndFeel());
Existen multitud de Look and Feel. Elegir uno es materia de gustos, colores y compatibilidad gráfica con el sistema
operativo.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 22/22
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Threads
Se codifica un programa para que las instrucciones se ejecuten secuencialmente.
En este código, la primera línea aparecerá siempre antes que la segunda. La tercera línea se ejecutará siempre la
última.
Esto lo garantiza Java ya que ejecuta estas instrucciones en un hilo de ejecución, un Thread.
Coloque un punto de interrupción (un breakpoint) haciendo doble clic en el margen izquierdo del editor en la
segunda línea del método main(). Aparecerá un punto azul. Si no, sitúe el cursor sobre esta línea y utilice la
combinación de teclas [Ctrl][Shift] B.
Ejecute el programa en modo depurador presionando la tecla [F11]. Se cambia a la perspectiva de depurador.
Se muestra el hilo de ejecución principal de este programa en la vista Debug. Este hilo, o thread, tiene un nombre:
main.
Dentro de este hilo, las instrucciones se ejecutan secuencialmente, una después de las otras.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Sin embargo, generalmente no es lo que un usuario espera de una aplicación: muchas veces quiere poder
descargar archivos al mismo tiempo que imprime un documento mientras escucha su canción favorita...
En Java, es posible crear tales aplicaciones: cada parte de la aplicación tendrá un thread dedicado, fuera del hilo de
ejecución principal, que ejecutará sus propias instrucciones secuencialmente. Esto dará al usuario la posibilidad de
realizar tareas en paralelo.
Existen varias maneras de ejecutar hilos paralelos. Implica en la mayor parte de los casos encapsular el código a
ejecutar dentro de un objeto que implemente la interfaz Runnable.
@Override
public void run() {
System.out.println("línea independiente");
}
};
Thread hilo = new Thread(run, "paralelo");
hilo.start();
Para que el código funcione correctamente, es necesario asegurarse de una buena sincronización de estos hilos de
ejecución: la impresión solo se ejecuta cuando el archivo se ha descargado…
Por ejemplo, el código anterior que ilustra el concepto de Thread paralelo tiene pocas posibilidades de ejecutarse si
lo añade en un método main de la clase Secuencial: ¡muchas veces se interrumpirá el programa antes de que se
ejecute el thread secundario!
Para conseguir un comportamiento correcto de la ejecución completa de los dos hilos de ejecución, deberá esperar a
que el thread paralelo se termine antes de que termine el propio programa: esto se consigue con la llamada a la
función join().
@Override
public void run() {
System.out.println("línea independiente ");
}
};
Thread hilo = new Thread(run, "paralelo");
hilo.start();
try {
hilo.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
}
}
Crear demasiados threads puede ralentizar la ejecución global del programa, dando la impresión (¡y no solo la
impresión!) de lentitud.
Una versión más sofisticada consistiría en usar un ExecutorService del package java.util.concurrent.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
...
Los threads tienen la capacidad de acceder al espacio de memoria de la aplicación. Por lo tanto es posible compartir
variables, a diferencia de los procesos donde cada uno posee su propio espacio de memoria.
Si se ejecutan varios threads y estos manipulan los mismos objetos, aparecen problemas de sincronización: un
thread puede modificar el valor de un dato entre dos lecturas de este dato por otro thread, lo que conlleva
comportamientos ridículos y aleatorios.
Se trata de uno de los peores bugs de una aplicación: el bug aparece... de vez en cuando.
Las problemáticas de sincronización y de concurrencia son aspectos avanzados de Java que no se tratarán en este
libro.
Cuando se ejecuta una aplicación gráfica, la máquina virtual de Java crea un thread específico llamado Event
Dispatch Thread (abreviado como EDT), cuya vocación es ejecutar todas las instrucciones ligadas a la visualización
gráfica de una aplicación, como los eventos de ratón, la visualización de un botón...
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
La ventana gráfica no debería mostrarse nunca desde un thread no gráfico. El método estático invokeLater de la
clase EventQueue permite ejecutar un código arbitrario en el EDT.
¿Ha utilizado ya una aplicación que parece estar colgada cuando hace clic en algún botón? ¿La ventana no se
refresca si se minimiza o se muestra un color gris si se posiciona otra ventana por encima y de repente todo vuelve
a la normalidad?
Esto es debido a que los desarrolladores de la aplicación crearon un código largo en cuanto a ejecución dentro del
hilo del EDT. Mientras este código no se termina, las demás operaciones gráficas están puestas en espera.
Para programar una aplicación gráfica que produzca una impresión de rapidez, ¡es esencial no ejecutar jamás un
código largo en la EDT!
Para simplificar el trabajo de los desarrolladores, Java proporciona una clase llamada SwingWorker, que permite
crear un código de este estilo como tareas en segundo plano y, cuando el código finaliza, ejecutar otro código en el
EDT.
Resulta habitual, por ejemplo, cuando un usuario hace clic sobre un botón de impresión, deshabilitarlo (para evitar
42 impresiones de la misma página, por ejemplo), ejecutar la impresión en segundo plano y reactivar el botón
cuando la impresión ha finalizado.
@Override
protected List<Object> doInBackground()
throws Exception {
// en el thread de segundo plano
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
@Override
protected void done() {
// la tarea en segundo plano ha finalizado,
// de vuelta en el thread gráfico
try {
// recuperar la lista de páginas impresas
List<Object> list = get();
// mostrar algo de esta lista
} catch (InterruptedException e) {
// interrupción : hay que gestionar
e.printStackTrace();
} catch (ExecutionException e) {
// error durante la ejecución
// hay que gestionar
Throwable causa = e.getCause();
e.printStackTrace();
}
// reactivar el botón
boton.setEnabled(true);
}
@Override
protected void process(List<Integer> chunks) {
// se recuperan los valores que provienen de la
// llamada al método publish()
// en el thread gráfico
En resumen:
¡Todas las operaciones sobre los componentes gráficos deben hacerse en el EDT!
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Anotaciones
Las anotaciones son metadatos que se pueden añadir directamente en el código de una aplicación Java, en casi
todos los niveles: en los packages, las clases, los métodos, los atributos, los parámetros y las variables locales.
@Override es una anotación de ayuda al compilador para indicar que el método anotado sobrecarga un método de
la súper clase o de una interfaz. Si el desarrollador comete un error de tipografía en el nombre de la clase, el
compilador señalará este error.
Para anotar un código, debe escribir la anotación inmediatamente antes de la sección de código afectada.
Por ejemplo, si desea anotar un método como @Deprecated (aparecerá entonces tachado en todo código que lo
utilice), haga lo siguiente:
@Deprecated
public void metodoNoMuyAcertado() {
...
}
Las anotaciones son útiles en dos fases distintas del programa: en su compilación y en su ejecución.
En su compilación, el compilador puede emitir avisos o directamente parar la compilación. Es posible también para
herramientas externas usar las anotaciones para generar automáticamente otras clases. Se trata de una técnica
utilizada en muchas ocasiones en ciertos frameworks, por ejemplo RESTX (http://restx.io ) o AndroidAnnotations
(http://androidannotations.org/).
Cuando se utiliza, librerías externas pueden «recuperar» las clases anotadas para permitir comportamientos no
previstos por Java. Es el caso por ejemplo de JPA (Java Persistence API) que permite crear objetos del dominio
almacenados en base de datos, o de JUnit que permite crear métodos para probar algunas partes de la aplicación.
Las anotaciones pueden parametrizarse con objetos como cadenas de caracteres o mediante otras anotaciones,
como se verá en el capítulo Entidades.
Puede escribir sus propias anotaciones así como utilizar herramientas de generación de clases adicionales. Este
aspecto no se detallará en este libro.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Otras nociones
¡El recorrido de las clases disponibles en Java no ha acabado, lejos de eso!
java.net
java.io
java.nio
java.util.concurrent
java.util.logging
java.util.regex
java.util.zip
javax.accessibility
javax.print
javax.imageio
Java proporciona a los desarrolladores un entorno rico y muy potente que permite hacer muchas cosas. Además,
existen muchas librerías (libres o de pago) disponibles para completar y enriquecer aún más las posibilidades de
programación.
El proyecto que sirve de hilo conductor a lo largo de este libro no utiliza las clases de estos packages. Por lo tanto,
no se profundizará en estas nociones.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Haga clic con el botón derecho en el nombre del proyecto y elija la opción Properties.
Para acelerar la búsqueda de la opción deseada, y siempre que sepa su nombre, una caja de texto le permite filtrar
las propiedades. Se encuentra arriba a la izquierda del cuadro de diálogo de propiedades, con un pequeño mensaje
type filter text.
Para cambiar el compilador, seleccione Java Compiler, marque la opción Enable project specific settings,
desmarque la opción Use compliance from execution environment, y elija la versión deseada en la lista
desplegable.
Si por ejemplo tiene la costumbre de prefijar el acceso a las propiedades de una clase con la palabra clave
this, marque la opción Qualify all generated field accesses with ’this’.
Para realizar acciones automáticas cada vez que guarde un archivo, acceda a la opción Java Editor Save
Actions, y marque las opciones que le interesen.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Cada vez que se guarde el código con esta configuración, se formateará y se añadirán las anotaciones habituales si
no están presentes.
También es posible hacer clic en el enlace Formatter presente en la página de acciones al salvaguardar para
especificar cómo se presentará el código fuente. Una vez que esté satisfecho con el formateo, es posible exportarlo
para compartirlo entre los miembros de un equipo.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Tests unitarios
En todo desarrollo de software, una etapa que no debe descuidar nunca es la fase de pruebas: ningún desarrollador,
hasta los más expertos, está exento de incurrir en errores en su desarrollo.
¿Qué diferencia a estos desarrolladores expertos de los demás? Han construido su confianza alrededor de su trabajo
probando una y otra vez su código.
No se trata de revisar manualmente todos los test de una aplicación. En una aplicación de cierta entidad, esto es
simplemente imposible.
Es mucho mejor confiar estas pruebas a un sistema automático que las realizará todos los días.
Se ha creado toda una cultura alrededor de esta manera de desarrollar que consiste en situar los test en el centro
de la actividad de creación de software, o dicho de otro modo el desarrollo está gobernado por los test (TestDriven
Development o TDD en inglés).
Este libro no es una introducción a esta cultura, pero algunos principios básicos pueden desde ahora utilizarse: por
ejemplo el Red/Green/Refactor o en español Rojo, Verde, Rehacer.
La idea es empezar primero por crear los test, que no se pasarán, para trabajar en la implementación y la
codificación hasta que estos test se pasen sin errores. Una vez conseguido, debe después seguir codificando el
producto hasta que tenga una forma correcta.
Eclipse propone herramientas para crear los test de bajo nivel, llamados unitarios.
Volvamos al proyecto MiPrimerPrograma haciendo clic con el botón derecho encima del explorador de
packages y elija la opción New Source Folder.
Se guardarán las clases de test en esta carpeta. Crear esta carpeta permite separar claramente entre las
clases de los test unitarios y las clases que se utilizarán para el desarrollo de la aplicación.
Vuelva a hacer clic con el botón derecho en el explorador de packages y elija la opción New JUnit Test
Case.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Esta clase prueba la clase MiPrimerPrograma. Está guardada en la carpeta src/main/java, y pertenece al
mismo package que la clase sobre la que se realiza la prueba, es decir tomaDeContacto.primero.
Una buena práctica es terminar el nombre de las clases de test con Test. Como por ejemplo MiPrimerProgramaTest.
Se abre un cuadro de diálogo adicional que permite elegir los métodos a probar.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Eclipse propone después añadir la librería JUnit en el path de librerías del proyecto para que pueda compilar la
clase.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
package tomaDeContacto.primero;
import org.junit.Test;
@Test
public void testMiPrimerPrograma() {
fail("Not yet implemented");
}
}
Esto genera el esqueleto en el que construir los test. Los métodos públicos anotados con @Test se ejecutarán en la
fase de tests.
@Test
public void testMiPrimerPrograma() {
MiPrimerPrograma.main(null);
}
Haga clic con el botón derecho en el editor o el explorador de packages teniendo la precaución de elegir la clase
MiPrimerPrograma antes. Seleccione después la opción Run As JUnit Test.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Una buena regla básica consiste en limitar el número de métodos main() en una aplicación.
El programa de test ya no compila: aparecen iconos rojos en distintos lugares, sobre todo allí donde está el error. Es
normal en este punto.
import org.junit.Assert;
import org.junit.Test;
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 6/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
@Test
public void testMiPrimerPrograma() {
MiPrimerPrograma test = new MiPrimerPrograma("Pepe");
test.yo();
Assert.assertEquals("¡No es Pepe!",
"Pepe", test.getNombre());
}
}
El método estático assertEquals de la clase org.junit.Assert verifica que el nombre de la clase a probar es
efectivamente igual a la cadena de caracteres ”Pepe”. Si no es el caso, devuelve un mensaje de error con la cadena
del primer parámetro.
El package a utilizar es efectivamente org.junit. Existe una clase con el mismo nombre en el package
junit.framework pero está descatalogada y no debe utilizarse.
Vuelva a ejecutar el test, por ejemplo haciendo clic en el botón Rerun Test.
Se pasa correctamente el test, pero sigue mostrando «¡Soy Yo!». Debería con toda lógica mostrar «¡Soy Pepe!».
No parece normal, pero es lo que hace realmente el código del método yo():
Este código instancia un nuevo objeto MiPrimerPrograma y lo ejecuta en vez de tomar en cuenta los atributos
del objeto.
El trabajo no ha acabado aún. El método yo() no se puede realmente probar: hace falta que alguien humano lea
su salida. Todavía no se puede confiar a un sistema automatizado.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 7/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Debe por lo tanto modificar el método añadiéndole por ejemplo un valor de retorno que será la cadena a mostrar, y
un método para mostrarlo en la salida de la consola.
@Test
public void testMiPrimerPrograma() {
MiPrimerPrograma test = new MiPrimerPrograma("Pepe");
Assert.assertEquals("¡No es Pepe!",
"Pepe", test.getNombre());
test.mostrar();
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 8/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Internacionalización
La internacionalización de un programa consiste en adaptarlo a varios países.
Desde el punto de vista de un desarrollador de un aplicación, los puntos importantes a tener en cuenta cuando
quiera distribuir su software en varios países son:
Las unidades de medida: esto abarca desde el peso (para una aplicación que gestiona artículos al peso) a la
moneda.
El sentido de lectura: si una aplicación se distribuye en países del mundo árabe, en Israel o en Japón tendrá usuario
con otros hábitos de lectura. Por ejemplo el mundo hebreo y árabe leen horizontalmente pero de derecha a
izquierda (salvo para las palabra inglesas, españolas,...).
El sentido de lectura tendrá también impacto sobre la disposición gráfica de los componentes de una aplicación.
Estas cadenas plantean un problema: tienen como objetivo crear un mensaje a partir de una variable, que es el
nombre. Nada permite afirmar que la estructura de la traducción será siempre la misma. Además, el traductor
deberá saber que las palabras a traducir forman parte de una frase más amplia.
Para facilitar la tarea de traducción, se transforma la técnica de concatenación de cadenas de caracteres usando
el método estático format() de la clase String para obtener el efecto deseado.
El código %s permite realizar la substitución textual de un objeto (se llama al método toString() del objeto y se
reemplaza %s por el valor devuelto de la llamada a este método, aquí el valor del nombre).
El método String.format es muy potente y acepta un parámetro con una sintaxis muy rica:
https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#syntax
Eclipse propone un mecanismo de traducción de cadenas de caracteres basada en las API de Java.
Seleccione la clase MiPrimerPrograma en el explorador de packages (en inglés Package Explorer) y haga
clic con el botón derecho para elegir la opción Source Externalize Strings.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/9
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Aparece un cuadro de diálogo que permite elegir las cadenas de caracteres a traducir.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/9
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/9
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Un archivo de texto messages.properties, que contiene las traducciones. Es un archivo que podrá enviar a los
traductores.
Una clase Messages. Esta clase agrupa la mecánica usada para leer el archivo de traducción.
Vuelva a ejecutar los test JUnit. El test unitario debe seguir mostrándose en verde.
Hasta aquí, las modificaciones no tienen ningún impacto sobre el código. Todo sigue funcionando como antes.
Cree una carpeta fuente con la opción New Source Folder y elija src/main/resources como nombre
de carpeta.
Todos los recursos, es decir las imágenes y archivos de traducción se guardarán en esta carpeta.
Esta organización de proyecto permitirá encontrar fácilmente los pocos archivos que deba enviar a los traductores.
Cree después un nuevo package llamado tomaDeContacto.primero en esta nueva carpeta y desplace el
archivo messages.properties hasta este package.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/9
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Esto permite crear un archivo de traducción específico para la lengua inglesa. La clase Messages encontrará las
traducciones en función de los archivos messages_<algo>.properties.
Si quiere añadir traducciones específicas para Inglaterra, cree el archivo messages_en_GB.properties. Si desea
crear una traducción para los belgas francófonos, cree el archivo messages_fr_BE.properties. Para los suizos,
cree el archivo messages_fr_CH.properties. Según el javadoc de la clase Locale
(http://docs.oracle.com/javase/8/docs/api/java/util/Locale.html ), las primeras letras que designan el idioma provienen de la
norma ISO 639, las letras que designan el país de la norma ISO 3166.
MiPrimerPrograma.0=I’m %s\!
@Test
public void testMiPrimerProgramaIngles() {
Locale.setDefault(Locale.ENGLISH);
MiPrimerPrograma test = new MiPrimerPrograma("John");
Assert.assertEquals("No es John!",
"John", test.getNombre());
Assert.assertEquals("La visualización es incorrecta",
"It’s John!", visualizacion);
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/9
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
test.mostrar();
}
La llamada al método estático setDefault de la clase java.util.Locale permite forzar el programa para que use el
inglés.
Uno de los dos test fallará. Esto es debido a que los mensajes (las traducciones) han cambiado en el inicio de la
aplicación y no pueden cambiar después.
Se transformará este mecanismo para que pueda soportar el cambio dinámico de idioma. Aquí tiene la clase
Messages:
package tomaDeContacto.primero;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
private Messages() {
}
Para encontrar una traducción vinculada a una clave, la clase Messages interroga un objeto de tipo
ResourceBundle gracias al atributo llamado RESOURCE_BUNDLE.
Este atributo se define estáticamente y solo se inicializa cuando la JVM carga la clase Messages: es la principal
causa del problema.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 6/9
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Se ha creado una propiedad estática de tipo Map<Locale, ResourceBundle> que almacenará los bundles de
traducción cada uno con la clave del idioma asociado. El tipo Map es una inferfaz, y se inicializa la propiedad con
un objeto de tipo HashMap, que es una clase que implementa la interfaz Map.
Se inicializa el contenido de esta propiedad con el bloque static { ... }, que llama al método charge(),
guardando este último las traducciones en el Map.
Este método utiliza dos novedades de Java 8: los Optional y los Stream.
Optional es una clase que permite tener en cuenta el potencial de una variable nula.
Son equivalentes a:
if (bundle == null) {
bundle = BUNDLES.get(Locale.SPANISH);
}
La complejidad de un método se cifra en función del número de if, else, for que lo compone. Un método demasiado
complejo es difícilmente mantenible y poco legible. La clase Optional permite reducir esta complejidad.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 7/9
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Para ello, se utilizan los streams. Un stream es un flujo de datos que puede transformarse en otros flujos con
filtrados intermedios.
El flujo se corresponde aquí con un solo dato, el bundle. Se crea con el método estático of() de la interfaz
Stream.
Lo que es equivalente a:
La primera traducción se recupera con el método findFirst(), que devuelve un Optional de esta cadena de
caracteres. Después se recupera el valor del opcional llamando a get().
Esta manera de codificar permite disminuir la complejidad de un método: ya no existen if para probar valores, se
suprimen los bucles for o while,... Uno de los objetivos es ganar en legibilidad.
Pruebe la clase.
Todavían existen errores. Esto es debido a que la clase ResourceBundle no encuentra traducción para el idioma
español (no hay archivo messages_es.properties), y propone como alternativa las traducciones por defecto, es
decir el resultado de llamar a Locale.getDefault().
La siguiente línea:
bundle = opt.orElse(BUNDLES.get(Locale.SPANISH));
permite proveer la traducción española si se desconoce el idioma en el sistema, por ejemplo el italiano.
@Test
public void testMessages() {
String string = null;
try {
string = Messages.getString("inexistente",
Locale.SPANISH);
Assert.fail("Mensaje desconocido recuperado ");
} catch(MissingResourceException e) {
Assert.assertNotNull(e);
}
string = Messages.getString("MiPrimerPrograma.0",
Locale.SPANISH);
Assert.assertEquals("Formato incorrecto en Español ",
"¡Soy %s!", string);
string = Messages.getString("MiPrimerPrograma.0",
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 8/9
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Locale.ENGLISH);
Assert.assertEquals("Formato incorrecto en Inglés ",
"It’s %s!", string);
string = Messages.getString("MiPrimerPrograma.0",
Locale.ITALIAN);
Assert.assertEquals("Formato incorrecto en Italiano ",
"¡Soy %s!", string);
}
Es posible utilizar otras versiones del método getString(String, Locale), como por ejemplo:
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 9/9
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Añadir plugins
Existen dos maneras de añadir plugins a Eclipse. O realiza usted mismo las operaciones, o indica a Eclipse la URL
del sitio web que proporciona el plugin para su descarga y le deja hacer el trabajo.
En el primer caso, una vez descargado el plugin, descomprímalo (un plugin está muchas veces disponible como
un archivo comprimido: un archivo zip) y copie su contenido en la subcarpeta dropins de la carpeta de instalación
de Eclipse. Eclipse supervisa esta carpeta y se instalarán los plugins tras el próximo inicio de Eclipse.
En el segundo caso, basta con ir al menú Help Install New Software... y seleccionar o copiar la dirección del
sitio web en la sección Work with.
Vaya al menú Help Eclipse Marketplace... para obtener e instalar un conjunto de plugins seleccionados
por Eclipse.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Por defecto, Eclipse está en inglés. Existen plugins de traducción en diferentes lenguajes. Llegando en muchas
ocasiones las traducciones con cierto retraso, se arriesga a tener dificultades para encontrar o disponer de una
interfaz gráfica que mezcle el español y el inglés. Además, una vez Eclipse traducido, no podrá acceder fácilmente a
las traducciones inglesas. Y en cuanto busque ayuda o información por Internet, el proceso será más difícil.
Dicho esto, Java está en inglés y a menos de que seamos totalmente herméticos al idioma de Shakespeare, se
recomienda trabajar con el idioma por defecto.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Para añadirlas selectivamente, haga clic con el botón derecho en el proyecto, seleccione Properties y elija
Java Build Path.
No se recomienda utilizar los jars externos (que no se encuentran en la carpeta del proyecto), si trabaja en equipo. E
incluso trabajando solo, si cambia de ordenador y solo ha comprimido el proyecto y no los jars externos, se arriesga
a perder tiempo para volver a inicializar todo. Lo mejor es copiar estos jars en el proyecto y referenciarlos con el botón
Add JARs....
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Es interesante para un desarrollador o un equipo de desarrollo guardar una traza de las diferentes modificaciones,
también llamadas versiones.
El método más básico de realizar esto es conservar en el disco duro cada modificación en una carpeta distinta, en
el mejor de los casos con la fecha incluida en el nombre.
Este método permite compartir el código entre varias personas, cada una trabajando de su lado de una a tres
semanas, y después... permite reintegrar cada una de las modificaciones transcurridas de una a tres semanas
teniendo el riesgo de olvidar algunas.
Por suerte, existen otras maneras de realizar esto. Para facilitar este trabajo necesario, existen programas de
gestión de versiones como Git (https://gitscm.com/), Mercurial (https://mercurialscm.org/), Subversion
(https://subversion.apache.org/) o incluso CVS (http://www.nongnu.org/cvs/) para los que tienen alma de
paleontólogo.
Estas distintas aplicaciones de gestión de versiones permiten archivar todas las modificaciones del código fuente de
un programa, compartir el código entre varios miembros de un equipo, integrar fácilmente sus modificaciones,
probar diferentes soluciones, encontrar qué modificación provocó bugs así como el responsable,... y más
vulgarmente recuperar un estado estable del código fuente si alguien modificó o borró sin querer archivos.
Estos sistemas de gestión de versiones no solo sirven para los equipos de desarrollo: aunque un único desarrollador
trabaje sobre un proyecto, podrá aprovechar este paracaídas en caso de error de codificación.
Estando Git integrado de manera nativa en Eclipse Luna, explicamos brevemente a continuación este sistema
descentralizado de gestión de versiones.
Seleccione el proyecto MiPrimerPrograma en el explorador de packages, haga clic con el botón derecho y
elija Team Share Project.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Se abre un nuevo cuadro de diálogo para crear un espacio de almacenamiento para todas las versiones
del proyecto. Esta etapa solo es necesario realizarla la primera vez.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Elija la ubicación en el disco duro donde crear este espacio de almacenamiento (llamado repository o
repositorio), y haga clic en Finish.
El proyecto está desde este momento gestionado por el sistema de control de versiones pero los archivos
existentes no se consideran aún como versionados: están decorados con un signo de interrogación en la vista
Package Explorer.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Seleccione el proyecto y haga clic con el botón derecho para elegir la opción Team Add to Index.
Los archivos están ahora marcados con un asterisco sobre fondo negro: se han detectado como pertenecientes a
una modificación por Git.
Seleccione después la opción Team Commit tras hacer clic con el botón derecho en el proyecto.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Añada un mensaje para el commit de las modificaciones (el mensaje es obligatorio en el sistema Git) y haga
clic en Commit.
Acaba de versionar (commit) los archivos: ahora están decorados con un pequeño cilindro amarrillo.
Si más adelante se modifica algún archivo en el proyecto, aparecerá con un símbolo > delante del nombre.
Existen numerosas posibilidades con Git en Eclipse. Aquí tiene una breve descripción de las más utilizadas,
enumeradas según su ubicación en el menú que aparece al hacer clic con el botón derecho:
Team Add to Index: añade un archivo en el sistema de gestión de versiones. Necesitará hacer un commit para
que se tome en cuenta el nuevo archivo.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Compare With Head Revision: compara un archivo con la última versión del mismo y abre un editor gráfico
para mostrar las modificaciones.
Replace With Head Revision: borra las modificaciones actuales y vuelve a la última versión.
Team Pull: recupera las últimas modificaciones guardadas en el repositorio del equipo.
Team Synchronize Workspace: enumera los archivos modificados localmente y los modificados por el resto del
equipo.
También es posible crear ramas, es decir versiones alternativas del proyecto que tendrán su vida propia, poner en
espera modificaciones con el comando llamado Stash, etc.
Eclipse proporciona únicamente un subconjunto de todas las funcionalidades de Git. Existen interfaces gráficas que
permiten utilizar todas las funcionalidades: https://gitscm.com/download/gui/linux
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 6/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Aplicación Luna
En el ámbito de este libro, el objetivo es triple: en primer lugar revestir y profundizar concretamente los conceptos
principales de la programación orientada a objetos vistos anteriormente, en segundo lugar aprender a elaborar
código estructurado adoptando un método de desarrollo y para finalizar comprender las dificultades de realizar un
proyecto para al final entregar una aplicación que incluya todas las funcionalidades que se pueden presentar en
una aplicación profesional.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
clientes,
artículos,
pedidos.
añadir,
eliminar,
modificar,
buscar,
imprimir,
exportar.
La exportación puede hacerse en los siguientes formatos: pdf, html, docx, odt, ods.
estadísticas,
gráficos.
El envío de mensajes.
Uso del teclado y/o del ratón en función de las acciones a realizar,
Ubicación del cursor en el primer campo de introducción de datos tras la apertura de la ventana,
Presentación de los datos en modo de ficha después de un doble clic en una línea de una tabla,
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
La serialización es una solución elegante que permite obtener la persistencia de los objetos pero conlleva varias
variantes que presentan cada una ventajas e inconvenientes. Según el tipo de serialización elegida, existen
limitaciones en cuanto a velocidad, número de clases o también su complejidad.
Las bases de datos relacionales son inevitables en el mundo informático y más particularmente en el de la gestión.
Están basadas en una tecnología probada desde hace decenas de años y han sabido integrar los datos de tipo
objeto. Esta opción es la elegida para el proyecto Java, opción que es por otra parte la más utilizada hoy en día.
Entre los numerosos SGBD relacionales, se elige MySQL por sus cualidades además de que, al igual que Eclipse, es
un producto open source. Se describen su puesta en marcha con el servidor XAMPP, su utilización con Java y el
mapping objetorelacional JPA en el capítulo Base de datos MySQL.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
El Gang Of Four (Gamma, Helm, Johnson y Vlissides: la banda de los cuatro) formuló 23 patrones de diseño en
1994 en un libro titulado Design Patterns Elements of Reusable ObjectOriented Software. Existen otros patrones
de diseño disponibles en la literatura como por ejemplo los patrones Support de inicialización al requerimiento,
Reactor y Active Record.
No se deben considerar como soluciones definitivas grabadas en piedra sino más bien como guías de buenas
prácticas que describen las grandes líneas de un diseño más modular y mantenible.
En un proyecto, los patrones están la mayor parte de las veces asociados entre ellos para proveer un código que
responda a las necesidades de los usuarios al mismo tiempo que dan un rumbo para las futuras evoluciones de la
aplicación.
La asociación de diferentes patrones de diseño lleva a patrones de arquitectura como MVC, que será el utilizado en
el proyecto Luna.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Metodología
Para realizar el análisis del proyecto, se utiliza UML (Unified Modeling Language).
UML nació de la fusión de las tres principales metodologías de modelización de objetos a mediados de los años 90:
OMT (Object Modeling Technique) de James Rumbaugh, Booch de Grady Booch y OOSE (ObjectOriented Software
Engineering) de Ivar Jacobson. No se trata sin embargo de una metodología pura sino de un lenguaje gráfico de
modelización de sistemas de información, dicho de otro modo de notación o representación gráfica del dominio que
se desea modelizar. Esto es tan cierto como que UML no precisa las etapas a seguir, sino cómo realizarlas para
pasar de las necesidades de los usuarios al código fuente.
La modelización UML se elabora con un formalismo gráfico y se apoya actualmente en catorce diagramas UML
versión 2.4.1. Estos últimos no se utilizan, generalmente, en su totalidad. Su empleo depende del tamaño, la
naturaleza y la complejidad del proyecto. Usaremos un número restringido de diagramas para construir la
aplicación Luna que es un proyecto pequeño clásico de gestión tal y como se definió en el capítulo Presentación del
proyecto.
Generalmente, se considera el diagrama de clases como el más importante. Es por lo tanto habitualmente el
primer diagrama que los creadores de aplicaciones realizan. Se elige un enfoque de Ingeniería del Software en este
libro inspirándose en métodos basados en UML como RUP (Rational Unified Process) y/o XP (eXtreme
Programming).
La metodología adoptada depende de las necesidades de los usuarios: no se modelizan por lo tanto en primer lugar
las clases técnicas, como las de conexión, que no son directamente perceptibles por el usuario final.
Por el contrario, teniendo en cuenta la situación central del usuario, se crean las maquetas representativas de la
aplicación final en primer lugar para que después sean sometidas a aprobación.
Se supone en el ámbito de este libro que estas maquetas y los prototipos posteriores serán validados por estos
usuarios.
Se resume la metodología elegida para este proyecto. Partiendo de las necesidades, se realizan maquetas y en
paralelo los diagramas de casos de uso.
Los diagramas de secuencia y de comunicación se crean después, para finalmente construir el diagrama de clases
de la aplicación. Todo esto se realiza utilizando ciclos iterativos e incrementales.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Al final la idea es simple: para modelizar (comprender y representar) un sistema complejo, es necesario volver a
empezar varias veces afinando el análisis en etapas y autorizando la marcha atrás hacia las etapas anteriores. Esta
metodología se aplica al ciclo de desarrollo en su conjunto. Una o varias iteraciones permiten así un avance
significativo que se llama incremento. Se reitera el proceso en cada incremento.
La metodología iterativa e incremental puede también aplicarse a los ciclos de vida más clásicos (en cascada, en
V,...).
Cuando se llegue al diagrama de clases, se puede optar por dos opciones. O bien la producción de código empieza
de inmediato o bien se difiere esta para antes agrupas las clases. Se realiza este esfuerzo adicional por la voluntad
de racionalizar el código para facilitar el mantenimiento correctivo y evolutivo.
diálogo,
control,
entidad.
El tipo diálogo agrupa las clases que constituyen la interfaz hombremáquina: el HMI (en inglés GUI por Graphical
User Interface). Son en su mayoría clases gráficas que surgen de las maquetas. No realizan ningún tratamiento
excepto los que permiten interactuar con el usuario y son responsables de la visualización de los datos.
El tipo entidad corresponde a las clases propias del dominio que se quiere modelizar. Cada instancia de estas
clases corresponderá en general a una línea de datos almacenada en alguna de las tablas de la base de datos
MySQL. Cada clase dispondrá de métodos llamados CRUD (del inglés Create, Read, Update, Delete), que permiten
realizar operaciones elementales sobre los datos.
Las clases de tipo control establecen la comunicación entre los dos tipos de clases anteriormente descritos. Por
ejemplo, se trata la solicitud de creación de un cliente por una clase de control que la transmite a la clase CRUD
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
que posee este método y que es responsable de la gestión de clientes. También es posible hacerlas responsables de
los controles relativos a las reglas de negocio.
UML es un formalismo que permite describir gráficamente un sistema informático. ¡Es perfectamente posible hacer
la modelización UML con un bolígrafo y un papel!
Algunos programas permiten también realizar esta modelización de manera informática, con el plus de por ejemplo
la generación automática de código...
Lo más importante en una modelización UML es que un participante en el proyecto puede comprender la aplicación
sin necesidad de tener un bagaje técnico adicional.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Existen varios plugins UML para Eclipse, algunos gratuitos y otros de pago. La elección de un plugin se hace por
razones de costumbre, coste y hasta de gusto... Para seguir con un espíritu open source, se utiliza el plugin
Papyrus que está totalmente integrado en Eclipse.
Abra el sistema de instalación de plugins de Eclipse accediendo al menú Help y después a Install New
Software.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Haga clic en Next y acepte el acuerdo de licencia. Haga clic en Finish. Al acabar la instalación, reinicialice
Eclipse.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Se proponen los diagramas elaborados según la metodología descrita anteriormente a título indicativo.
Como se ha subrayado en la sección Metodología, el diagrama de casos de uso es a la vez uno de los más sencillos
y uno de los menos utilizados. Numerosos desarrolladores le dan todavía poca importancia. Esto es quizás debido a
la herencia de Merise y de la mayor parte de las primeras metodologías de modelización de objetos (antes de la
fusión de OMT, OOSE y Booch) para los cuales no existe un equivalente y cuyo elemento principal en cuanto a
Merise es el modelo conceptual de los datos, modelo que presenta similitudes con el diagrama de clases.
Se trata con el diagrama de casos de uso de definir las funcionalidades y el comportamiento de la futura aplicación
de cara al usuario o de su entorno (otros usuarios, sistemas informáticos externos a la aplicación, autómatas,...).
Permite una comprensión común entre el conocimiento del dominio y el conocimiento de realización, dicho de otra
forma entre el cliente, ya sea simple usuario o experto, y el desarrollador.
Los casos de uso describen únicamente las interactuaciones o las funcionalidades esperadas del sistema en
desarrollo. No describen la manera de conseguirlas. Durante el desarrollo sirven de referencia para verificar si las
necesidades expresadas por el cliente están realmente satisfechas.
Cree un nuevo proyecto UML_Luna de tipo Papyrus Project con el menú File New Other y eligiendo
Papyrus Project. Introduzca el nombre del proyecto y haga clic en Next.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Cree al mismo tiempo un nuevo diagrama de casos de uso seleccionando UseCase Diagram y llamándole
CasoDeUso. Haga clic en Finish.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Ahora abra la perspectiva de modelización Papyrus con el menú Window Open Perspective Other.
Las vistas de Eclipse se reordenan para adaptarse al contexto de modelización UML según Papyrus, y aparece el
modelo en la zona de edición con el primer diagrama. Este diagrama está evidentemente vacío.
El diagrama de casos de uso propuesto aquí presenta las interacciones posibles del usuario con el sistema, limitado
a la gestión de clientes. Este debe poder, después de identificarse, consultar, añadir modificar y suprimir un cliente.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Para realizar este diagrama, utilice los elementos gráficos presentes en la paleta situada a la derecha del
diagrama.
Para añadir un usuario, deslice el elemento Actor en el diagrama. Este elemento se encuentra en la sección
Nodes de la paleta.
Para enlazar elementos, seleccione los enlaces en la sección Links de la paleta, y apunte sucesivamente con
el ratón a los elementos del diagrama que desea enlazar.
Después de conversar con los usuarios, se da cuenta de que olvidó una función importante: ¡cuando se quiere
modificar o suprimir un cliente, debe primero realizar una búsqueda entre ellos! Por lo tanto se modifica el
diagrama en la siguiente iteración de esta manera:
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Diagrama de secuencia
El diagrama de secuencia persigue modelizar el aspecto dinámico del sistema. Se le puede comparar a una
storyboard que evidencia las interacciones existentes entre los objetos del sistema. Se pone el acento sobre el
orden cronológico de los mensajes emitidos, siendo el eje de tiempo el eje vertical de este diagrama y siendo los
primeros mensajes emitidos los situados gráficamente más arriba en el diagrama.
Para cada acción del usuario definida en el diagrama de casos de uso, es conveniente establecer un diagrama de
secuencia. Utilizamos la función de lectura de todos los clientes como ejemplo.
En la vista Model Explorer, seleccione el módulo raíz haciendo clic con el botón derecho y elija la opción
New Diagram Create a new UML Sequence Diagram.
Haga clic con el botón derecho en el elemento modelo de la vista Model Explorer y añada un package con la
opción New Child Package. Llame a este elemento dialogo.
Haga clic con el botón derecho en este package en la misma vista y añada una clase con la opción New Child
Class. Llame a este elemento VistaClientes.
Utilice los elementos presentes en la vista Model Explorer para construir el diagrama deslizando elementos
que interactúen entre ellos.
Utilice la sección Edges de la paleta para crear flechas que representen las interacciones entre los elementos.
Las flechas solo pueden enlazar componentes de sistema sobre un rectángulo presente en su línea de
vida.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Para añadir este rectángulo, seleccione el elemento Action Execution Specification en la paleta de la
sección Nodes, y deslícelo sobre el elemento deseado.
Un diseño del sistema en un lenguaje orientado a objetos como Java necesita para una mayor claridad y
mantenibilidad de la aplicación confiar solamente en una responsabilidad por clase. El control de cliente tiene aquí
demasiadas responsabilidades a gestionar, algunas de estas responsabilidades son comunes al futuro control de
artículo. Por lo tanto es más ventajoso descomponerlas en clases más pequeñas.
Una segunda iteración en la construcción del diagrama permite aproximar mejor los diferentes elementos
necesarios en las interacciones entre el control cliente y la base de datos.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Diagrama de comunicación
El diagrama de comunicación es equivalente desde un punto de vista semántico al diagrama de secuencia. Sin
embargo pone el acento sobre la organización estructural de los objetos de cara a la emisión de mensajes.
Cree un nuevo diagrama a partir del modelo de la vista Model Explorer, seleccionándolo y eligiendo la opción
New Diagram Create a new UML Communication Diagram. Llámelo por ejemplo
ComunicacionLecturaClientes.
Deslice en este nuevo diagrama los elementos presentes en el diagrama de secuencia desde la vista Model
Explorer.
Para cada uno de los mensajes emitidos presentes en el diagrama de secuencia, cree un nuevo mensaje
equivalente entre los mismos elementos en el diagrama de comunicación.
Es habitual prefijar estos mensajes con números indicando el orden de aparición en el diagrama de
secuencia.
Algunas otras herramientas de modelización UML proponen la generación automática de este diagrama.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Diagrama de clases
Se utiliza el diagrama de clases para modelizar el aspecto estático de un sistema. Pone en evidencia las clases y
las relaciones que estas tienen entre ellas: dependencia, asociación, generalización,... Según la importancia del
sistema a modelizar, se pueden utilizar diagramas de clases intermediarios, particularmente los diagramas de
clases participantes que describen para cada caso de uso las tres principales clases de análisis y sus relaciones
(consulte la sección Metodología).
Según la metodología elegida, es (por fin) posible llegados a este punto construir el diagrama de clases. Debe
contener las clases puestas en evidencia por los objetos presentes en los diagrama de secuencia y de comunicación
así como las que eventualmente faltan, necesarias para el funcionamiento de las maquetas (consulte el capítulo
Maquetas).
Haga clic con el botón derecho en la vista Model Explorer y elija la opción Create a new UML Class
Diagram.
En función de las necesidades, otras clases pueden aparecer desde el análisis y/o al realizar el primer juego de
maquetas. Por ejemplo se puede añadir una clase gráfica adicional surgida de las maquetas si se desea mostrar el
conjunto de registros a guardar de una consulta SQL en una nueva ventana. Maquetas y diagramas pueden así
interactuar para llegar lo antes posible a lo esencial de las clases del proyecto.
El diagrama de clases propuesto es consecuencia de los diagramas de secuencia y de comunicación del caso de uso
Leer todos los clientes.
Este diagrama basado en los tres tipos de clases de análisis presentados anteriormente propone por lo tanto una
división semejante a la descrita por el modelo MVC (Modelo Vista Controlador). MVC, creado en 1980 por Xerox
Parc para el lenguaje Smalltalk, es un patrón de arquitectura o design pattern que introduce entre otros el
concepto de sincronización. La idea es actualizar automáticamente todas las vistas que presentan los mismos
datos en función de las modificaciones realizadas en la base de datos y recíprocamente. Esta actualización puede
afectar solamente a vistas debido a las acciones del usuario. Es lo que diferencia fundamentalmente MVC del
enfoque escogido que busca esencialmente la separación de los datos de la interfaz hombremáquina mediante
una capa encargada de la transmisión de las consultas y los resultados.
MVC no presenta solamente ventajas. Para llevar a cabo la sincronización, debe crear nuevas interfaces y clases de
tipo listener y gestionar eventos para añadirlos al modelo para que pueda escuchar y notificar los cambios que le
afectan. Los controladores deben escribirse de manera que puedan ser informados de los cambios para realizar la
sincronización y actualización de las vistas y del modelo. Esta arquitectura aporta por lo tanto un nivel de
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
complejidad añadido que implica un importante trabajo de diseño y el correspondiente incremento del código. Por
otra parte, la sincronización puede depender de numerosas condiciones, lo que hace aún más pesada su
realización. Se detallan estos elementos en el capítulo Modelo MVC.
El rombo blanco en la relación VistaClientes ControlClientes representa una relación de agregación: las
instancias de la clase VistaClientes contienen una instancia (y solamente una ya que se especifica con el número al
lado de la relación) de la clase ControlClientes.
El rombo negro en la relación ControlClientes ClienteCrud es una relación de composición, que es equivalente a
una relación de agregación con restricciones adicionales. Representa sobre todo que una instancia de ClienteCrud
solo puede estar asociada con una única instancia de la clase ControlClientes, y que si se elimina una instancia de
la clase ControlClientes del sistema, se eliminará también la instancia ligada de ClienteCrud.
También se observa una relación de herencia entre la clase VistaClientes y la clase JPanel.
Para crear la clase Direccion, seleccione el elemento Class de la paleta en la sección Nodes y deslícelo en el
diagrama en el interior del package entidad. Llame a esta nueva clase Direccion.
Repita la operación para las clases VentanaClientes y JPanel y deslícelas en el package dialogo.
La flecha simboliza esta relación de herencia en una dirección. Tenga cuidado de no invertir el sentido de esta
relación.
Materialice la relación entre Cliente y Direccion eligiendo el enlace Association en la sección Edges de la
paleta.
Seleccione esta nueva relación en la vista Properties de la sección UML, y en el detalle Member end
llamado direccion. Seleccione composite en la propiedad Aggregation. La propiedad Multiplicity debe valer
0..1.
La relación VistaClientes ControlClientes posee una propiedad Aggregation con el valor shared en el Member
end del ControlClientes.
Las etapas seguidas para el análisis de las entidades Articulo y Pedido siguen una metodología similar a la de la
entidad Cliente. Evidentemente es posible afinar el análisis para tratamientos particulares de la aplicación siendo la
idea cubrir primero las necesidades genéricas antes de perder tiempo en los detalles.
El plugin Papyrus no permite generar automáticamente el código. En este punto, es inútil mejorar la modelización
añadiendo atributos a las clases.
Otros plugins o programas permiten esta generación automática. Usted deberá decidir si esto vale la pena para su
proyecto, sabiendo que a veces este proceso resulta (muy) consumidor de tiempo para generar un proyecto entero a
partir de la modelización UML. Se puede realizar esta elección si por ejemplo la aplicación o ciertas partes de la misma
tendrán que escribirse en diferentes lenguajes orientados a objetos, como Java o C++.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Introducción
Este capítulo necesita conocimientos generales sobre las bases de datos y el lenguaje SQL (Structured Query
Language).
Se utiliza la aplicación XAMPP, acrónimo de X Apache MySQL Perl PHP. Así se dispone de un servidor, una base de
datos relacional y una interfaz gráfica de administración. Los lenguajes Perl y PHP no son útiles en el ámbito del
proyecto Luna. Existen versiones de XAMPP para Windows, Linux, Mac OS y Solaris.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Una tabla define un número determinado de columnas, una por cada información a guardar. Por ejemplo la tabla
de clientes contendrá una columna con los apellidos del cliente, otra para almacenar su nombre, otra para su e
mail, otra para la fecha de creación del cliente en el sistema, ... Estas columnas tienen todas un nombre único
dentro de la tabla.
Cada tabla contendrá datos. Se almacenarán estos datos en una fila. Por ejemplo la tabla de clientes contendrá
tantas filas como clientes existan, cada fila almacenará los datos del cliente en la columna apropiada.
Con el fin de identificar de manera única a un cliente, se dedicará una columna para almacenar este identificador.
Se habla de este identificador como una clave primaria. Una clave primaria debe obligatoriamente ser única
dentro de los valores de esta columna para un tabla dada.
Habitualmente, existen varias tablas en una base de datos, ligadas entre ellas: se debe poder en un sistema
encontrar todos los pedidos de un cliente dado. Se habla comúnmente de una relación entre los datos (de donde
proviene el término de bases de datos relacionales). Esta relación se crea añadiendo una columna adicional en
una de estas tablas donde se almacena la clave primaria de una fila de otra tabla. Se llama a esta columna
habitualmente clave foránea. Así la tabla de facturas tendrá una columna llamada ”clave_cliente”, representando
cada fila un pedido que almacena en esta columna la clave primaria del cliente asociado.
Una relación así se denomina Many To One: un cliente puede estar ligado a varios pedidos, pero solo puede existir
un cliente para un pedido dado. Existen otras relaciones del tipo One To One o Many To Many.
Este sistema de almacenamiento permite obtener datos estructurados y organizados entre ellos. Otro aspecto
importante es que si se debe modificar el nombre del cliente, esta modificación se llevará a cabo en un único lugar
ya que una base de datos bien concebida minimiza la información almacenada.
Un sistema de gestión de bases de datos relacionales (SGBD) es un programa que permite acceder y manipular
todos sus datos desde y hacia diferentes bases de datos. MySQL es un SGBD, como lo pueden ser Oracle,
PostgreSQL, SQLite,...
Existen otros tipos de SGBD, por ejemplo del tipo NoSQL como MongoDB. MongoDB no almacena los datos en
tablas sino en colecciones, no en filas sino en documentos, no en columnas sino en campos,...
Organizar una base de datos y tener un conjunto de datos coherente está muy bien, pero es necesario poder
«jugar» con ellos, consultar la base de datos o suprimir datos... Para alcanzar este objetivo, es necesario un
lenguaje de consultas: se trata de SQL.
2. SQL
SQL es un lenguaje, por lo tanto con una sintaxis, que permite interrogar una base de datos para encontrar
datos, y manipularlos (almacenarlos, modificarlos, suprimirlos). Permite efectuar lo que se llama operaciones
CRUD (del inglés Create, Read, Update, Delete) y muchas otras operaciones (contar, ...).
Para insertar (crear) una fila de datos en la tabla cliente, podrá escribir:
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Para encontrar los pedidos de un cliente, deberá realizar un enlace entre dos tablas. Este enlace (llamado join en
inglés) consiste generalmente en asociar las líneas de dos tablas con una restricción entre los valores de una
columna de la primera tabla y los valores de una columna de la segunda tabla.
Este join devuelve las filas que representan los pedidos realizados por los clientes cuyo apellido es «Fernández»,
estando cada fila completada con los detalles del cliente.
Existen varios tipos de join: join interno (el más extendido, como el que se ve en el ejemplo anterior), join
externo a la izquierda, join externo a la derecha, join externo completo,... No se hablará más de estos tipos
diferentes en este libro.
La instrucción EXPLAIN situada delante de la consulta permite evaluar la complejidad de esta consulta y buscar
así posibles optimizaciones.
3. Transacciones
Las manipulaciones (las escrituras) de datos en una base de datos se hacen dentro de una transacción.
Suponga que está al cargo de una base de datos que gestiona las cuentas bancarias de diferentes clientes y que
una operación de gestión consiste en trasferir dinero de una cuenta a otra. Concretamente se trata de debitar de
una cuenta y añadir (abonar) la misma cantidad en la otra cuenta. Técnicamente, esto consiste en realizar dos
operaciones de UPDATE, una para cada cuenta.
Suponga ahora que se produce un error justo entre estas dos operaciones de UPDATE (alguien desenchufa la
toma de corriente del servidor, la conexión a la red cae,...). Es mala suerte pero esto significa que el dinero ha
desaparecido: ¡es un drama para uno de los clientes y para el banco!
Una transacción permite agrupas operaciones elementales en una única operación más grande. Si todas las
operaciones elementales tienen éxito, la transacción se valida (se habla de commit). Si alguna de las operaciones
elementales fracasa, la transacción considera que la operación global no se ha completado y se anularán todas las
operaciones elementales (se habla de rollback).
MySQL propone dos motores de almacenamiento: MyISAM e InnoDB. Solo InnoDB gestiona las transacciones (y el
mecanismo de claves foráneas). En contrapartida, InnoDB es más lento y goloso en espacio de almacenamiento.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Una transacción presenta tradicionalmente propiedades, agrupadas bajo el acrónimo ACID. Según este acrónimo,
una transacción debe ser:
Atómica: una transacción se realiza en su totalidad o bien no se realiza. Si alguna de las operaciones de la
transacción fracasa, se eliminará la ejecución del conjunto de las operaciones anteriores y los datos modificados
recuperarán sus valores antes del inicio de la transacción.
Coherente: cada transacción transforma datos válidos en datos válidos. La noción de dato válido se define
mediante reglas funcionales propias al dominio de la aplicación que muchas veces se implementan como
restricciones o triggers en la base de datos.
Aislada: cada transacción se ejecuta como si fuese la única en curso: si se ejecutan más transacciones
simultáneamente, cada una permanece independiente de las demás.
Permanente: cuando se efectúa una transacción con éxito, los resultados de la transacción se almacenan de
manera persistente: si una avería cualquiera ocurriera justo después de la ejecución de la transacción, los datos
modificados se podrán recuperar después de reiniciar la base de datos.
Existe un conjunto de propiedades alternativas a las propiedades ACID: se trata de los propiedades BASE (Basically
Available, Soft state, Eventual consistency) que permiten crear sistemas distribuidos de almacenamiento de datos.
Los químicos medirán la fuerza o debilidad del juego de palabras vinculado.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Las siguientes explicaciones y capturas de pantalla se corresponden con esta versión. Las operaciones a llevar a
cabo para versiones más recientes pueden ser diferentes.
Después puede elegir los componentes a instalar. Deje las opciones por defecto. Si no dispone de suficiente
espacio en su disco duro, elija solamente Apache, MySQL y phpMyAdmin.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
La carpeta por defecto para la instalación es C:\xampp. Puede elegir otra carpeta. Haga clic después en el
botón Next.
Prosiga hasta llegar a la pantalla siguiente. Haga clic en Next para arrancar el proceso de instalación.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Si todo se ha ejecutado bien, debería llegar a la siguiente pantalla. Haga clic en el botón Finish.
Acceda al panel de control situado debajo a la derecha de su pantalla y haga clic en los botones Start para
arrancar Apache y MySQL.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Se abre una página web, empezando la dirección por http://localhost/phpmyadmin. Se trata de la página de
administración de las bases de datos MySQL gestionadas por phpMyAdmin.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Introduzca una contraseña y haga clic en el botón Continuar. La que usará para el proyecto Luna es
«tempo» (no introduzca las comillas dobles).
Pare todos los servicios haciendo clic en los botones Stop en el panel de control y salga haciendo clic en el
botón Exit.
Se han modificado los parámetros del usuario root. Ahora debe actualizar estos datos en el archivo
config.inc.php para que se tome en cuenta en la próxima ejecución del servidor.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Antes, era necesario crear estas tablas manualmente en la base de datos. Con JPA, el proceso de creación puede
realizarse de manera completamente automática. Sigue sin embargo siendo necesario crear la base de datos en
sí misma.
Ejecute phpMyAdmin.
Se securizó el acceso al servidor MySQL creando una contraseña para el súper usuario root. En vez de
acceder a la base de datos con el súper usuario, lo que está fuertemente desaconsejado, se creará un
usuario que servirá de punto de acceso a la base de datos «luna».
Introduzca después un nombre de usuario, por ejemplo «eni» (sin las comillas dobles). Después introduzca
dos veces su contraseña, «java» (siempre sin las comillas dobles). Estos valores son arbitrarios. Solo es
importante recordarlos bien ya que servirán en la aplicación más tarde.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 6/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Asegúrese de desmarcar todos privilegios globales. Haga clic por último en Ejecutar. Se ha creado el
usuario.
Ahora debe darle permisos específicos de acceso a la base de datos «luna» y únicamente a esta.
Después haga clic en Continuar. Se muestra la página de permisos específicos a esta base de datos. Por
rapidez, haga clic en Seleccionar todo y en Ejecutar.
Ya solo queda una última etapa para permitir a la aplicación acceder a su base de datos. En efecto,
MySQL gestiona los permisos de acceso con la ayuda de diferentes reglas. Seleccione la primera regla,
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 7/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
que corresponde al permiso de acceso, para garantizarlo. Sin embargo, por defecto en la instalación, dos
reglas prohíben este acceso: se tratan de las reglas «anonymous» que debe suprimir.
En la pestaña Usuarios, seleccione las dos primeras reglas. Después haga clic en Ejecutar en la sección
Eliminar los usuarios seleccionados.
El usuario «eni» puede desde entonces acceder a la base de datos «luna», lo que podrá verificar un poco más
adelante con el driver ODBC.
Las operaciones de configuración de una base de datos en producción están generalmente delegadas a un
administrador de bases de datos.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 8/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
JDBC
JDBC (Java DataBase Connectivity) permite que las aplicaciones clientes desarrolladas en Java accedan a bases de
datos relaciones. JDBC provee todas las clases útiles para gestionar estas operaciones.
JDBC es una API para la cual existen cuatro tipos de drivers que difieren en rendimiento y portabilidad.
Tipo 1: el primer tipo está basado en ODBC. Las llamadas JDBC se convierten en este caso en llamadas ODBC. Este
las transmite al SGBD que ejecuta las consultas recibidas. De la misma manera, el conjunto de resultados devueltos
utilizan la pasarela o puente JDBC/ODBC entre la aplicación Java y la base de datos.
6. Cierre de la conexión
Tipo 2: se trata de un driver del una parte está escrita en Java y la otra en el lenguaje del SGBD. Las llamadas JDBC
se convierten en llamadas nativas. Su utilización necesita la instalación de la parte nativa en el cliente. Otros
inconvenientes: la pérdida de portabilidad del código, lo que excluye su uso con applets.
Tipo 3: se trata de un driver totalmente escrito en Java que se comunica con el SGBD mediante una aplicación
middleware. La portabilidad y el uso con los applets están por lo tanto asegurados.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Tipo 4: se trata de un driver totalmente escrito en Java con acceso directo al SGBD. Como para el tipo anterior, la
portabilidad y el uso con applets están asegurados. Son los editores del SGBD quienes proveen este tipo de driver.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
JPA
Java Persistence API o JPA es una interfaz de programación de aplicaciones que permite facilitar el acceso y la
codificación a datos persistentes por ejemplo en una base de datos.
JPA permite crear un modelo del dominio (clases) que servirá de interfaz entre la aplicación y los datos en la base de
datos. Se escribirán las consultas hacia la base de datos en lenguaje JPQL (JPA Query Language) que permite
escribir consultas SQL en forma de objeto. Sin embargo sigue siendo posible escribir las consultas en SQL.
Gracias a anotaciones del package javax.persistence es posible establece correspondencias entre las propiedades
de estos objetos y las tablas y columnas de la base de datos.
Incluso es posible con este modelo de dominio crear completamente la estructura de la base de datos: las tablas y
las columnas.
JPA es por lo tanto una API de alto nivel que permite concentrarse en el modelo del dominio y enmascarar los
detalles técnicos de la persistencia en una base de datos.
Ejemplo de anotación
package entidad;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@Entity
public class Articulo {
@Id
private String code;
@ManyToOne(cascade = { CascadeType.PERSIST })
private Categoria categoria;
@Basic
private String designacion;
@Basic
private int cantidad;
@Basic
private double precio_unitario;
@Temporal(TemporalType.TIMESTAMP)
private Date fecha;
...
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
1. Connector/ODBC
ODBC (Open Database Connectivity) gestiona numerosos drivers que permiten establecer la comunicación entre
aplicaciones cliente y SGBD. No es la solución con mejor rendimiento pero presenta la ventaja de la simplicidad.
Por otra parte está disponible gratuitamente en prácticamente todas las plataformas.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/7
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
El nombre dado al Data Source Name debe ser explícito y no llevar espacios. Por comodidad para el
desarrollo Java que se lleva a cabo, conserve el mismo nombre propuesto a continuación.
Para el servidor, introduzca 127.0.0.1 o localhost en el campo TCP/IP Server. El DNS establece la
correspondencia entre el nombre y la dirección IP del servidor.
Para el User y el Password, consulte lo que indicó en la instalación de XAMPP: eni y java.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/7
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
2. Connector/J
Como se indica en el sitio web de MySQL, se trata del tipo de driver JDBC oficial para las bases de datos MySQL. Es
un driver JDBC de tipo 4.
En el sitio web de MySQL, solo está disponible la instalación mediante un archivo ejecutable para la versión
5.1.35. Descárguela desde la dirección https://dev.mysql.com/downloads/connector/j, e instálela.
Vaya a la carpeta C:\Program Files (x86)\MySQL\MySQL Connector J para obtener el driver JDBC.
La documentación y las fuentes de las clases del driver están también disponibles en esta carpeta.
Para evitar tener enlaces rotos, puede copiar el driver en una carpeta del proyecto (por ejemplo lib/).
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/7
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Haga clic en el botón Add External JARs, acceda a la carpeta que contiene el driver y añádalo.
Haga clic en la pestaña Order and Export, marque la opción correspondiente al driver y valide.
Drivers de tipo 2 y 3
Para instalar un driver de tipo 2 o 3, descargue y descomprima el archivo jar. Siga después los mismos pasos que
para el driver de tipo 4.
3. EclipseLink
EclipseLink es una de las implementaciones disponibles de JPA. Puede descargar los archivos binarios en la
dirección http://eclipse.org/eclipselink/downloads/.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/7
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
El proyecto contiene ahora todos los bloques necesarios para acceder a la base de datos.
Queda sin embargo configurar el proyecto para que sepa a qué base de datos acceder.
Cree una carpeta METAINF en src/main/resources haciendo clic con el botón derecho después de seleccionar
la carpeta src/main/resources, y seleccione New Folder. En Folder Name, teclee «METAINF» (sin
comillas dobles). Haga clic en Finish.
En esta carpeta, cree un nuevo archivo llamado «persistence.xml» (sin comillas dobles), seleccionándolo en
el explorador. Haga después un clic derecho y seleccione New File. Después teclee «persistence.xml» (sin
comillas dobles) en File Name. Haga clic después en Finish.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/7
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
La propiedad eclipselink.ddlgeneration configura JPA para suprimir y volver a crear las tablas en cada
ejecución. Es una propiedad muy útil en fase de desarrollo. ¡Sin embargo es primordial quitarla cuando la
aplicación está en producción!
Para probar el acceso a la base de datos con JPA, queda crear una clase TestJPA en el package test.
Cree una clase llamada TestJPA en el package test. Esta clase tendrá el siguiente contenido:
package test;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Id;
import javax.persistence.Persistence;
@Entity
public class TestJPA {
@Id
private long clavePrimaria;
@Basic
private String mensaje;
EntityManager em = fabrica.createEntityManager();
System.out.println(em.getProperties());
}
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 6/7
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Abra un navegador web en la dirección http://localhost/phpmyadmin. Puede ver en la base de datos «luna»:
Haciendo clic en el enlace Estructura de la tabla testjpa, puede ver que JPA ha creado correctamente las
columnas:
¡En resumen, JPA le ha permitido crear una tabla con sus columnas sin haber realizado directamente ninguna
consulta SQL!
Aunque si bien JPA permite evitar la codificación de la mayor parte de las consultas INSERT, UPDATE o DELETE,
será sin embargo necesario codificar las consultas de tipo SELECT.
Este comportamiento de JPA es interesante en el caso de la creación desde cero de una aplicación. En el caso del
acceso a una base de datos existente, JPA permite enlazar las propiedades de un objeto de dominio a columnas
precisas, pero no es el caso que interesa en este libro.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 7/7
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Como se anunció en el capítulo Análisis, se realizan las maquetas de la interfaz gráfica en primer lugar. Después se
efectuarán varias iteraciones sobre el proyecto para la entrega de la aplicación final.
Bienvenida: formulario de bienvenida que permite elegir entre los principales módulos.
La decoración «git maquetación» significa que se utiliza el sistema de gestión de versiones git y que la rama activa se
llama «maquetación». Se ha introducido brevemente la gestión de las versiones de los archivos de código fuente en el
capítulo La caja de herramientas de Eclipse. Se recomienda encarecidamente su uso si desea tener el control sobre sus
desarrollos y así poder gestionar el histórico de sus archivos de código fuente. Se trata también de una ayuda formidable
para trabajar en equipo.
Acceda a su espacio de trabajo con el explorador de archivos. Verá la siguiente arborescencia en su disco duro:
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Vuelva a Eclipse y seleccione la carpeta del proyecto luna. Presione sobre la tecla [F5] para refrescar los
recursos.
A continuación se presenta una vista global de los seis primeros formularios más importantes del proyecto.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Empiece creando las maquetas de estos formularios. Después se abordará la gestión de su visualización y cierre, sin
preocuparse en este punto de los tratamientos de los datos.
Se explicó en el capítulo Toma de contacto de Eclipse cómo crear formularios con WindowBuilder. Siendo el objetivo
del proyecto completamente didáctico, no se proporciona en este libro el código fuente completo (ya que el objetivo
es que se familiarice con la herramienta WindowBuilder) pero se incidirá sobre los puntos importantes respecto a la
construcción de la interfaz gráfica y el tratamiento de las acciones de los usuarios. También abordaremos los
bloques de código donde se realizan las llamadas a los métodos.
Intente crear las interfaces gráficas tomando como ejemplo las capturas de pantallas expuestas.
De vez en cuando, WindowBuilder no consigue volver a leer los archivos de código fuente. No es muy grave. Basta
con indicar a WindowBuilder que debe releer los archivos de código fuente con la ayuda del botón Reparse. Haga clic
varias veces en el botón si es necesario.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
1. Formulario de conexión
Todas las imágenes usadas para este formulario se encuentran en la subcarpeta conexion de la carpeta
imagenes.
Cree una nueva clase FConexion que herede de JFrame apoyándose en WindowBuilder. Para ello, utilice la
combinación de teclas [Ctrl] N, y seleccione la opción Window Builder Swing Designer JFrame en el
cuadro de diálogo que se abre. Sitúe esta clase en el package dialogo de la carpeta maquetas.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
La visualización es ligeramente distinta en WindowBuilder, ya que no tiene en cuenta todavía las pequeñas
mejoras que se aportarán a la interfaz gráfica.
Al norte de este layout (por lo tanto arriba) se encuentra un JLabel que indica la función del formulario.
Al sur de este layout (por lo tanto abajo) se encuentra un JPanel que contiene los botones que sirven para realizar
las acciones estándar.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
El panel de acciones tiene como layout un GridBagLayout. Se trata de uno de los layouts más potentes que
permite utilizar varios tipos de ubicaciones.
En particular, el botón de los parámetros tiene restricciones adicionales: toma el espacio disponible en el panel y
está anclado al inicio de la línea, es decir a la izquierda para los occidentales.
El botón Validar presenta algunas características: a diferencia de sus homólogos, la imagen está situada a la
derecha del texto y este texto está escrito en blanco.
WindowBuilder no proporciona ninguna acción gráfica para cambiar la posición de la imagen en relación al texto.
Debe por lo tanto codificarlo en la pestaña Source.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 6/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Haga clic en la pestaña Source del editor. Inserte la siguiente línea en el código de la declaración del botón:
btnValidar.setHorizontalTextPosition(SwingConstants.LEADING);
Los nombres de sus variables pueden ser diferentes de los del libro.
En el centro del contentPane encontramos los campos a introducir para la conexión. Está compuesto de un
primer JPanel. Este panel no tiene ningún interés: proporciona un borde vacío a su contenido, que consiste en un
único panel.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 7/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Este panel tiene un borde compuesto por dos bordes: un borde en forma de línea en su exterior y un borde vacío
de 5 píxeles en su interior para airear un poco su contenido.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 8/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
En este último panel se encuentran los componentes gráficos que sirven para la conexión: el nombre de usuario
que se introduce mediante un JTextField y la contraseña mediante un JPasswordField.
Para airear un poco más este formulario, cada componente tiene en sus parámetros de ubicación un objeto de
tipo Insets que permite configurar unos márgenes de 5 píxeles a su alrededor.
Al final, un componente JTextPane propone un pequeño mensaje en caso de que el usuario necesite ayuda
para rellenar los campos. Este componente no es editable y contiene un texto en varias líneas que podemos
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 9/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 10/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Existen varias maneras de modificar el aspecto de un componente gráfico. Todas implican modificar el código
generado por WindowBuilder. Las más destacadas son la gestión manual (que será tratada aquí) y la creación de un
Look and Feel específico.
package dialogo;
import java.awt.Color;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.SwingConstants;
public class UI {
quitarAspectoBoton(boton);
String nombre = "/imagenes/" + carpeta + "/" + icono
+ "‐" + size;
boton.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
boton.setIcon(getIcono(nombre
+ "‐activo.png"));
}
@Override
public void mouseExited(MouseEvent e) {
boton.setIcon(getIcono(nombre + ".png"));
}
});
boton.setHorizontalAlignment(SwingConstants.LEFT);
boton.setIcon(getIcono(nombre +".png"));
boton.setFont(boton.getFont().deriveFont(14f));
}
Esta clase contiene algunos métodos estáticos de los cuales los más interesantes para la creación de la interfaz
son:
UI.darAspecto() permite cambiar la tipografía de todos los componentes gráficos fácilmente (la fuente por
defecto de Java no resulta muy agradable).
UI.quitarAspecto() y sus variantes permiten cambiar el look por defecto de los botones para aplicarles algunas
modificaciones: el texto será de color blanco, no tendrá aspecto de botón clásico y las imágenes cambiarán
automáticamente cuando el ratón pase por encima del botón.
Esta clase funciona bien porque los nombres de las imágenes de los botones están normalizados.
setIconImage(UI.getLogo());
setTitle("Luna SA");
...
UI.darAspecto(lblContrasena);
...
UI.darAspecto(pwdContrasena);
...
UI.darAspecto(txtpnInfo);
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 12/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Cree una nueva clase FBienvenida que herede de JFrame con WindowBuilder.
Este formulario contiene un JPanel como contenido (un contentPane). Este panel tiene un BorderLayout como
layout.
Contiene también un JLabel que muestra el nombre de la empresa, así como un pequeño JLabel que indica la
acción que se efectuará cuando el puntero pasa por encima de los diferentes botones del formulario.
También dispone de una barra de menú para tener acceso estandarizado a los diferentes módulos.
En el centro del panel principal (restricción CENTER), un panel contiene todos los botones que permiten acceder a
los diferentes módulos de la aplicación.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 13/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Intente crear esta disposición con ayuda de un GroupLayout para el panel izquierdo. WindowBuilder
permite cambiar muy fácilmente la disposición de manera gráfica.
Los puntos importantes en el GroupLayout son la disposición relativa de los elementos y el espacio existente entre
ellos.
MigLayout no es una clase disponible de manera estándar en Java. Se basa en una descripción textual de líneas y
columnas. Su sintaxis es relativamente compleja pero WindowBuilder enmascara al máximo esta complejidad
permitiendo realizar pantallas espectaculares.
Ejecute el código de este formulario e intente redimensionarlo. El contenido debe adaptarse en casi todos los
tamaños excepto los más extremos.
Añada una barra de menú con ítems que permitan acceder rápidamente a los módulos.
package dialogo;
import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.SystemColor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 14/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import net.miginfocom.swing.MigLayout;
import dialogo.articulo.FArticulos;
import dialogo.cliente.PClientes;
import dialogo.pedido.FPedidos;
import dialogo.estadistica.FTablaBorde;
/**
* Create the frame.
*/
public FBienvenida() {
setIconImage(UI.getLogo());
setTitle("Bienvenida");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 926, 686);
setLocationRelativeTo(null);
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 15/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 16/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
btnSalir.setForeground(SystemColor.control);
btnArticulos.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
lblInfos.setText("Articulos");
}
@Override
public void mouseExited(MouseEvent e) {
lblInfos.setText(" ");
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 17/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
});
btnArticulos.setCursor(new Cursor(Cursor.HAND_CURSOR));
panel_principal.add(btnArticulos,
"cell 1 0,alignx center");
btnClientes.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
lblInfos.setText("Clientes");
}
@Override
public void mouseExited(MouseEvent e) {
lblInfos.setText(" ");
}
});
btnClientes.setCursor(new Cursor(Cursor.HAND_CURSOR));
panel_principal.add(btnClientes,
"cell 0 1,alignx center");
btnStats.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
lblInfos.setText("Estadísticas");
}
@Override
public void mouseExited(MouseEvent e) {
lblInfos.setText(" ");
}
});
btnStats.setIcon(new ImageIcon(
FBienvenida.class.getResource(
"/imagenes/bienvenida/Diagram‐128.png")));
btnStats.setCursor(new Cursor(Cursor.HAND_CURSOR));
panel_principal.add(btnStats,
"cell 1 1,alignx center");
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 18/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
btnPedidos.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
lblInfos.setText("Pedidos");
}
@Override
public void mouseExited(MouseEvent e) {
lblInfos.setText(" ");
}
});
btnPedidos.setIcon(new ImageIcon(
FBienvenida.class.getResource(
"/imagenes/bienvenida/Shopping‐Bag‐128.png")));
btnPedidos.setCursor(new Cursor(Cursor.HAND_CURSOR));
panel_principal.add(btnPedidos,
"cell 2 1,alignx center");
@Override
public void mouseExited(MouseEvent e) {
lblInfos.setText(" ");
}
});
btnParametros.setCursor(new Cursor(Cursor.HAND_CURSOR));
panel_principal.add(btnParametros,
"cell 1 2,alignx center");
SwingUtilities.invokeLater(
() ‐> btnSalir.requestFocusInWindow()
);
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 19/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
SwingUtilities.invokeLater(
() ‐> btnSalir.requestFocusInWindow()
);
Se trata de pedir a Java que ponga por defecto el foco en el botón Salir utilizando las expresiones lambdas de
Java 8.
Este formulario posee acciones que abren otros componentes gráficos: los botones Añadir, Modificar y Buscar
mostrarán cada uno una interfaz gráfica distinta.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 20/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Se podría mostrar entonces un nuevo formulario para cada una de estas acciones, pero el aspecto no sería muy
agradable: la apertura de un formulario puede ser perturbadora para un usuario y es preferible reservar este
comportamiento para los principales módulos únicamente.
Para conservar un único formulario para los clientes, se crean distintos JPanel con WindowBuilder. Estos paneles
se utilizan como panel de contenido en el formulario de clientes.
a. Panel principal
Cree el contenido de este panel con la ayuda de WindowBuilder inspirándose en la siguiente captura de
pantalla:
El gran espacio gris de este panel se hace con un JTable. Idealmente este mismo JTable debe estar ubicado en
un JScrollPane para gestionar el caso en el que el espacio no sea suficiente para mostrar todos los clientes al
mismo tiempo.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 21/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Los JTextField usados para la visualización de un cliente seleccionado en la tabla de abajo no están activados: su
propiedad enabled debe valer false. Lo mismo es válido para la casilla a marcar arriba a la derecha.
Cada una de las grandes categorías de información correspondiente al cliente se sitúa en un JPanel con bordes y
un título (Titled Border).
El campo que muestra la fecha de creación del cliente no es un JTextField sino un JFormattedTextField.
Para este último componente gráfico, es necesario crear una clase anexa que se encargue de dar formato y
convertir un objeto de tipo Instant en una cadena de caracteres y a la inversa. Este punto se abordará más
adelante en este libro.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 22/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Esta ventana integra un componente JToolBar que propone acciones disponibles relativas al artículo.
Para gestionar los clics en los botones radio y para crear la lógica de deselección de uno de estos botones cuando
el otro está seleccionado, hay que crear un ButtonGroup que los contenga con ayuda del siguiente código:
Es posible modificar el valor mostrado para la cantidad cuando se utiliza el slider con la ayuda del siguiente código:
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 23/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
rdbtnCodigo.addActionListener((ActionEvent e) ‐> {
System.out.println("Código seleccionado ? "
+ rdbtnCodigo.isSelected());
});
rdbtnCategoria.addActionListener((ActionEvent e) ‐> {
System.out.println("Categoría seleccionada ? "
+ rdbtnCategoria.isSelected());
});
sliderCantidad.addChangeListener((ChangeEvent e) ‐> {
String valor =
Integer.toString(sliderCantidad.getValue());
txtCantidad.setText(valor);
});
Los códigos anteriores implican transformar las variables gráficas locales en atributos de la clase FArticulos.
El código de la clase FArticulos está disponible para su descarga en el sitio web de Ediciones ENI.
Este formulario permite realizar la introducción de los pedidos. La validación provoca automáticamente la
impresión del pedido en curso.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 24/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Para estos tres últimos formularios, el código es similar al del formulario de gestión de clientes.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 25/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
El código completo de todas las maquetas del proyecto está disponible para su descarga en el sitio web de
Ediciones ENI.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 26/26
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Antes hay que profundizar en el concepto de evento visto en el capítulo La caja de herramientas de Java.
En Java, excepto los tipos primitivos, todo es un objeto. Los eventos también lo son. Más precisamente, son
instancias de clases cuyos nombres acaban por Event, por ejemplo ActionEvent, FocusEvent, HyperlinkEvent,
MenuEvent, etc. Java se encarga de numerosos eventos, repartidos por razones históricas entre el package
java.awt.event y javax.swing.event.
¿Pero quién crea el objeto evento? Dicho de otra manera, ¿cuál es la fuente que crea este objeto evento? Una
primera respuesta sería el usuario, que lo crea mediante un clic con el ratón o pulsando en una tecla del teclado. En
parte es verdad en el sentido en que el evento necesita de esta acción del usuario para ser creado.
Desde un punto de vista de programación, es en efecto el propio componente gráfico quien crea el objeto evento a
partir de una solicitud externa. Una vez el evento creado, se envía a uno o varios objetos especializados que están a
la escucha de este evento en particular. Por esta razón, se llama a estos objetos escuchadores o listeners en la
terminología Java.
Cada componente puede generar diferentes tipos de eventos, pero no todos los eventos se generan por todos los
componentes. Por ejemplo, las ventanas como Window, JFrame o JDialog podrán generar objetos
WindowEvent (para la apertura, el cierre,...). Un JButton no tiene ninguna utilidad en este tipo de evento, por el
contrario podrá generar eventos de acción, de tipo ActionEvent, cuando se presione. Estos ActionEvent serán
también generados por las instancias de clase JList o JMenuItem.
Para cada tipo de evento, existe una interfaz de escuchador (que incluye al menos un método y con frecuencia
varios). Java estandariza los nombres de estos escuchadores y son por lo tanto fácilmente identificables. Por
ejemplo, el nombre de la interfaz del escuchador de un evento ActionEvent se llama ActionListener; el listener
de un WindowEvent se llama WindowListener.
Concretamente, un escuchador es por lo tanto una instancia de una clase que implemente la interfaz deseada.
Estos escuchadores y sus clases debe crearlos usted mismo.
Queda añadir los escuchadores a los componentes gráficos. Por esta razón, estos poseen métodos llamados
addXXXListener y removeXXXListener. Así, un JButton tiene métodos addActionListener(ActionListener) y
removeActionListener(ActionListener) para permitir añadir y suprimir un escuchador en los eventos de tipo
ActionEvent. Ya los ha utilizado de manera implícita al crear interfaces gráficas con WindowBuilder.
Pasemos ahora al tratamiento de estos eventos. A más bajo nivel, debe crear por lo tanto una clase que
implemente la interfaz deseada, pero también necesita redefinir todos sus métodos (no todos serán
necesariamente útiles para la aplicación).
Aquí tiene un ejemplo de una clase que implementa la interfaz WindowListener, interfaz que posee siete
métodos.
@Override
public void windowIconified(WindowEvent evt) {
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
@Override
public void windowDeiconified(WindowEvent evt) {
}
@Override
public void windowDeactivated(WindowEvent evt) {
}
@Override
public void windowClosing(WindowEvent evt) {
}
@Override
public void windowClosed(WindowEvent evt) {
}
@Override
public void windowActivated(WindowEvent evt) {
}
}
Para no implementar los métodos que no tienen ningún interés para la aplicación, es posible recurrir a los
adaptadores. Son clases particulares llamadas XXXAdapter, que ya redefinen con una definición vacía todos los
métodos de las interfaces XXXListener. Así, a las interfaces ActionListener y WindowListener les corresponden
las clases adaptadas ActionAdapter y WindowAdapter.
En vez de crear una clase en un archivo aparte, también es posible crear lo que se llama una clase interna anónima
que extiende la interfaz deseada y proceder a su almacenamiento cerca del componente gráfico.
boton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
System.out.println("Botón accionado");
}
});
La última posibilidad es utilizar expresiones lambda para aligerar la sintaxis de creación de estas clases internas
anónimas.
boton.addActionListener(evt ‐> {
System.out.println("Boton accionado");
});
Para acabar con esta introducción a los eventos, es importante recordar que:
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
1. Conexión
Abra la clase FConexion.
Seleccione el botón Parámetros, haga un clic derecho y elija la opción Add event handler mouse
mouseClicked.
En el código creado automáticamente por WindowBuilder, añada la llamada a un método como sigue:
btnParametros.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
parametros();
}
});
Sitúese encima de la línea con el error: un mensaje con fondo amarrillo aparece entonces proponiéndole
diferentes soluciones. Elija la última opción Create method ’parametros()’ in type ’FConexion’.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/9
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Ponga el cursor sobre la línea con el error y presione las teclas [Ctrl] 1. Se trata de un QuickFix de Eclipse
que propone las mismas correcciones. Elija la opción Create method ’parametros()’ in type
’FConexion’.
Repita el procedimiento con el botón Salir, esta vez creando un método llamado salir().
Selecciónelo, haga clic con el botón derecho y elija la opción Set Action New.
El texto del botón cambia. También es normal. Todo volverá a su sitio en poco tiempo.
btnValidar.setAction(action);
Todo esto permite crear un sistema que reaccione al clic del botón.
Renombre la clase SwingAction por ActionValidar con la ayuda de la combinación de teclas [Ctrl] 1
después de seleccionarla en el código fuente. Elija la opción Rename in File.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/9
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
public ActionValidar() {
putValue(NAME, "Validar");
putValue(SHORT_DESCRIPTION,
"Conectarse a la aplicación");
}
public void actionPerformed(ActionEvent e) {
validar();
}
}
Traduzca los textos de la interfaz con la ayuda de Eclipse. Seleccione la clase en el explorador de packages,
haga un clic derecho y elija la opción Source Externalize Strings. Haga después lo mismo que se
describe en el capítulo La caja de herramientas de Eclipse para poner las traducciones en un archivo
messages.properties.
Es preferible gestionar las acciones de botones con clases derivadas de AbstractAction que añadir un
ActionListener sobre estos mismos botones.
Cree una clase Main en el package inicio, con un método main(). Será el punto de entrada principal de la
aplicación.
package inicio;
import java.awt.EventQueue;
import dialogo.FConexion;
/**
* Punto de partida de la aplicación.
*/
public class Main implements Runnable {
EventQueue.invokeLater(new Main());
}
@Override
public void run() {
FConexion frame = new FConnexion();
frame.setLocationRelativeTo(null);
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/9
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
frame.setVisible(true);
El método dispose() de un JFrame libera los recursos gráficos. Como se trata de un único formulario mostrado
en este momento, la aplicación se termina.
Codificaremos un doble control a la vez de introducción y de conexión en el método validar(). Se advertirá así al
usuario de un eventual problema de acceso a la base de datos. Si la introducción es incorrecta, aparece un nuevo
mensaje desde la clase FConexion.
Este código realiza las siguientes operaciones: si la combinación usuario/contraseña es válida, cierra la ventana de
conexión (FConnexion.this.dispose() es equivalente a dispose()), crea un formulario de bienvenida y lo
muestra con setVisible(true).
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/9
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
En caso contrario, se muestra un cuadro de diálogo de error al usuario y la ventana de conexión queda en
pantalla para realizar un nuevo intento.
NombreDeLaClase.this es una notación Java para encontrar la instancia de la clase principal en el caso de una
clase interna.
2. Clientes
Abra la clase PClientes.
Ponga en marcha las acciones para todos los botones del panel con la ayuda de la opción Set Action New
del menú contextual.
Estos métodos permiten respectivamente fijar y encontrar el formulario activo en el panel y modificar el panel del
contenido principal.
Añada una acción que servirá a los paneles de añadir y buscar para que puedan volver a mostrar el panel
principal.
Complete el método actionPerformed del escuchador del botón Añadir con el siguiente código:
Este código permite cambiar el panel del contenido por el panel para añadir un cliente.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/9
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
btnAñadir.setIcon(new ImageIcon(
PClientes.class.getResource(
"/imagenes/gestion/Add‐New‐48.png")));
cambiarPanel(añadir ,"Añadir un nuevo cliente");
Complete el método actionPerformed del escuchador del botón Buscar con el siguiente método:
btnBuscar.setIcon(new ImageIcon(
PClientes.class.getResource(
"/imagenes/gestion/Search‐48.png")));
cambiarPanel(busqueda, "Búsqueda de cliente(s)");
}
3. Bienvenida
Abra la clase FBienvenida.
Añada los escuchadores de eventos ActionListener en los diferentes botones para responder a los clics del
usuario, como en el formulario de conexión.
Para cada uno de los botones, añada la gestión de eventos (event handler) para mostrar el nombre de la
acción cuando pasa por encima con el ratón.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 6/9
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
FBienvenida.class.getResource(
"/imagenes/bienvenida/People‐128‐activo.png"
)));
UI.quitarAspectoBoton(btnClientes, "bienvenida", "People", 128);
btnClientes.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
mostrarClientes();
}
});
btnClientes.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
lblInfos.setText("Clientes");
}
@Override
public void mouseExited(MouseEvent e) {
lblInfos.setText(" ");
}
});
btnClientes.setCursor(new Cursor(Cursor.HAND_CURSOR));
panel_principal.add(btnClientes, "cell 0 1,alignx center");
lblInfos es el nombre en el panel de la izquierda que muestra la acción que se efectuará. Este nombre puede ser
diferente en su código.
La línea siguiente:
btnClientes.setCursor(new Cursor(Cursor.HAND_CURSOR));
permite mostrar un cursor con forma de mano cuando el ratón pasa por encima del botón.
Esto implica crear acciones que gestionen los clics en estos ítems.
A continuación, puede ver el código de la acción que gestiona la visualización del módulo de artículos.
public ActionArticulos() {
putValue(NAME, "Articulos");
putValue(SHORT_DESCRIPTION,
"Mostrar la lista de artículos");
putValue(ACCELERATOR_KEY,
KeyStroke.getKeyStroke(
KeyEvent.VK_A,
ActionEvent.ALT_MASK));
putValue(MNEMONIC_KEY, KeyEvent.VK_A);
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 7/9
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Cree un método setFormulario() en el archivo PClientes que tenga como parámetro un diálogo:
Este código crea un nuevo cuadro de diálogo cuyo padre es el formulario actual, sitúa el panel de clientes en él,
almacena el diálogo en el panel de clientes, inicializa algunas propiedades como el título y el icono y muestra este
cuadro de diálogo.
Seleccione el botón Salir y añada una nueva acción haciendo clic con el botón derecho y la seleccionando la
opción Set Action New.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 8/9
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
public ActionSalir() {
putValue(NAME, "Salir");
putValue(SHORT_DESCRIPTION, "Salir de la aplicación");
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 9/9
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Introducción
Se han creado las principales maquetas entre las cuales encontramos el formulario de conexión y se ha tratado la
gestión de eventos en el capítulo Maquetas. En este capítulo se trata de poner en marcha todos los controles
necesarios antes de autorizar el acceso a la aplicación. Simultáneamente, hay que gestionar el acceso al servidor y
a la base de datos teniendo en cuenta los posibles problemas de conexión.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Parámetros de conexión
JPA provee un mecanismo general de configuración de la conexión a la base de datos. Este mecanismo se apoya en
la presencia de un archivo particular en un sitio determinado. Se ha realizado la primera etapa, vista en el capítulo
Base de datos MySQL, creando este archivo y rellenándolo con los datos de conexión a la base de datos. Se
completará a medida que se avance en el proyecto.
Pruebe el acceso a la base de datos con la clase TestJPA que se codificó en el capítulo Base de datos MySQL,
importándola en el proyecto Luna.
Para hacerlo sencillo y rápido, el nombre del usuario y la contraseña son únicos para todos los usuarios de la
aplicación. Los parámetros correspondientes al driver, el servidor y la base de datos vienen de la instalación
realizada para el SGBD MySQL (consulte el capítulo Base de datos MySQL), así como los datos de identificación.
En una aplicación más realista, sería posible crear tablas de usuarios y configurar para cada uno una combinación
usuario/contraseña única y un identificador de acceso único a la base de datos. Otra posibilidad sería gestionar los
usuarios mediante la propia base de datos, encargándose la aplicación de inyectar en la configuración los datos de
conexión provistos por el usuario. También es posible conectarse a un directorio LDAP o usar cualquier otro método.
Se crea ahora una clase que permite centralizar el acceso a base de datos. Esta clase contiene también los métodos
útiles para el resto de la aplicación.
Complete la estructura del proyecto añadiendo los packages llamados ”entidad” y ”control” a la carpeta
src/main/java:
Para conservar una organización clara y estructurada de nuestras clases, añada un subpackage llamado
«connection» al package control (no olvide el punto de separación).
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Después cree una clase Conexion con la ayuda del menú File New Class, o seleccionando el package
connection y haciendo clic con el botón derecho y eligiendo New Class. Informe el nombre de la clase,
verifique que el nombre del package es correcto y haga clic en Finish.
package control.connection;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
Las clases del package javax.persistence permiten acceder a la base de datos y a sus datos.
El código de la clase TestJPA permite formarse una idea previa de la manera de codificar con JPA:
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
cerrar la fábrica.
Las primeras y últimas etapas que están en relación con la fábrica deberían realizarse respectivamente al inicio y al
final del ciclo de vida de la aplicación.
La clase Conexion conservará por lo tanto la referencia de una fábrica para cerrarla y liberar los recursos
asociados.
Para respetar el principio de encapsulación, este atributo se declara private. Se aisla su acceso, por lo tanto no
tiene ni accesor, ni mutador.
Para proteger la modificación de esta propiedad que es vital para la aplicación, se marca como final, lo que impide
toda modificación una vez que se inicializa el atributo.
Declarar sus atributos static: las instancias de la clase no poseerán estos atributos. El acceso se realiza entonces
según la sintaxis clase.accesor.
La manera de escribirlo es una cuestión de elección: personal o de acuerdo con el equipo de proyecto.
Después hay que inicializar esta propiedad fabrica. Una propiedad final solo se puede inicializar de dos maneras:
en su declaración o en la construcción del objeto. Optamos por inicializarla en el constructor.
Conexion(){
fabrica = Persistence.createEntityManagerFactory("eni‐acces");
}
El interés de utilizar este método es poder proveer varias unidades de persistencia en función de las necesidades.
Entonces es posible declarar una unidad de persistencia específica al desarrollo, donde se eliminan y recrean las
tablas y donde se utiliza una base de datos específica. Se utiliza otra unidad de persistencia para la producción y
otra para los test. ¡Esto permite evitar modificar o eliminar definitivamente los registros vitales de la producción!
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Conexion(String unidad){
fabrica = Persistence.createEntityManagerFactory(unidad);
}
Se asegura el enlace con la base de datos mediante esta propiedad de tipo EntityManagerFactory, gracias a objetos
de tipo EntityManager y a la ayuda que presta el método de instancia createEntityManager.
Se proporciona la clase EntityManager en javax.persistence, que es el punto de acceso que permite crear, leer,
modificar y eliminar los datos de la base de datos con las clases del dominio.
El método público estático getConexion() permite después recuperar el objeto que servirá para toda la aplicación.
Este método devuelve justo el contenido de la propiedad privada estática y final conexion. Esta última propiedad
es estática para permitir al método público estático acceder a ella. Es privada, así nada ni nadie puede acceder por
otros medios salvo el método público. Es, por último, final para evitar cualquier tentativa de modificación.
El objeto de tipo Conexion enmascara por lo tanto al máximo los detalles técnicos de acceso a la base de datos, lo
que permite facilitar cambios eventuales en el código.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
La clase FConexion es una HMI. Se contenta con transmitir la solicitud del usuario a la clase Conexion para la
conexión a la base de datos.
import control.connection.Conexion;
por :
Por razones de seguridad en el uso de la clase JPasswordField, Java hace una distinción entre las cadenas de
caracteres declaradas con la clase String y los arrays de caracteres.
// aceptado
char[] vChar = pwdContrasena.getPassword();
// rechazado
String laContrasena = pwdContrasena.getPassword();
Hay que convertir el array de caracteres en una cadena de caracteres, conversión realizada con el método estático
valueOf() de la clase String.
laContrasena = String.valueOf(pwdContrasena.getPassword());
Estos controles se efectúan de dos maneras: si la introducción del usuario/contraseña es incorrecta, el método
control() de la clase Conexion devolverá false. Se trata de un error a fin de cuenta muy corriente, que puede
por lo tanto gestionarse sencillamente con un booleano. Por el contrario, si aparecen problemas de conexión a la
base de datos, estos problemas deben ser excepcionales. El recurso a un tratamiento de estos errores por excepción
es una manera elegante de conseguirlo.
Para ello, Java permite encapsular las instrucciones críticas del programa en un bloque try/catch.
A la palabra clave try siempre le sigue un bloque de instrucciones que definen el tratamiento a ejecutar. Si este
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
fracasa, se crea o «eleva» un error o excepción. En este caso, se tiene en cuenta o «se atrapa» la excepción y se
trata en el bloque catch que siempre está situado inmediatamente después del bloque try.
Existen variantes a la gestión de excepciones. Por ejemplo, se puede indicar que un tratamiento es susceptible de
lanzar una excepción usando la palabra clave throws en la declaración del método.
La gestión de errores se hace aquí en dos tiempos. Primero nos preocupamos del driver para el SGBD MySQL. Si no
encontramos ninguna excepción, una variable booleana indica que se puede efectuar el segundo tratamiento. Este
código permite proponer mensajes personalizados en función de la excepción elevada.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
} catch (Exception e) {
// ha ocurrido una excepción totalmente desconocida
JoptionPane.showMessageDialog(null,
"Error desconocido " +e.getMessage(),
Messages.getString("FConnexion.32"),
JOptionPane.ERROR_MESSAGE); //$NON‐NLS
}
}
Object nombreUsuarioCorrecto =
administrador.getProperties().get(
"javax.persistence.jdbc.user");
Object contrasenaCorrecta =
administrador.getProperties().get(
"javax.persistence.jdbc.password");
verificacionIntro =
nombre.equals(nombreUsuarioCorrecto)
&& contrasena.equals(contrasenaCorrecta);
administrador.close();
return verificacionIntro;
}
Puede afinar todavía más el control de la introducción de datos verificando si el usuario por lo menos ha introducido
los campos necesarios y precisando si el error proviene del nombre o de la contraseña. Este proceso de afinar debería
probablemente situarse en la clase FConexion.
Ahora queda ocuparse de la salida de la aplicación y cerrar correctamente la conexión a la base de datos.
La ventana de conexión está operacional. Pruebe la aplicación modificando los parámetros para provocar
voluntariamente errores. Por ejemplo, puede quitar temporalmente la librería del conector MySQL del Build Path, o
modificar la dirección del servidor o el nombre de la base de datos. ¡La profesión de desarrollador informático
consiste principalmente en gestionar todos estos errores para crear programas robustos!
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/3
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Primero añada un método toString() a esta clase usando la opción Source Generate toString()... del
menú contextual. Permitirá tener más comodidad para visualizar los datos.
@Override
public String toString() {
return "TestJPA [clavePrimaria=" + clavePrimaria
+ ", mensaje=" + mensaje
+ "]";
}
em.remove(test3);
// finalizar la transacción
transaccion.commit();
Para efectuar una operación de escritura hacia la base de datos, JPA debe obligatoriamente encontrarse en una
transacción activa. Esta transacción se recupera con el método getTransaction() de la clase EntityManager.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Una transacción representa un conjunto de operaciones enlazadas que funciona automáticamente: si no es posible
realizar alguna de las operaciones y ocurre un error, se considera que el conjunto de las operaciones ha fracasado.
Entonces es posible volver atrás y eliminar las operaciones de la transacción como si no pasara nada: se habla en
este caso de rollback.
Para efectuar operaciones de escritura, se arranca la transacción con el método begin(), y se indica al
administrador de entidades que debe guardar nuevos datos gracias al método persist(). Estos datos no se escriben
inmediatamente en la base de datos. Varias operaciones pueden así realizarse en una misma transacción: modificar
otros datos, eliminar algunos, …
Una vez que se han ejecutado todas las operaciones deseadas, el método flush() solicita al administrador de
entidades que envíe todos los datos modificados a la base de datos. Se hace después un commit de la transacción
activa y se cierra con el método commit().
Estos dos últimos métodos realizan aproximadamente la misma acción. La diferencia es que commit() envía todos
los cambios y cierra la transacción mientras que flush() envía los cambios pero mantiene la transacción activa: a
veces es ventajoso empezar a escribir datos en la base de datos a mitad de la transacción a la que se hará el
commit más tarde.
Una vez los datos guardados en la base de datos, hay que leerlos.
...
// los datos están ahora en la base de datos
TestJPA encontrado = em.find(TestJPA.class, Long.valueOf(1));
System.out.println("encontrado: " + encontrado);
La primera línea permite encontrar directamente un registro en la base de datos si se conoce su identificador (o
clave primaria). El administrador de entidades de JPA permite entonces encontrar directamente este registro
mediante un objeto de dominio con la ayuda del método find().
Para recuperar varios datos con criterios arbitrarios, es posible efectuar una consulta a la base de datos. Para ello
basta con crear un objeto Query a partir del administrador de entidades llamando al método createQuery() que
recibe como parámetro una consulta con formato de texto, y recuperar los resultados desde la consulta con la
ayuda del método getResultList().
Basándose en lo aprendido con la clase TestJPA para acceder a los datos, se modifica la clase Conexion para
introducir buenas prácticas en el código.
La clase Conexion no puede conocer todas las operaciones a efectuar. En un determinado estado de avance en la
codificación de las funcionalidades, sería cada vez más difícil mantenerlo y hacerlo evolucionar. Sin embargo, es
posible hacer que el código que llama al método se encargue de las especificidades (consultas, modificaciones,...)
permitiendo a la vez asegurar que las reglas de acceso al EntityManager se respeten (cierre, inicio y commit de las
transacciones entre otras cosas).
Para ello, se crean dos métodos. Estos dos métodos resumen las principales acciones a efectuar sobre los datos: la
aplicación los buscará y los modificará. Será también la oportunidad de poner en práctica algunas técnicas descritas
en los capítulos anteriores.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
try {
} finally {
administrador.close();
}
}
Este método crea un administrador de entidad desde la fábrica y lo cierra al final de una operación de búsqueda.
Para ello, se añade un bloque finally en el bloque try/catch: significa que sea cual sea el desarrollo de
operaciones, el código dentro del bloque finally se ejecutará. Esto permite asegurar que los recursos del
administrador se liberan limpiamente. Si se produce alguna excepción durante la ejecución, el código que la invoca
la recuperará y será el responsable de su gestión.
¿Por qué hacerlo así? Buscando un poco en Internet, y leyendo la documentación de las clases
EntityManagerFactory y EntityManager, puede percatarse de que la clase EntityManagerFactory es thread
safe, mientras que la clase EntityManager no lo es. Por lo tanto no es razonable tener un atributo de tipo
EntityManager en la clase Conexion: los riesgos de corrupción o incoherencia de los datos serían demasiado
grandes.
Hay que racionalizar los accesos a los EntityManager. La manera más sencilla es tener únicamente variables locales
de este tipo: una vez que se ejecuta el método, el objeto EntityManager ya no es accesible. Además, los objetos
de este tipo son ligeros: crearlos no resulta computacionalmente (en recursos y tiempo de ejecución) hablando
pesado.
Esta manera de trabajar tiene una consecuencia directa: una vez que se cierra el administrador de entidades, las
entidades leídas se consideran como liberadas; ya no están conectadas a la base de datos y no se actualizarán más en
caso de modificación. Estas entidades se convierten entonces en simples estructuras que contienen los datos leídos.
Hay que usar este EntityManager y esto solo se puede hacer dentro del bloque try.
Como recordatorio, no se trata de crear todas las maneras posibles de buscar datos en la clase Conexion. El
número de posibilidades ya es considerable en esta aplicación, y solo se puede multiplicar.
Se crea una manera genérica de trabajar: el problema es ejecutar código dentro del bloque try, con el
EntityManager como parámetro y devolver resultados al código que llama el método. La interfaz
java.util.function.Function responde exactamente a esta problemática: se trata de una interfaz funcional que
tiene un único método apply(), que recibe un objeto como parámetro y devuelve un objeto como resultado.
Modifique el método buscar() para introducir un parámetro de tipo Function y devolver un resultado.
La interfaz Function es genérica: es posible declarar los tipos de parámetros y del resultado.
Como el tipo de resultado no es conocido en este punto, se declara como genérico a su vez.
Para usarlo, bastará por ejemplo con escribir el siguiente código utilizando expresiones lambda:
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
o bien:
R resultado = funcion.apply(administrador);
transaccion.commit();
return resultado;
} finally {
administrador.close();
}
}
No se gestionan los rollbacks en este ejemplo. Un rollback es una operación de la transacción que consiste en volver
atrás para obtener de nuevo un conjunto de datos coherente si la transacción ha fracasado.
¡Y es casi todo! JPA simplifica enormemente los accesos a la base de datos. Anteriormente, era necesario escribir
mucho código para crear una conexión con JDBC, cambiar las clases del driver MySQL, configurar este acceso
programáticamente,... JPA se encarga de todas estas etapas siempre y cuando se provea el archivo correcto de
persistencia.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
La técnica de codificación de la clase Conexion permite enmascarar los detalles técnicos del acceso a la base de
datos: únicamente esta clase puede crear objetos de tipo EntityManager o EntityManagerFactory.
Esto evita al desarrollador estar pendiente de la transacción. Por el contrario implica que tenga cuidado en la
utilización del parámetro EntityManager. En este punto, puede ser interesante empezar con un framework
especializado como Spring, PicoContainer o Guice.
Puede ahora eliminar la clase TestJPA ya que su rol de acceso a la base de datos se ha terminado. Ya no servirá en
el resto de nuestro proyecto.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Introducción
Se trata en este capítulo de crear las clases para las entidades ModoPagos, Cliente, Articulo y Pedido que
representan el modelo de dominio principal resultante del análisis del proyecto.
Las entidades tienen dos vertientes funcionales: una describe los datos a manipular, la otra las operaciones que
permiten su interacción con la base de datos.
Este segundo aspecto concierne principalmente a las operaciones CRUD (Create, Read, Update, Delete, es decir
Crear, Leer, Modificar, Eliminar) que permiten respectivamente realizar operaciones de creación, lectura,
actualización y eliminación de registros de la base de datos. A lo largo de este capítulo se podrá ver cómo
codificarlas poco a poco.
Es tentador mezclar las operaciones CRUD en el interior de la clase de datos. Esto plantea varios problemas: las
responsabilidades funcionales se mezclan entonces; el código resultante se encuentra muchas veces con artificios
técnicos; el aspecto pedagógico se diluye y ¡el objetivo de este libro es ante todo pedagógico!
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
En lo referente a la base de datos, estos errores tomarán la forma de excepciones lanzadas por JPA cuando se
llame a los métodos de la clase EntityManager. Estos errores heredan de la clase RuntimeException, no forman
por lo tanto parte de la firma de la clase, pero son aun así susceptibles de ocurrir. En vez de gestionarlos a través
de un bloque try/catch en esta parte de código, un capítulo posterior explicará cómo será responsabilidad de los
métodos que las llaman el gestionarlos.
Esta manera de trabajar simplifica la legibilidad del código, y contribuye a una mejor separación de las tareas: ¡no
es normal para una clase técnica mostrar cuadros de diálogo informando de estos errores!
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Clase ModoPagos
Se empieza por la clase ModoPagos. Es la más sencilla de nuestras modelizaciones, y está en el corazón de la
aplicación. ¡Después de todo, se trata de una aplicación de gestión de pedidos, y sin pagos esta aplicación no serviría
de nada en una empresa privada!
package entidad;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Transient;
@Entity
public class ModoPagos implements Serializable {
@Id
@GeneratedValue
private int codigo;
@Basic
private String tipo;
@Transient
private transient boolean ignorado;
/*
* Constructor 1.
* Utilizado por JPA.
*/
public ModoPagos(){
super();
}
/*
* Constructor 2
* Para la gestión de pedidos
*/
public ModoPagos(String tipo){
this();
this.tipo = tipo;
}
/*
* Accesores
*/
public int getCodigo() {
return this.codigo;
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
}
public String getTipo() {
return this.tipo;
}
/*
* Mutadores
*/
public void setTipo(String tipo) {
this.tipo = tipo;
}
/**
* Describe el modo de pago
* de manera textual.
*/
@Override
public String toString() {
return tipo;
}
}
Existe una anotación a nivel de la clase: @Entity. Permite a JPA darse cuenta de las instancias de esta clase
corresponden a líneas en la base de datos para la tabla cuyo nombre es el de la clase por defecto.
Esta clase contiene un código, que es el identificador técnico que permite crear relaciones (este identificador se
llama también clave primaria) y un tipo que es la descripción textual del modo de pago.
JPA permite relacionar estas dos propiedades con columnas de la base de datos.
La propiedad codigo posee una anotación @Id. Permite indicar que la clave primaria de la fila se guardará en esta
propiedad. Corresponderá al valor presente en la columna CODIGO, es decir el nombre de la propiedad por defecto.
También posee una anotación @GeneratedValue, para indicar que es la base de datos la que proporciona este
valor en una inserción de un registro en la tabla de la base de datos.
La propiedad codigo no tiene un mutador, ya que la base de datos fija el valor de esta propiedad.
La propiedad tipo está también marcada por una anotación: @Basic. Esta anotación es opcional (por defecto todas
las propiedades se consideran con un equivalente en base de datos).
Estas dos propiedades son privadas en el interior de la clase y los accesores y mutadores permiten acceder y
modificar a sus valores.
Se anota la propiedad ignorado con @Transient. Este atributo no será por lo tanto mapeado por JPA y no tendrá
su equivalencia en la base de datos.
La clase está marcada con la interfaz Serializable. Esta marca permite a la aplicación transformar las instancias de
ModoPagos en formato binario y enviarlas a otras aplicaciones.
Este mecanismo no sirve en la aplicación actualmente, pero se considera como una buena práctica marcar las clases
de entidades como que implementan la interfaz Serializable.
Para conservar la misma semántica para el mecanismo de serialización, la palabra clave transient se añade también a
la propiedad ignorado.
Por último, se crea un constructor por defecto. Es obligatorio tener este constructor ya que JPA lo utiliza para crear
un objeto de dominio recuperado de las lecturas en base de datos.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/2
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
package entidad.crud;
import java.util.Collections;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.swing.JOptionPane;
import control.connection.Conexion;
import entidad.ModoPagos;
/**
*
*/
private final Conexion laConexion;
/**
* Constructor
*/
public ModoPagosCrud(Conexion unaConexion) {
laConexion = unaConexion;
}
}
Esta clase posee un constructor con un parámetro: la instancia de Conexion. Esto evita utilizar métodos estáticos
durante el ciclo de vida de la aplicación y mejora las posibilidades de test unitarios de esta clase.
El CRUD debe poder crear, leer, modificar y eliminar una línea de datos. Se añadirá una posibilidad de búsqueda a
este conjunto de funcionalidades.
1. Crear
Cree un nuevo método en la clase.
/**
* Añadir un nuevo elemento en la base de datos.
*
* @param tipo
* el nombre textual del modo de pago.
* @return el modo de pago creado
* o null si no se puede crear.
*/
public ModoPagos crear(String tipo) {
return laConexion.modificar((administrador) ‐> {
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
if ("".equals(tipo)) {
throw new IllegalArgumentException(
"El nombre del modo de pago es obligatorio");
} else {
ModoPagos pagos = new ModoPagos(tipo);
administrador.persist(pagos);
return pagos;
}
});
}
Su rol principal es crear un objeto ModoPagos con la información indicada en los parámetros. La clase Conexion
se encarga de crear el administrador de entidades, arrancar la transacción, llamar a la función pasada como
parámetro y finalizar la transacción.
Este método verifica también el valor del parámetro con la ayuda de la siguiente instrucción:
if ("".equals(tipo)) {
...
} else {
...
}
y lanza una excepción específica para prevenir al método que hace uso de este método de un valor no conforme.
Se invierte el test respecto a lo que puede ver generalmente; type.equals(""). Esta inversión permite no preocuparse
del caso eventual de un parámetro tipo no incializado y por lo tanto evitar los NullPointerException.
2. Leer
Cree un nuevo método en la clase.
/**
* Lectura de registros.
*
* @return la lista de los modos de pago
* o una lista vacía si no existe ninguno.
*/
public List<ModoPagos> leer() {
return laConexion.buscar(
(administrador) ‐> {
Query consulta = administrador.createQuery(
"SELECT m FROM ModoPagos m");
return consulta.getResultList();
});
}
En el caos de la lectura, no es necesario utilizar una transacción, el método llama por lo tanto simplemente a
buscar() de la clase Conexion.
Después se crea una consulta a partir de este administrador, con una descripción textual de esta consulta.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Esta descripción no está escrita en SQL sino en JPQL, que es la versión orientada a objetos de SQL. En vez de
escribir "SELECT * FROM modopagos", que es la versión SQL «clásica», recuperamos los objetos en JPQL a
partir de la clase: en "SELECT m FROM ModoPagos m", ModoPagos corresponde a la clase que se quiere
obtener.
3. Modificar
Cree dos nuevos métodos en la clase.
/**
* Modificación de un regitro.
*
* @param codigo
* la clave primaria del modo de pago.
* @param tipo
* la nueva descripción del modo de pago.
* @return true si se ha modificado el modo, en caso contrario false.
*/
public boolean modificar(int codigo, String tipo) {
return laConexion.modificar((administrador) ‐> {
boolean modificado = false;
if ("".equals(tipo)) {
throw new IllegalArgumentException(
"El nombre del modo de pago es obligatorio");
} else {
ModoPagos modo = buscar(codigo);
if (modo != null) {
modo.setType(tipo);
modificado = true;
}
}
return modificado;
});
}
/**
* Busca un modo de pago específico.
*
* @param codigo
* la clave primaria del modo de pago.
* @return el modo de pago.
*/
private ModoPagos buscar(int codigo) {
return laConexion.buscar((administrador) ‐> {
ModoPagos modo = administrador.find(ModoPagos.class, codigo);
return modo;
});
}
Después de controlar los parámetros de entrada, el método modificar buscará en la base de datos el modo de
pago que corresponde al código indicado. Si el objeto devuelto no es null, esto quiere decir que el modo existe,
entonces se modifica dentro de la transacción. Cuando termina la transacción, se ha modificado el dato
correspondiente en la base de datos.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
El método buscar utilizará el método find de la clase EntityManager para encontrar directamente el objeto
correspondiente a la clave primaria pasada como parámetro.
4. Buscar
Cree un nuevo método en la clase.
/**
* Búsqueda de un pedido.
*
* @param busqueda
* una parte del tipo de modo de pago a buscar.
* @return la lista de modos de pago
* o una lista vacía si no existe ninguno.
*/
public List<ModoPagos> buscar(String busqueda) {
return laConexion.buscar((administrador) ‐> {
La búsqueda consiste en crear una consulta global con parámetros y devolver los resultados.
Si ningún registro corresponde a la consulta, JPA devuelve una lista vacía como resultado.
La consulta textual se construye simplemente por concatenación de cadenas de caracteres para obtener una
consulta parametrizada. Se verán varias técnicas más adelante en el capítulo para aligerar esta construcción.
No utilice nunca el operador += para construir una cadena de caracteres: ¡se trata de una de las técnicas de
construcción menos eficaces!
Esta consulta es una consulta de selección, como la que se codificó para la lectura, pero con una cláusula WHERE
que indica qué criterios adicionales deben aplicarse. En esta cláusula WHERE, un criterio LIKE permite buscar un
patrón determinado.
En la construcción de esta consulta, el formateo del parámetro es importante: debe estar entre dos comillas
simples o apóstrofes (’) y entre dos signos de porcentaje (%). Las comillas simples son obligatorias para que la
base de datos entienda que el parámetro es una cadena de caracteres. El porcentaje sirve aquí para reemplazar
todos los demás caracteres.
Si el parámetro del criterio LIKE es ’%a’, la consulta busca todos los tipos que acaben por la letra a.
Si el parámetro del criterio LIKE es ’a%’, la consulta busca todos los tipos que empiecen por la letra a.
Si el parámetro del criterio LIKE es ’%a%’, la consulta busca todos los tipos que contengan la letra a.
5. Eliminar
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
/**
* Supresión de un registro.
*
* @param codigo
* la clave primaria del modo de pago.
* @return true si se suprime el modo, false en caso contrario.
*/
public boolean eliminar(int code) {
return laConexion.modificar((administrador) ‐> {
boolean modificado = false;
ModoPagos modo = buscar(codigo);
if (modo != null) {
administrador.remove(modo);
modificado = true;
}
return modificado;
});
}
El método eliminar buscará en la base de datos el registro correspondiente al código con la ayuda del método
buscar visto anteriormente.
Si el modo de pago existe, se llama al método remove de la clase EntityManager. Al final de la transacción, se
elimina el registro correspondiente en la base de datos.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Clase Cliente
Aquí tiene el código de esta clase:
package entidad;
import java.io.Serializable;
import java.time.Instant;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@Entity
public class Cliente implements Serializable {
@Basic
private String apellido;
@Temporal(TemporalType.DATE)
private Date fecha;
@OneToOne(cascade=CascadeType.ALL)
private Direccion direccion;
// CONSTRUCTORES
// ‐‐‐‐‐‐‐‐‐‐‐‐‐
// 1er Constructor
// para la creación completa de un cliente
// limitado aquí a 5 propiedades para aligerar el código
public Cliente(String codigo,
String apellido, String nombre,
boolean tarjetaFidelidad, Instant creacion) {
this.codigo = codigo;
this.apellido = apellido;
this.nombre = nombre;
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
this.tarjeta_fidelidad = tarjetaFidelidad;
setFechaCreacion(creacion);
}
/**
* Usado por JPA.
*/
public Cliente() {
super();
}
// Getters básicos
// ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
public String getCodigo() {
return this.codigo;
}
// Setters
public void setCodigo(String codigo) {
this.codigo = codigo;
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
this.fecha = Date.from(fecha_creacion);
}
Al contrario que para la clase ModoPagos, la propiedad código no posee la anotación @GeneratedValue. Esto
quiere decir que deberá asignar este código manualmente, ya que la base de datos no lo gestiona
automáticamente.
Se almacena la fecha de creación del cliente en el sistema. JPA utiliza la anotación @Temporal para convertir
automáticamente el objeto Date al formato SQL DATE, con la ayuda de la indicación adicional: la variable
TemporalType.DATE.
En el momento actual, la versión 2.1.0 de JPA no gestiona las propiedades de tipo time de Java 8. No es posible por
lo tanto declarar atributos de tipo java.time.Instant o java.time.LocalDate. Solo gestiona los tipos java.util.Date o
java.util.Calendar nativamente.
Por lo tanto es conveniente hacer la transformación deseada en el código con ayuda de los métodos:
y:
Una vez introducido este código, la información se convierte automáticamente y se almacena en la base de datos
gracias a JPA.
La clase Cliente establece una relación con una instancia de la clase Direccion. Se considera cada cliente en la
aplicación con una dirección como máximo, y cada dirección corresponde a un solo cliente. Esta relación se
formaliza con la anotación @OneToOne. Esta anotación se parametriza después con cascade, que indica que en
una operación sobre una entidad de la clase Cliente, la dirección enlazada se verá también afectada por esta
operación.
package entidad;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Direccion implements Serializable {
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
@Id
@GeneratedValue
private Long id;
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
package entidad.crud;
import java.util.Collections;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.swing.JOptionPane;
import control.connection.Conexion;
import entidad.Cliente;
// CONSTRUCTOR
// ‐‐‐‐‐‐‐‐‐‐‐‐‐
public ClienteCrud(Conexion conexion) {
this.laConexion = conexion;
}
}
1. Crear
Cree un nuevo método en la clase.
A diferencia de ModoPagosCrud, este método recibe un cliente completo como parámetro, y no devuelve nada. Si
termina con normalidad, se ha guardado el cliente en la base de datos. Si la operación no se ha podido realizar, se
lanzará una excepción de tipo RuntimeException por JPA, interrumpiendo el hilo normal de ejecución.
2. Leer
Cree un nuevo método en la clase.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
/*
* Lectura y recuperación de registros de la BD
*/
public List<Cliente> leer() {
return laConexion.buscar((administrador) ‐> {
Query query = administrador.createQuery("SELECT c FROM Cliente c");
return query.getResultList();
});
}
Para las necesidades de la aplicación, es necesario disponer de una manera de recuperar un cliente
determinado a partir de su código (su clave primaria).
En vez de concatenar directamente las cadenas de caracteres, se utiliza una instancia de la clase StringBuilder,
que permite acelerar este proceso. Una vez construida la consulta, puede utilizarse con el método toString() de
StringBuilder.
A partir de aquí, como se busca un único cliente, se llama al método getSingleResult() del objeto Query.
Siendo el valor devuelto por el método getSingleResult() un simple objeto y estando seguros que se trata de
un cliente, basta con hacer un cast de este objeto con el tipo Cliente para que el método sea más cómodo de
usar con la notación (Cliente).
¡Podría ser más sencillo utilizar el método find de la clase EntityManager pero sería menos didáctico!
3. Modificar
Cree un nuevo método en la clase.
// Modificación de un cliente en la BD
// ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
public Cliente modificar(Cliente elCliente) {
return laConexion.modificar((administrador) ‐> {
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
if (cliente == null) {
throw new IllegalArgumentException(
"Ningún cliente para el código " + codigo);
} else {
cliente.setApellido(elCliente.getApelido());
cliente.setNombre(elCliente.getNombre());
cliente.setTarjetaFidelidad(elCliente.isTarjetaFidelidad());
return cliente;
}
});
}
El método find de la clase EntityManager se utiliza aquí para encontrar el cliente con un código determinado.
Después se modifica el cliente obtenido con las propiedades del cliente pasado como parámetro, con la excepción
notable de la fecha de creación que no se puede modificar. Esta simple técnica permite limitar los parámetros del
método al mismo tiempo que se controlan las propiedades que se pueden modificar o no.
4. Eliminar
Cree un nuevo método en la clase.
// Supresión de un cliente en la BD
// ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
public boolean eliminar(String vCodigo) {
return laConexion.modificar((administrador) ‐> {
if (cliente == null) {
throw new IllegalArgumentException(
"Ningún registro se corresponde con el código "
+ vCodigo + ".");
} else {
administrador.remove(cliente);
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
}
return true;
});
}
Este método es un poco más complejo que el método eliminar() de ModoPagosCrud, ya que hay que
asegurarse de no eliminar un cliente que tenga pedidos almacenados en el sistema.
Para ello, se realiza una primera consulta que comprueba cuántos pedidos tiene el cliente con el código indicado:
La palabra clave COUNT es estándar en SQL y devuelve el número de resultados como número de tipo long.
Al final, se presenta la propiedad cliente en el pedido. Se puede acceder a ella, así como a sus subpropiedades con
el separador punto (.). Aquí, ped.cliente.codigo corresponde al código del cliente del pedido «ped».
5. Buscar
Para efectuar búsquedas sobre los clientes, es el momento de introducir una herramienta de JPA para construir
consultas. En efecto, resulta molesto construir consultas «manualmente» con parámetros y es completamente
contrario a un buen rendimiento. Como las consultas de búsqueda son siempre las mismas excepto por los
parámetros, es mejor preparar estas consultas en JPA. Estas consultas se llaman consultas con nombre o
NamedQuery.
@Entity
@NamedQueries({
@NamedQuery( name="buscarCliente",
query="SELECT c FROM Cliente c "
+ "WHERE c.codigo LIKE :busqueda "
+ "OR c.apellido LIKE :busqueda "
+ "OR c.nombre LIKE :busqueda"),
@NamedQuery( name="clientePreciso",
query="SELECT c FROM Cliente c "
+ "WHERE c.codigo LIKE ?1 "
+ "AND c.apellido LIKE ?2 "
+ "AND c.nombre LIKE ?3")
})
public class Cliente {
...
}
Estas consultas serán accesibles por su nombre, especificado con el atributo name.
Identifique las consultas con nombre de manera que no resulten ambiguas: ¿una consulta llamada «buscar» tendrá
relación con los clientes, los artículos o los pedidos?
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
// Búsqueda en la BD
// ‐‐> con una cadena de caracteres cualquiera
public List<Cliente> buscar(String busqueda) {
return laConexion.buscar((administrador) ‐> {
Query query = administrador.createNamedQuery("buscarCliente");
query.setParameter("busqueda", "%" + busqueda + "%");
return query.getResultList();
});
}
Este método interroga JPA para obtener la consulta guardada con el nombre «buscarCliente» con la ayuda del
método createNamedQuery de la clase EntityManager. Una vez obtenida la consulta, el parámetro llamado
”busqueda” se fija con el valor deseado, sin las comillas simples.
Los nombres de los parámetros son arbitrarios, deben simplemente estar anotados en la consulta textual como «:
<nombre del parámetro>», aquí «busqueda». El tipo del parámetro se infiere respecto a la columna de la base de
datos.
// Búsqueda en la BD
// ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
public List<Cliente> buscar(String vCodigo, String vApellido, String
vNombre) {
String codigo, apellido, nombre;
if (vCodigo.equals("")) {
codigo = "%";
} else {
codigo = "%" +vCodigo +"%";
}
if (vApellido.equals("")) {
apellido = "%";
} else {
apellido = "%" +vApellido +"%";
}
if (vNombre.equals("")) {
nombre = "%";
} else {
nombre = "%" +vNombre +"%";
}
return query.getResultList();
});
}
Se trata de una variante de la manera anterior donde los parámetros están indicados de manera ordinal: por un
número.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Estos parámetros deben anotarse como ”?<posición del parámetro>”, aquí "?1", "?2", "?3".
Es obligatorio inicializar todos los parámetros antes de poder ejecutar estas consultas.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 6/6
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Clase Articulo
Continúe creando la clase Articulo en el package entidad.
package entidad;
import java.io.Serializable;
import java.time.Instant;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@Entity
public class Articulo implements Serializable {
@Id
private String codigo;
@ManyToOne(cascade = { CascadeType.PERSIST })
private Categoria categoria;
@ManyToMany(fetch = FetchType.EAGER)
private Set<Proveedor> proveedores = new HashSet<>();
@Column(name="precio_unitario")
private double precioUnitario;
@Temporal(TemporalType.DATE)
private Date fecha;
/*
* Constructor
*/
public Articulo(String codigo, String codigoCategoria,
String designacion,
int cantidad, double precioUnitario,
Instant fecha) {
this(code,
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
new Categoria().setCodigo(codigoCategoria),
designacion,
cantidad, precioUnitario,
fecha);
}
/*
* Constructor 2
*/
public Articulo() {
}
/*
* Accesores
*/
public String getCodigo() {
return codigo;
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
/*
* Mutadores
*/
public void setCodigo(String codigo) {
this.codigo = codigo;
}
Esta anotación significa que un artículo puede estar ligado a una única categoría, y a la inversa, una categoría
puede estar ligada a varios artículos, lo que corresponde al espíritu de la aplicación.
Se trata de uno de los tipos estándar de relación entre objetos definidos por JPA. Los demás son @OneToOne,
@OneToMany (la relación inversa de @ManyToOne) y @ManyToMany.
Observe que así la relación ArticuloCategoria es unidireccional. Un artículo conoce su categoría pero no podemos
buscar sencillamente los artículos a partir de una categoría. Se trata aquí de una característica de navegación para
esta relación.
Para transformar esta relación en una navegación bidireccional, se tendría que anotar la clase Categoria. La clase
Pedido implementará esta navegación bidireccional.
En la base de datos, esta relación se materializará en la tabla ARTICULO, con la introducción de una columna que
contenga el código de la categoría para cada artículo. Por defecto esta columna tendrá como nombre
CATEGORIA_CODIGO, es decir el nombre de la clase referenciada concatenada con un underscore o guion bajo (_)
seguido del nombre de la propiedad del identificador o clave primaria.
En otros términos, la tabla ARTICULO contiene la información relativa a la relación, la tabla CATEGORIA no contiene
nada relativo a esta relación.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
La propiedad proveedores también se encuentra anotada, esta vez con @ManyToMany. Esta anotación formaliza
que cada artículo puede comprarse a varios proveedores y que cada proveedor puede vender varios artículos.
En la base de datos, esta relación se almacena en una tabla intermedia, llamada tabla de asociación, compuesta por
dos columnas: una columna almacena la clave primaria del artículo, la otra la clave primaria del proveedor.
La anotación @ManyToMany se parametriza con fetch = FetchType.EAGER. En efecto, las relaciones de tipo
@ManyToMany son lazy (perezosas en español) por defecto: la propiedad correspondiente solo se rellena (fetch) en
el primer acceso. Para facilitar el uso y considerando que solo existen pocos proveedores por cada artículo, la
propiedad proveedores se rellenará directamente al recuperar el artículo de base.
Por último, la propiedad precioUnitario se anota con @Column, que permite indicar información específica: aquí,
los datos de precio se almacenan en la columna precio_unitario y no en la columna PRECIOUNITARIO.
new Categoria().setCodigo(codigo_categoria)
La clase Categoria utiliza lo que se llama una interfaz fluida (en inglés fluent interface). Se trata de una
técnica de programación que permite encadenar las llamadas a los métodos, sobre todo las de tipo
setXXX, devolviendo la instancia. Así encontrará el siguiente código en la clase Categoria:
Esto permite crear un objeto, asignarle una propiedad y continuar trabajando con él.
Esta manera de codificar permite aumentar la legibilidad del código y está muy en boga desde hace algunos años.
La encontramos por ejemplo en el principio del builder pattern, en las clases del package java.time y en muchas
otras librerías.
LocalDate penúltimoDiaDelMes =
hoy.with(TemporalAdjusters.lastDayOfMonth()).minusDays(1);
En este último ejemplo, los objetos devueltos por lo métodos with y minusDays son copias modificadas del objeto
original, siendo la mayoría de las clases del package java.time inmutables (en inglés: immutable).
No se explican las clases Categoria y Proveedor en este libro. Se deja su codificación para su libre iniciativa como
ejercicio.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
package entidad.crud;
import java.util.Collections;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.swing.JOptionPane;
import control.connection.Conexion;
import entidad.Articulo;
import entidad.Categoria;
/*
* Constructor
*/
public ArticuloCrud(Conexion conexion) {
this.laConexion = conexion;
}
}
1. Crear
Cree un nuevo método en la clase.
/**
* Añadir un nuevo artículo en la BD.
*
* @throws IllegalArgumentException
* si resulta imposible crear el artículo.
*/
public void crear(Articulo articulo) {
laConexion.modificar((administrador) ‐> {
administrador.persist(articulo);
return articulo;
});
}
La novedad de este método respecto a los métodos crear anteriores es indicar en JavaDoc que puede producirse
una excepción durante su ejecución.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Se trata de un indicador adicional para el que use este método. ¡Sin embargo esta indicación supone que el
desarrollador lee esta documentación para tenerla en cuenta!
2. Leer
Cree dos nuevos métodos en la clase.
/**
* Lectura y recuperación de los registros en la BD.
* @throws IllegalArgumentException
* si no se pueden leer los artículos.
*/
public List<Articulo> leer() {
return laConexion.buscar((administrador) ‐> {
Query query = administrador.createQuery(
"SELECT a FROM Articulo a");
return query.getResultList();
});
}
/**
* leer un artículo determinado de la BD.
* @throws IllegalArgumentException
* si no se puede leer el artículo.
*/
public Articulo leer(String codigo) {
return laConexion.buscar((administrador) ‐> {
Query query = administrador.createQuery(
"SELECT a FROM Articulo a WHERE a.codigo = :codigo");
query.setParameter("codigo", codigo);
return (Articulo) query.getSingleResult();
});
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Este código muestra por ejemplo que las consultas parametrizadas pueden crearse al instante sin almacenarse en
un consulta con nombre.
Las consultas con nombre presentan un mejor rendimiento en cuanto a velocidad de ejecución ya que no necesitan
que se las instancie.
3. Modificar
Cree un nuevo método en la clase.
/**
* Modificación de un artículo en la BD.
* @throws IllegalArgumentException
* si no se puede modificar el artículo.
*/
public boolean modificar(String vCodigo, String vReferencia,
String vDesignacion, int vCantidad, double vPu) {
return laConexion.modificar((administrador) ‐> {
if (articulo == null) {
throw new IllegalArgumentException(
"Ningún artículo para el código " + vCodigo);
} else {
articulo.setReferencia(
new Categoria().setCodigo(vReferencia));
articulo.setDesignacion(vDesignacion);
articulo.setCantidad(vCantidad);
articulo.setPrecioUnitario(vPu);
}
return true;
});
}
Este método modificar() no recibe como parámetro un artículo, sino las nuevas propiedades del artículo a
modificar.
Compare la legibilidad de este método con el método modificado de la clase ClienteCrud. ¿Cuál de las dos
maneras de codificar le parece más legible?
4. Eliminar
Cree un nuevo método en la clase.
/**
* Supresión de un cliente en la BD.
* @throws IllegalArgumentException
* si no se puede suprimir el artículo.
*/
public boolean eliminar(String vCodigo) {
return laConexion.modificar((administrador) ‐> {
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
5. Buscar
Cree dos nuevos métodos en la clase.
/*
* Búsqueda en la BD
*/
@SuppressWarnings("unchecked")
public List<Articulo> buscarTodos(String busqueda) {
return laConexion.buscar((administrador) ‐> {
String consulta = "";
+ "SELECT a "
+ "FROM Articulo a "
+ "WHERE a.codigo LIKE ’%" + busqueda + "%’ "
+ "OR a.categoria.codigo LIKE ’%" + busqueda + "%’ "
+ "OR a.designacion LIKE ’%" + busqueda + "%’ ";
/*
* Búsqueda rápida sobre el código
*/
@SuppressWarnings("unchecked")
public List<Articulo> buscarPorCodigo(String vCodigo) {
return laConexion.buscar((administrador) ‐> {
Query consulta = administrador.createQuery(
"SELECT a FROM Articulo a WHERE a.codigo LIKE ?1");
consulta.setParameter(1, vCodigo);
return consulta.getResultList();
});
}
Habrá observado con total seguridad las advertencias generadas por Eclipse en el editor sobre las líneas que
incluyen la llamada al método getResultList(). Este aviso se presenta mediante un subrayado amarillo sobre la
línea.
Eclipse le advierte de esta manera de que no es seguro que la lista devuelta contenga realmente objetos del tipo
esperado. Esto se debe al hecho de que el objeto Query no puede saber a ciencia cierta el tipo de los objetos
devueltos en la lista.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Sitúe el cursor del ratón sobre la línea subrayada y utilice la combinación de teclas [Ctrl] 1 o haga clic con el
botón derecho y seleccione QuickFix.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
package entidad.crud;
import java.util.Collections;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.swing.JOptionPane;
import control.connection.Conexion;
import entidad.Articulo;
import entidad.Categoria;
/*
* Constructor
*/
public ArticuloCrud(Conexion conexion) {
this.laConexion = conexion;
}
}
1. Crear
Cree un nuevo método en la clase.
/**
* Añadir un nuevo artículo en la BD.
*
* @throws IllegalArgumentException
* si resulta imposible crear el artículo.
*/
public void crear(Articulo articulo) {
laConexion.modificar((administrador) ‐> {
administrador.persist(articulo);
return articulo;
});
}
La novedad de este método respecto a los métodos crear anteriores es indicar en JavaDoc que puede producirse
una excepción durante su ejecución.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Se trata de un indicador adicional para el que use este método. ¡Sin embargo esta indicación supone que el
desarrollador lee esta documentación para tenerla en cuenta!
2. Leer
Cree dos nuevos métodos en la clase.
/**
* Lectura y recuperación de los registros en la BD.
* @throws IllegalArgumentException
* si no se pueden leer los artículos.
*/
public List<Articulo> leer() {
return laConexion.buscar((administrador) ‐> {
Query query = administrador.createQuery(
"SELECT a FROM Articulo a");
return query.getResultList();
});
}
/**
* leer un artículo determinado de la BD.
* @throws IllegalArgumentException
* si no se puede leer el artículo.
*/
public Articulo leer(String codigo) {
return laConexion.buscar((administrador) ‐> {
Query query = administrador.createQuery(
"SELECT a FROM Articulo a WHERE a.codigo = :codigo");
query.setParameter("codigo", codigo);
return (Articulo) query.getSingleResult();
});
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Este código muestra por ejemplo que las consultas parametrizadas pueden crearse al instante sin almacenarse en
un consulta con nombre.
Las consultas con nombre presentan un mejor rendimiento en cuanto a velocidad de ejecución ya que no necesitan
que se las instancie.
3. Modificar
Cree un nuevo método en la clase.
/**
* Modificación de un artículo en la BD.
* @throws IllegalArgumentException
* si no se puede modificar el artículo.
*/
public boolean modificar(String vCodigo, String vReferencia,
String vDesignacion, int vCantidad, double vPu) {
return laConexion.modificar((administrador) ‐> {
if (articulo == null) {
throw new IllegalArgumentException(
"Ningún artículo para el código " + vCodigo);
} else {
articulo.setReferencia(
new Categoria().setCodigo(vReferencia));
articulo.setDesignacion(vDesignacion);
articulo.setCantidad(vCantidad);
articulo.setPrecioUnitario(vPu);
}
return true;
});
}
Este método modificar() no recibe como parámetro un artículo, sino las nuevas propiedades del artículo a
modificar.
Compare la legibilidad de este método con el método modificado de la clase ClienteCrud. ¿Cuál de las dos
maneras de codificar le parece más legible?
4. Eliminar
Cree un nuevo método en la clase.
/**
* Supresión de un cliente en la BD.
* @throws IllegalArgumentException
* si no se puede suprimir el artículo.
*/
public boolean eliminar(String vCodigo) {
return laConexion.modificar((administrador) ‐> {
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
5. Buscar
Cree dos nuevos métodos en la clase.
/*
* Búsqueda en la BD
*/
@SuppressWarnings("unchecked")
public List<Articulo> buscarTodos(String busqueda) {
return laConexion.buscar((administrador) ‐> {
String consulta = "";
+ "SELECT a "
+ "FROM Articulo a "
+ "WHERE a.codigo LIKE ’%" + busqueda + "%’ "
+ "OR a.categoria.codigo LIKE ’%" + busqueda + "%’ "
+ "OR a.designacion LIKE ’%" + busqueda + "%’ ";
/*
* Búsqueda rápida sobre el código
*/
@SuppressWarnings("unchecked")
public List<Articulo> buscarPorCodigo(String vCodigo) {
return laConexion.buscar((administrador) ‐> {
Query consulta = administrador.createQuery(
"SELECT a FROM Articulo a WHERE a.codigo LIKE ?1");
consulta.setParameter(1, vCodigo);
return consulta.getResultList();
});
}
Habrá observado con total seguridad las advertencias generadas por Eclipse en el editor sobre las líneas que
incluyen la llamada al método getResultList(). Este aviso se presenta mediante un subrayado amarillo sobre la
línea.
Eclipse le advierte de esta manera de que no es seguro que la lista devuelta contenga realmente objetos del tipo
esperado. Esto se debe al hecho de que el objeto Query no puede saber a ciencia cierta el tipo de los objetos
devueltos en la lista.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Sitúe el cursor del ratón sobre la línea subrayada y utilice la combinación de teclas [Ctrl] 1 o haga clic con el
botón derecho y seleccione QuickFix.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
package entidad.crud;
import java.util.Collections;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.swing.JOptionPane;
import control.connection.Conexion;
import entidad.Pedido;
/*
* Constructor 1
*/
public PedidoCrud(Conexion conexion) {
this.laConexion = conexion;
}
}
1. Crear
Cree un nuevo método en la clase.
/**
* Añadir un nuevo pedido en la BD.
* @throws EniException si es imposible crear el pedido.
*/
public void crear(Pedido pedido) throws EniException {
try {
laConexion.modificar((administrador) ‐> {
admnistrador.persist(pedido);
return pedido;
});
} catch(RuntimeException e) {
throw new EniException(e.getMessage(), e);
}
}
Este método tiene la particularidad de recuperar y transformar la excepción RuntimeException, que puede que
JPA lance, en una excepción específica, que no es de tipo RuntimeException. Esta última debe por lo tanto
obligatoriamente indicarse en la firma del método con la palabra clave throws. A partir de aquí, todo código que
invoque a este método debe explícitamente gestionar esta excepción, en caso contrario el código no compilará.
Se trata de una técnica eficaz para no arriesgarse a olvidar la gestión de un eventual error durante la ejecución de
la aplicación.
package entidad.crud;
public EniException() {
super();
}
2. Leer
Cree un nuevo método en la clase para leer todos los pedidos.
/*
* Lectura y recuperación de registros en la BD
*/
public List<Pedido> leer() {
return laConexion.buscar((administrador) ‐> {
TypedQuery<Pedido> query = administrador.createQuery(
"SELECT p FROM Pedido p",
Pedido.class);
return query.getResultList();
});
}
Este método utiliza una versión del método createQuery disponible desde JPA 2.0, que recibe un parámetro
adicional de tipo Class. Permite obtener un objeto de tipo TypedQuery que deriva de Query.
Este objeto está tipado con la clase que se pasa como parámetro, y permite evitar los @SuppressWarning vistos
anteriormente.
/*
* Consulta equivalente a
* "SELECT p FROM Pedido p WHERE p.codigo = :codigo"
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
*/
public Pedido leer(String codigo) {
return laConexion.buscar((administrador) ‐> {
CriteriaBuilder cb = administrador.getCriteriaBuilder();
CriteriaQuery<Pedido> criterio =
cb.createQuery(Pedido.class);
ParameterExpression<String> parametro =
cb.parameter(String.class);
Root<Pedido> objeto = criterio.from(Pedido.class);
criterio.select(objeto)
.where(cb.equal(objeto.get("codigo"), parametro));
query.setParameter(parametro, codigo);
return query.getSingleResult();
});
}
Este método utiliza la API Criteria de JPA. Permite crear consultas programáticamente en vez de pasar consultas
en forma de cadena de caracteres.
Su principal ventaja es que los eventuales errores en la sintaxis de la consulta se detectan en tiempo de
compilación y no en tiempo de ejecución, como en los ejemplos anteriores.
Su principal inconveniente es que es menos legible para aquellos desarrolladores habituados a la sintaxis SQL,
mientras que el lenguaje JPQL es al final muy cercano a SQL.
3. Modificar
Cree un nuevo método en la clase.
/*
* Modificación de un pedido en la BD
*/
public Pedido modificar(Pedido elPedido) throws EniException
{
Object devuelto = laConexion.modificar((administrador) ‐> {
String codigo = elPedido.getCodigo();
if (pedido == null) {
return new EniException(
"Ningún pedido para el código " + codigo);
} else {
try {
pedido.setCliente(elPedido.getCliente());
pedido.setPago(elPedido.getPago());
if (elPedido.getLineas().isEmpty()) {
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
pedido.getLineas().clear();
}
} catch(RuntimeException e) {
return new EniException(e.getMessage(), e);
}
return pedido;
}
});
Pedido pedido = null;
if (devuelto instanceof Pedido) {
pedido = (Pedido) devuelto;
}
if (devuelto instanceof EniException) {
throw (EniException) devuelto;
}
return pedido;
}
La interfaz Function no tiene en cuenta las excepciones verificadas. Si quiere continuar advirtiendo al
desarrollador de que el método es susceptible de lanzar una excepción, no debe lanzar la excepción desde el
bloque del método apply() de la interfaz Function sino fuera de este.
4. Eliminar
Cree un nuevo método de la clase.
/*
* Supresión de un pedido de la BD
*/
public void eliminar(String vCodigo) throws EniException {
EniException problema =
laConexion.modificar((administrador) ‐> {
EniException excepcion = null;
Pedido pedido = administrador.find(Pedido.class, vCodigo);
if (pedido == null) {
exception = new EniException(
"Ningún pedido para el código " + vCodigo);
} else {
try {
administrador.remove(pedido);
} catch(RuntimeException e) {
excepcion = new EniException(e.getMessage(), e);
}
}
return excepcion;
});
if (problema != null) {
throw problema;
}
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
5. Buscar
Cree un nuevo método en la clase.
/*
* Búsqueda en la BD
*/
public List<Pedido> buscar(String busqueda) {
return laConexion.buscar((administrador) ‐> {
TypedQuery<Pedido> query =
administrador.createNamedQuery(
"buscarPedido", Pedido.class);
query.setParameter("busqueda", "%" + busqueda +"%");
return query.getResultList();
});
}
Este método muestra por ejemplo que también es posible crear consultas con nombre Y tipadas.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/5
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Las clases Categoria y Proveedor y sus CRUD asociados todavía no se han abordado. Le dejamos este trabajo como
ejercicio.
Una vez codificadas estas clases, queda indicar a JPA que estas clases deben tenerse en cuenta en las consultas
hacia la base de datos.
Abra el archivo persistence.xml. Puede utilizar el atajo de teclado [Crtl][Shift] R para abrir un cuadro de
diálogo de búsqueda rápida. Empiece a teclear el nombre del archivo en la zona de búsqueda. Se escaneará el
espacio de trabajo con el fin de mostrar los archivos correspondientes. Haga doble clic en el archivo deseado.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
En un contexto MVC, los datos constituyen los modelos, las clases gráficas las vistas. Modelo y Vista son
totalmente independientes el uno del otro: no se conocen. ¿Cómo entonces las acciones que los usuarios realizan
sobre las vistas pueden modificar los modelos y a la inversa, cómo pueden mostrarse los datos esperados? Son
precisamente los controladores los que se encargaran de este enlace.
Otra particularidad importante: el modelo, si no conoce directamente las vistas que muestran sus datos, tiene al
menos la posibilidad de notificar a estas cualquier cambio que les afecte.
El esquema muestra que el usuario no tiene ningún conocimiento del controlador. Sin embargo es el encargado de
transmitir de manera transparente para el usuario las peticiones al modelo para su tratamiento. Las vistas se
actualizan mediante el modelo a través de un sistema de notificación basado en la noción de eventos y
escuchadores de eventos.
Las ventajas de esta arquitectura, que se basa en una separación de responsabilidades, son múltiples:
Si se deben comunicar a la aplicación los cambios de los datos, el modelo se verá impactado así como
probablemente el controlador, mientras que la vista sigue siendo la misma. Si deben realizarse cambios gráficos, se
modificará la vista, dejando el modelo y el controlador sin alterar. Esto facilita el mantenimiento de la aplicación.
La distribución del modelo, de la vista y del controlador permite escribir más facilmente test unitarios para la lógica
del dominio.
Esta modularidad permite a diferentes desarrolladores trabajar simultáneamente sobre los aspectos lógicos del
dominio y la interfaz de usuario.
Para la gestión de los clientes, artículos y pedidos del proyecto Luna, se utiliza este método de diseño para mostrar
los datos en tablas gráficas que tendrán la capacidad de actualizarse una vez modificados los datos.
Se utiliza un componente gráfico de Swing, llamado JTable, construido sobre este patrón de diseño MVC para este
fin, y esta separación de responsabilidades se retoma a un nivel superior.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
MVC y JTable
Las clases ya existentes permiten establecer la conexión con la base de datos y la importación de estos datos en el
modelo del dominio de la aplicación.
Se usa un componente Swing de tipo JTable para permitir la representación gráfica de los datos con la forma de
una tabla de doble entrada, en filas y columnas.
JTable está construido sobre los principios de la arquitectura MVC, como la mayoría de los componentes Swing que
representan datos complejos como las JList y los JTree. Entender los mecanismos utilizados en una JTable facilita el
uso de los otros tipos de componentes.
Instanciar un JTable se hace como con cualquier otra clase: basta con llamar a su constructor.
Para ello, existen varias maneras: proporcionar directamente todos los datos de las filas y columnas en la
construcción, proporcionar una lista de objetos que representan cada una una línea de datos, o usar un modelo de
tabla. Es esta última opción la elegida para la aplicación.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Gestión de clientes
Se utiliza el paradigma MVC empezando por la gestión de clientes. Los pasos a seguir para los artículos y los pedidos
son similares.
Su clase asociada ClienteCrud permite realizar operaciones desde la base de datos, entre ellas la búsqueda de
clientes.
La tabla podrá mostrar los clientes que provienen de la base de datos. Para ello, el método leer() de la clase
ClienteCrud intervendrá en algún momento.
2. Modelo gráfico
La clase JTable necesita datos para mostrar. La plantilla de la tabla proporciona estos datos, es decir un objeto
que implementa la interfaz TableModel. Este modelo de tabla es diferente del modelo de dominio ya que solo
concierne el aspecto gráfico.
El nombre de las columnas a mostrar con el método getColumnName(int columna), siendo el parámetro el
índice de la columna (empezando por 0).
El valor de la celda con el método getValueAt(int fila, int columna), siendo el primer parámetro el índice de la
fila, el segundo el de la columna.
una manera de añadir escuchadores a este modelo gracias a dos métodos: addTableModelListener y
removeTableModelListener.
Proporciona también un medio para editar directamente cualquier celda de la tabla desde la interfaz gráfica gracias
a los métodos isCellEditable y setValueAt. No se utilizarán estos métodos en la aplicación.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Cree una nueva clase llamada ModeloClientes en el package control.modelo. Esta clase hereda de la
clase AbstractTableModel que pertenece al package javax.swing.table.
import javax.swing.table.AbstractTableModel;
La clase ModeloClientes debe sin embargo obligatoriamente redefinir varios métodos de la interfaz TableModel.
Por este motivo, Eclipse ha creado estos métodos insertándoles valores por defecto (0 y null).
package control.modelo;
import javax.swing.table.AbstractTableModel;
@Override
public int getRowCount() {
// TODO Auto‐generated method stub
return 0;
}
@Override
public int getColumnCount() {
// TODO Auto‐generated method stub
return 0;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
// TODO Auto‐generated method stub
return null;
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Las columnas son fijas: no cambiarán en la aplicación, y corresponden al código, apellido, nombre, posesión de la
tarjeta de fidelidad y la fecha de creación.
La siguiente etapa consiste en recuperar el número de clientes. Lo más sencillo es construir el modelo de tabla
con una lista de clientes que se pasa como parámetro al constructor.
Sería posible recuperar las columnas desde la base de datos, pero esto hace el modelo más complejo.
El hecho de crear una nueva lista a partir de una lista existente permite guardar un control absoluto sobre esta
nueva lista.
case 2:
return cliente.getNombre();
case 3:
return cliente.isTarjetaFidelidad();
case 4:
return cliente.getFechaCreacion();
default:
return null;
}
}
En este punto, se dispone de un modelo de tabla que permite mostrar los clientes.
Añada un pequeño método para encontrar un cliente a partir de su número de fila. Será útil más tarde.
Después debe ocuparse del enlace con la clase ClienteCrud. Este enlace se hará en una clase «Controlador» que
no controlará la tabla gráfica, sino que enlazará la recuperación de datos de la base de datos y la importación de
los mismos en el modelo gráfico.
Esta clase instancia como atributos finales un nuevo objeto de tipo ClienteCrud para acceder a los datos de la base
de datos, lee los clientes y los inserta en el modelo gráfico para la tabla a mostrar. Este modelo gráfico se puede
recuperar después gracias al método getModelo().
@Test
public void test() {
Conexion conexion = Conexion.getConexion();
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
crud.crear(unCliente);
Como puede constatar, la clase ControlCliente recibe un objeto Conexion como parámetro en su constructor.
¿De dónde procede este objeto? Sería tentador utilizar el método estático getConexion() de la clase Conexion
para recuperarlo, pero esta manera no se puede testear.
Este objeto se crea en realidad una única vez en la aplicación al validar el usuario. Después se pasa a la instancia
de la clase FBienvenida que lo almacena en un atributo privado. Cuando el usuario hace clic en el botón para
mostrar los clientes, se pasa este atributo de conexión al panel correspondiente.
Se trata por lo tanto de crear una cadena de paso del objeto Conexion de la ventana de conexión a la ventana
de clientes.
Vaya al método validar() de la clase FConexion y añada el objeto conexion como parámetro del
constructor de la clase FBienvenida.
if (valido) {
Conexion conexion = Conexion.getConexion();
FConexion.this.dispose();
FBienvenida laVentanaMenu = new FBienvenida(conexion);
laVentanaMenu.setVisible(true);
}
Eclipse advierte de un error. Propone soluciones pasando el cursor del ratón por encima de la línea con el error.
Después guarde el parámetro de conexión en la clase FBienvenida como atributo privado y final.
...
Acuérdese de que la palabra clave this corresponde al objeto actual. Prefijar un atributo o método con ”this”
permite evitar ambiguedades cuando el nombre de alguna variable y de ciertos atributos son idénticos.
Pase después este atributo como parámetro al panel de tipo PClientes, en el método mostrarClientes().
this.tabla.setModel(controlCliente.getModelo());
A partir de ahora, los datos de clientes de la base de datos se muestran en la tabla del módulo de clientes.
En esta fila, cada celda corresponde a una propiedad de este cliente, siendo el valor de esta propiedad un objeto.
Por defecto, cada celda de una fila se formatea con el método toString() del objeto correspondiente.
Por este motivo la columna «Tarjeta fidelidad» muestra true o false, y la fecha de creación tiene este formato tan
particular.
La clase JTable propone dos maneras para personalizar la apariencia, que se expondrán una tras otra.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 6/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
@Override
public Class<?> getColumnClass(int columnIndex){
Class<?> clase = null;
switch(columnIndex){
case 3:
clase = Boolean.class;
break;
case 4:
clase = Instant.class;
break;
default:
clase = super.getColumnClass(columnIndex);
break;
}
return clase;
}
Este método permite asignar índices a la JTable en función del tipo de datos a mostrar, devolviendo la clase del
objeto cuyo aspecto se quiere modificar.
Este método se redefine, el método ya existe en la clase madre AbstractTableModel. Para invocar a la
implementación de esta clase madre, se utiliza la palabra clave super.
Se mejora la visualización de valores booleanos: casillas de selección reemplazan los textos true o false.
Una tarjeta centrada aparecerá para los que tengan una tarjeta de fidelidad en vez de la casilla de selección.
Para conseguir este resultado, se modificarán las clases llamando a una clase especializada en la definición del
aspecto de las celdas de tablas: DefaultTableCellRenderer.
Esta clase hereda de JLabel y puede por lo tanto mostrar un texto y/o una imagen.
Implementa también la interfaz TableCellRenderer y por lo tanto permite especificar lo que se tiene que
mostrar dentro de una celda; para ello debe redefinir el método getTableCellRendererComponent.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 7/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
boolean hasFocus,
int row,
int column)
Este método contiene también un resultado a devolver de tipo JComponent. Se trata del componente gráfico
utilizado para la visualización de la celda.
Para la clase DefaultTableCellRenderer, basta en general con devolver el renderer, es decir finalizar el método
con la siguiente línea:
return this;
package dialogo.aspecto;
import java.awt.Component;
import java.awt.Font;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
setFont(getFont().deriveFont(Font.BOLD));
return this;
}
}
Esta clase es muy sencilla y se encarga simplemente de poner en negrita lo que se tiene que mostrar.
No olvide llamar al método getTableCellRendererComponent de la clase madre con la instrucción super. Esto
permite inicializar muchos pequeños detalles gráficos (el color de fondo si la fila está seleccionada por ejemplo) que
son bastante molestos de implementar por uno mismo.
Esta clase es tan sencilla que ni siquiera se preocupa del valor a mostrar.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 8/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Aqui tiene la manera de cambiar el aspecto de un valor booleano para que aparezca una imagen.
package dialogo.aspecto;
import java.awt.Component;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
public BooleanoRenderer() {
super();
tarjeta = new ImageIcon(
getClass().getResource(
"/imagenes/gestion/Pending‐Invoice‐32.png"));
}
super.getTableCellRendererComponent(table,
value,
isSelected, hasFocus, row, column);
Esta clase crea un objeto de tipo Icon que mostrará si el cliente dispone de una tarjeta de fidelidad.
Se recupera después el valor a mostrar. Como se trata con seguridad de un booleano, se hace directamente un
cast sobre este valor y se asigna el icono si el booleano vale true. Si vale false, se asigna un icono nulo, lo que
equivale a ”no mostrar ninguna imagen”.
Se crea un renderer para optimizar el aspecto gráfico. En particular, se crea un único componente gráfico con el
operador new para todas las celdas de la columna. Si olvida poner el icono a null, las visualizaciones serán
incoherentes al cabo de un tiempo. ¡Y no existe nada peor que un bug aleatorio!
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 9/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
package dialogo.aspecto;
import java.awt.Component;
import java.time.Instant;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import control.utilidades.GestionFechas;
this.setText(texto);
this.setHorizontalAlignment(CENTER);
return this;
}
}
package control.utilidades;
import java.text.ParseException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 10/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
}
}
Esta clase convierte un objeto de tipo Instant en una cadena de caracteres con formato español.
El componente JTable va desde este momento a poder aprovechar todas estas presentaciones personalizadas.
La visualización de la fecha es todavía un poco pequeña con respecto al resto de la tabla, y cuando se selecciona
una fila, no cambia el color de fondo. Esto es debido al hecho de que el método
super.getTableCellRendererComponent no se ha llamado en la clase InstantRenderer.
super.getTableCellRendererComponent(table,
value,
isSelected, hasFocus,
row, column);
Desplace después esta llamada al inicio del método. ¿Qué diferencia observa?
table.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent evt) {
// gestión de un simple clic en la fila de la tabla
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 11/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
La clase ClienteCrud realiza operaciones básicas sobre estos datos: creación de un cliente, lectura de un cliente o
de todos los clientes, modificación y supresión de un cliente y búsqueda entre el conjunto de clientes.
La clase del modelo gráfico para la JTable ModeloClientes se encarga de gestionar la lista de clientes mostrada en
la tabla.
La clase ControlCliente se encarga de llamar al CRUD del cliente y tiene conocimiento del modelo de la tabla.
La clase gráfica de tipo panel PClientes que contiene la JTable se encarga de mostrar el modelo de la tabla.
La clase gráfica de tipo panel PCliente se encarga de mostrar un cliente concreto. Su misión es también apoyar
en la creación de un nuevo cliente y modificar un cliente existente.
Se trata, a continuación, de realizar las operaciones siguientes: añadir, modificar, buscar y eliminar, debiendo
guardar los datos en la base de datos.
a. Añadir un cliente
Hacer clic en el botón Añadir para mostrar el panel PCliente de creación de un cliente.
Se llama al ControlCliente para guardar el cliente con la ayuda de la clase ClienteCrud, y se actualiza el modelo
de la tabla.
En esta cadena de operaciones, cabe destacar un punto importante: para que todo funcione como se desea, la
instancia PCliente debe tener el mismo ControlCliente que la instancia PClientes. Lo más sencillo es pasarlo
con un método.
Añada la siguiente línea en el método actionPerformed del escuchador de acciones del botón Añadir de
la clase PClientes.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 12/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
public ActionAnadir() {
putValue(NAME, "Añadir");
putValue(SHORT_DESCRIPTION,"Añadir un nuevo cliente");
}
Cree un método setControlCliente() en la clase PCliente. Implica crear en la clase un atributo privado
no final de tipo ControlCliente.
Añada una acción al botón Añadir, cuya clase hereda de la clase AbstractAction y se llamará
AccionPrincipal.
public AccionPrincipal() {
configurarAccion(this);
}
accionPrincipal(codigo,
apellido, nombre,
tarjetaFidelidad, creadoEl);
}
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 13/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
La principal ventaja del método accionPrincipal es que los parámetros ya están informados con los valores
introducidos por el usuario, se solicitaron ya los componentes gráficos para recuperar su valor: el método es más
senciilo de probar.
Queda un punto a resolver antes de codificar la lógica de creación: se trata de la recuperación de la fecha de
creación.
Añada el siguiente código en el constructor de la clase PCliente, un poco antes de la creación del atributo
fechaCreacion.
JFormattedTextField.AbstractFormatter formatter =
new JFormattedTextField.AbstractFormatter() {
@Override
public String valueToString(Object value)
throws ParseException {
String text = "";
if (value instanceof Instant) {
Instant instant = (Instant) value;
text = GestionFechas.fechaEnCadenaES(instant);
}
return text;
}
@Override
public Object stringToValue(String text)
throws ParseException {
try {
return GestionFechas.cadenaESEnFecha(text);
} catch (ParseException e) {
return null;
}
}
};
fechaCreacion = new JFormattedTextField(formatter);
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 14/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
AbstractFormatter permite transformar un objeto cualquiera en una cadena de caracteres para poderlo
mostrar en el JFormattedTextField. De manera inversa, permite convertir una cadena de caracteres en un
objeto.
Para utilizarlo en este caso preciso, basta con usar el siguiente código:
fechaCreacion.setValue(cliente.getFechaCreacion());
Instant creadoEl = (Instant) fechaCreacion.getValue();
txtCodigo.requestFocus();
} else {
JOptionPane.showMessageDialog(null,
"La introducción del código de cliente"
+ " es obligatoria",
"Verifique lo introducido",
JOptionPane.ERROR_MESSAGE);
}
}
Este método delega la operación de creación al control cliente. El ControlCliente usa el ClienteCrud para
guardar en base de datos, y el modelo de tabla se actualiza mediante el ControlCliente después. Todas estas
operaciones se enmascaran por el hecho de que la acción gráfica para añadir solo está destinada a un objeto
«sencillo».
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 15/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Para facilitar la adición de nuevos clientes, se reinicializan después los campos de introducción de datos, con el
campo de fecha de creación indicando el instante presente.
return elCliente;
} catch (Exception e) {
JOptionPane.showMessageDialog(
null,
"Ninguna creación realizada en la BD.\n\n"
+ e.getMessage(), "Problema encontrado",
JOptionPane.ERROR_MESSAGE);
}
return null;
}
b. Modificación de un cliente
La modificación se realiza desde la clase PClientes. Después de seleccionar una fila de la tabla de clientes, el
acceso a su modificación se realiza haciendo clic en el botón Modificar, o haciendo doble clic en esta fila.
Sea cual sea el modo de acceso elegido, el tratamiento es el mismo. Por esta razón se crea un método
modificacion(), que tiene como objetivo verificar que se ha seleccionado realmente una fila, y en caso
afirmativo extraer las datos a pasar al panel PCliente.
Cambio de la información del panel PCliente para reflejar la operación de modificación: su texto y su icono.
Los números de fila empiezan por cero. Si no se ha seleccionado ninguna fila, el método getSelectedRow()
devuelve 1.
Desde el momento en que se obtiene una fila, se puede recuperar el cliente desde el modelo:
Ahora se trata de hacer que el panel PCliente conozca al cliente seleccionado. La opción elegida es pasarlo como
parámetro del constructor.
Cree un nuevo constructor de la clase PCliente que reciba como parámetro un objeto Cliente. El
constructor existente llamará al nuevo constructor creado con un parámetro null.
PCliente() {
this(null);
}
PCliente(Cliente cliente) {
createContents();
if (cliente != null) {
txtCodigo.setText(cliente.getCodigo());
fechaCreacion.setValue(cliente.getFechaCreacion());
txtApellido.setText(cliente.getApellido());
txtNombre.setText(cliente.getNombre());
tarjetaFidelidad.setSelected(cliente.isTarjetaFidelidad());
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 17/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
}
}
Este constructor actualiza los widgets gráficos del panel con la información después de crear dichos widgets.
En caso de modificación de un cliente, está terminantemente prohibido cambiar el código identificador. ¡Este
sirve para establecer las relaciones con los pedidos del cliente en base de datos! De la misma forma está
prohibida toda modificación de la fecha de creación, gracias el método setEditable() del componente gráfico.
// se impide la modificación
// de los campos código y fecha de creación
txtCodigo.setEditable(false);
fechaCreacion.setEditable(false);
El mismo panel usado para añadir utilizará el constructor por defecto, sin cliente pasado como parámetro, y no
tendrá por lo tanto ninguna inicialización de widgets.
Para modificar los labels del panel y la acción del botón principal, añada los accesores de estos widgets gráficos
en la clase PCliente.
Convierta la variable del label del título en atributo, si no lo ha hecho ya. Para ello, seleccione la línea de la
declaración y utilice la combinación de teclas [Ctrl] 1, y elija la opción Convert local variable to field.
Después vaya a la declaración de la propiedad del label del título, utilice [Ctrl] 1 y elija la opción Create
getter and setter for ’lblTitulo’. Elimine después el setter (es decir el mutador): no servirá de nada.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 18/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Queda ocuparse del botón principal. Este debe mostrar un ícono y un texto correcto, y claro ¡modificar
el cliente en vez de crear uno nuevo!
@Override
protected void configurarAccion(AbstractAction accion) {
accion.putValue(Action.NAME, "Modificar");
accion.putValue(Action.LARGE_ICON_KEY,
new ImageIcon(
getClass().getResource(
"/imagenes/gestion/Save‐48.png")));
}
@Override
protected void accionPrincipal(String codigo,
String apellido, String nombre,
boolean fidelidad, Instant creadoEl) {
try {
boolean modificado = controlCliente.modificar(codigo,
apellido, nombre,
fidelidad, creadoEl);
if (modificado) {
accionCancelar.actionPerformed(null);
}
} catch (ParseException e) {
e.printStackTrace();
}
}
};
Este código crea una clase interna anónima que hereda de PCliente, en la cual las operaciones críticas están
redefinidas. Aquí, las operaciones críticas son la configuración de la acción del botón y el código de la acción
principal.
Existen varias maneras de hacerlo, de las cuales algunas evitan la herencia. ¡Esta tiene sobre todo un objetivo
didáctico!
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 19/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
try {
elCliente = crud.modificar(elCliente);
} catch (Exception e) {
JOptionPane.showMessageDialog(
null,
"Ninguna modificación efectuada en la BD.\n\n"
+ e.getMessage(), "Problema encontrado",
JOptionPane.ERROR_MESSAGE);
}
return false;
}
El botón Modificar realiza ahora su rol, con las restricciones que se le han dado.
La próxima etapa consiste en mostrar el panel de modificación de cliente al hacer doble clic sobre una de las filas
de la tabla.
Añada las siguientes líneas en el escuchador de la tabla que se codificó cuando se gestionó el clic sencillo.
El evento de clic en la tabla proporciona información adicional, como el número de clics consecutivos. Es
accesible desde el método getClickCount() de la clase MouseEvent.
En lo sucesivo es posible añadir y modificar un cliente desde la interfaz gráfica. Tiene ahora que ocuparse de la
eventual supresión.
c. Supresión de un cliente
JOptionPane.showMessageDialog(null,
"Seleccione una fila antes.",
"Eliminar",
JOptionPane.INFORMATION_MESSAGE);
} else {
// pedir confirmación al usuario
}
}
De la misma manera que para la modificación, este código comprueba que se ha seleccionado
realmente alguna fila. En caso contrario, muestra un cuadro de diálogo con información para el usuario
indicándole qué hacer.
if (numeroFila < 0) {
// el usuario no ha seleccionado ninguna fila
} else {
int eleccion = JOptionPane.showConfirmDialog(
null,
String.format("¿Desea eliminar la ficha del cliente?"
+ "\ncódigo : %s"
+ "\napellido : %s",
table.getValueAt(numeroFila, 0),
table.getValueAt(numeroFila, 1)),
"ELIMINAR", JOptionPane.YES_NO_OPTION);
// 0: sí 1: no
if (eleccion == JOptionPane.YES_OPTION) {
controlCliente.eliminar(numeroFila);
}
}
Este código abre un cuadro de diálogo de confirmación para el usuario, dejándole la elección entre responder sí o
no a la pregunta formulada, gracias al método estático showConfirmDialog de la clase JOptionPane.
Este método devuelve un código específico de la respuesta del usuario. Si responde SI, el control cliente elimina
realmente la fila.
En el procesamiento de los códigos devueltos, evite usar los valores literales (0 o 1). Utilice siempre las
constantes definidas en las clases.
} catch (Exception e) {
JOptionPane.showMessageDialog(
null,
"No se ha eliminado nada en la BD.\n\n"
+ e.getMessage(), "Problema encontrado",
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 21/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
JOptionPane.ERROR_MESSAGE);
}
}
d. Búsqueda de un cliente
Se acaba esta primera parte del procesamiento por las búsquedas. Las impresiones y exportaciones se detallarán
en el capítulo Aplicación final.
// créación de la ventana
PClienteBusqueda busqueda = new PClienteBusqueda();
busqueda.setAccionCancelar(accionCancelar);
busqueda.setControlCliente(controlCliente);
...
cambiarPanel(busqueda, "Búsqueda de cliente(s)");
}
}
A diferencia de las demás acciones, no hay que compartir el control cliente con la busqueda. La busqueda filtrará
en la tabla los clientes encontrados y al final de la búsqueda el objetivo es encontrar todos los clientes, no
filtrados.
public AccionPrincipal() {
putValue(NAME, "Buscar");
putValue(SHORT_DESCRIPTION,
"Buscar entre los clientes existentes");
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 22/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
6. Actualización de la tabla
a. Notificación
Recuerde que en el modelo MVC existe una estricta separación de las vistas y los datos. Por lo tanto para que la
vista, en este caso el componente JTable, actualice la visualización gráfica de los datos, hay que informarle del
cambio de estado del modelo de tabla. Se trata ni más ni menos que de la aplicación del concepto de
notificación.
Para que el modelo pueda notificar a las vistas sus cambios de estado, debe disponer de métodos particulares,
siendo lo ideal tener un método para añadir, otro para modificar, otro para eliminar, etc. La clase
AbstractTableModel ya dispone de estos métodos y, como guinda del pastel, no necesita ni siquiera
redefinirlos. Pueden utilizarse como están.
Este atajo le muestra todos los métodos que puede utilizar de la clase que empiezan por las letras
«fire».
Cree los métodos que el resto del código podrá invocar para notificar al modelo de los cambios.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 23/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
// ===========================================================
// permite actualizar el modelo
// después de nuevas búsquedas
// e informar a las vistas que muestran este modelo
public void leido(List<Cliente> nuevosDatos){
losDatos.clear();
losDatos.addAll(nuevosDatos);
fireTableDataChanged();
}
El método creado() incluye el cliente pasado como parámetro en la lista de clientes. Recupera después el
último índice de esta lista, y usa el método fireTableRowsInserted con este índice. El método
fireTableRowsInserted notificará al componente gráfico de que se ha insertado una fila, y por lo tanto
actualizará el aspecto gráfico para tener en cuenta este nuevo cliente.
El método eliminado() suprime un cliente respecto a su índice en la lista. Se llama después al método
fireTableRowsDeleted() para solicitar al componente gráfico que se refresque teniendo en cuenta que se ha
suprimido una fila concreta.
El método leido() considera que se han modificado todas las filas, y borra por lo tanto todas las existentes con
el método clear() de la lista y añade en dicha lista todos los clientes recuperados. Como se trata de una
modificación global de los datos, se notifica al componente gráfico con fireTableDataChanged() que hay que
refescar globalmente la vista.
En el caso en el que se modifica un cliente, resulta práctico crear un pequeño método que devuelva el número
de fila del cliente modificado.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 24/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Transformar un conjunto de datos de clientes en una tabla de doble entrada (filas y columnas).
Notificar, por lo tanto informar, a las vistas afectadas con cualquier cambio del modelo.
Llame a los métodos creado(), modificado(), eliminado() y leido() de este modelo de tabla desde el
ControlCliente.
// 2. Añadir en el modelo
elModeloClientes.creado(elCliente);
return elCliente;
} catch (Exception e) {
...
}
return null;
}
...
// 1. Guardar primero en la BD
try {
elCliente = crud.modificar(elCliente);
if (elCliente != null) {
elModeloClientes.modificado(elCliente);
return true;
}
} catch (Exception e) {
...
}
return false;
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 25/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
} catch (Exception e) {
...
}
}
b. Eventos
No se trata de interesarse en los eventos clásicos como el clic del ratón o una tecla pulsada, sino en los eventos
relativos a la modificación del estado del modelo de datos: añadir, eliminar, etc. Esto concierne a la vez a los
datos y a la estructura necesaria.
c. Escuchador de eventos
En este momento, es posible abordar otro concepto utilizado en el patrón de diseño MVC, el de escuchador de
eventos.
Efectivamente, si bien la clase ModeloClientes es capaz de informar a sus vistas, ¡sería interesante que estas
la escuchen! A esto se añade otra restricción: ¡las vistas no tienen el permiso de escuchar a cualquiera!
Únicamente están en relación con algunos objetos privilegiados, situados en un conjunto de datos especiales,
previsto a este respeto.
Se añaden métodos del tipo «fireXXX» a la clase ModeloClientes para que el modelo se pueda expresar. ¿Qué
se deberá añadir como código para que el componente JTable entienda las notificaciones del modelo? En
realidad nada. La mayor parte de los componentes sofisticados de Java como JTable, JList o JTree están dotados
de un «buen oído» por la sencilla razón de que se construyen según la arquitectura MVC.
Con todo rigor, JTable, JList y JTree no se construyen según MVC sino según MV*. Los desarrolladores Java
decidieron históricamente fusionar los controladores y las vistas en estos componentes gráficos por razones de
simplicidad.
Pertenecen a estos componentes que disponen de modelos para acoger sus datos (TableModel para JTable,
ListModel para JList, TreeModel para JTree,...). El componente JTable se actualizará por lo tanto
automáticamente cuando el modelo de datos llame a los métodos ”fireXXX”.
Compruebe la aplicación para las operaciones de modificación y supresión. Debería confirmar que el JTable
modifica sus datos.
Dejando aparte a los privilegiados, como el componente JTable, ¿cómo podría informar el modelo a otro
interlocutor si no le conoce? Simplemente añadiendo un escuchador en la lista de escuchadores a notificar, esto
gracias al método addTableModelListener.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 26/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
controlCliente.getModelo().addTableModelListener(this);
Eclipse le informa de un error en esta línea. Es normal, este método debe pasar como parámetro un
objeto que implemente la interfaz TableModelListener.
Haga clic sobre la línea con error y utilice la combinación de teclas [Ctrl] 1 para que aparezcan los Quick Fix
de Eclipse.
El tooltip sobre fondo amarrillo (en Windows) da una idea de las modificaciones que se efectuarán.
La clase PClientes muestra un error ya que implementa la interfaz TableModelListener cuando aún no existen
los métodos de esta interfaz.
Esta interfaz implementa un escuchador de eventos, o siendo más precisos de objetos eventos relativos a los
datos o metadatos del modelo de tabla. Posee un único método tableChanged(TableModelEvent e), que se
redefinirá (es evidentemente una obligación) y completará en función de las necesidades de la aplicación.
Pase por encima del nombre de la clase con el ratón. Aparece un tooltip que describe el error y que sugiere
soluciones. Elija la primera, Add unimplemented methods.
@Override
public void tableChanged(TableModelEvent e) {
// TODO Auto‐generated method stub
Una buena práctica de codificación es definir por lo menos tres caracteres para cualquier variable, atributo o
método, aunque sea local. ¡Nombrar una variable con un único caracter como e no hará su código más rápido
sino que lo hará seguramente menos legible!
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 27/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Los eventos de la clase TableModelEvent tienen todos un tipo, accesible mediante el método getType().
Estos tipos de eventos se definen en la clase TableModelEvent como atributos estáticos.
Añada simples trazas con System.out.println para comprobar el comportamiento de este método.
Una aplicación profesional no debe nunca usar los métodos print de System.out sino más bien utilizar los
mecanismos de traza de Java del package java.util.logging o de librerías de logging como SLF4J
(http://www.slf4j.org/), Log4j 2 (http://logging.apache.org/log4j/2.x/), o Logback (http://logback.qos.ch/).
Gracias a este sencillo mecanismo, se puede disponer de notificaciones sobre cualquier modificación del modelo
de datos.
Para resolver este pequeño problema, el panel de búsqueda debería disponer de su propio modelo de tabla. Basta
para ello con asignarle un nuevo objeto ControlCliente. Así, las dos partes del módulo, la búsqueda y la
visualización, serán independientes.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 28/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 29/29
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Notificaciones no gráficas
Los conceptos de notificación, eventos y escuchadores no están reservados a las clases gráficas. Es posible utilizar
este mecanismo en cualquier parte de una aplicación con unas pocas líneas.
Ponga en marcha esta técnica para la clase ControlCliente, para notificar a cualquier objeto de que se ha
modificado un cliente.
...
import java.beans.PropertyChangeSupport;
...
public class ControlCliente {
PropertyChangeSupport es una clase de utilidad de Java que ya posee el código necesario para añadir y quitar
escuchadores y generar eventos destinados a estos escuchadores.
Utilice la opción Source Generate Delegate Methods... del menú contextual para añadir los métodos
addPropertyChangeListener() y removePropertyChangeListener() en la clase ControlCliente.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Estos métodos permitirán grabar escuchadores sobre los eventos que genere la clase ControlCliente. Estos
escuchadores tienen únicamente la restricción de implementar la interfaz PropertyChangeListener.
La siguiente etapa consiste en generar un evento cuando la clase ControlCliente modifica un cliente. Esto se hace
llamando al método firePropertyChange de la instancia notificaciones de la clase PropertyChangeSupport.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Este método recibe como parámetro una cadena de caracteres arbitraria, el antiguo valor y el nuevo. Salvo el
primer parámetro, los valores restantes pueden ser nulos.
...
Cliente antiguoCliente = crud.leer(codigo);
...
// 1. Guardar primero en la BD
elClient = crud.modificar(elCliente);
notificaciones.firePropertyChange("cliente",
antiguoCliente,
elCliente);
// 2. Después añadir en el modelo ‐‐> ACTUALIZACIÓN del JTable auto
...
} catch
...
}
}
En la clase Cliente, utilice la opción Source Generate toString() del menú contextual. Esto generará
automáticamente el método toString(), que permite visualizar explícitamente objetos de tipo Cliente en la
consola.
@Override
public String toString() {
return "Cliente [codigo=" + this.codigo
+ ", apellido=" + this.apellido
+ ", nombre=" + this.nombre
+ ", tarjeta_fidelidad=" + this.tarjeta_fidelidad
+ ", fecha=" + this.fecha + "]";
}
Para acabar, añada un escuchador, por ejemplo en el método setConexion() de la clase PClientes.
controlCliente.addPropertyChangeListener(evt ‐> {
System.out.println("Cambio " +evt.getPropertyName());
System.out.println("Antiguo valor " +evt.getOldValue());
System.out.println("Nuevo valor " +evt.getNewValue());
});
¡En toda aplicación digna de este nombre, nunca debería olvidar quitar los escuchadores (darse de baja)!
Pruebe este código ejecutando la aplicación y modificando un cliente. Debe ver en la consola las trazas de esta
modificación.
Para darse el gusto e ilustrar con un pequeño ejemplo de otros usos de Java, se modifica este código para
abrir además una página web.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
controlCliente.addPropertyChangeListener(evt ‐> {
System.out.println("Cambio " +evt.getPropertyName());
System.out.println("Antiguo valor " +evt.getOldValue());
System.out.println("Nuevo valor " +evt.getNewValue());
abrirPaginaWeb("www.google.es");
});
¡Recuerde suprimir estos métodos después de divertirse! O mejor, desplácelos a una clase de test.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Estructura y fuentes
La estructura general, la clase ModeloCliente, la clase ControlCliente y las acciones de los botones de los paneles
PClientes y la clase PCliente se pueden ver a continuación.
1. ModeloClientes
En esta sección encontrará el código fuente de la clase ModeloClientes.
package control.modelo;
/*
* Clase que contiene el modelo de datos de Clientes.
* Debe extender la clase abstracta AbstractTableModel
*/
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
import entidad.Cliente;
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
/*
* útiles para los renderers por defecto que aplicarán un estilo
* de presentación de datos en función de la clase
*/
@Override
public Class<?> getColumnClass(int columnIndex) {
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
/*
* permite actualizar el modelo después de nuevas búsquedas e
* informar a las vistas que muestran este modelo
*/
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
2. ControlCliente
En esta sección encontrará el código fuente de la clase ControlCliente.
package control;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.text.ParseException;
import java.time.Instant;
import java.util.Collections;
import java.util.List;
import javax.swing.JOptionPane;
import control.connection.Conexion;
import control.modelo.ModeloClientes;
import entidad.Cliente;
import entidad.crud.ClienteCrud;
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
JOptionPane.ERROR_MESSAGE);
clientes = Collections.emptyList();
}
return null;
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
if (elCliente != null) {
elModeloClientes.modificado(elCliente);
return true;
}
} catch (Exception e) {
JOptionPane.showMessageDialog(
null,
"Ninguna modificación realizada en la BD.\n\n"
+ e.getMessage(), "Problema encontrado",
JOptionPane.ERROR_MESSAGE);
}
return false;
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 6/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
}
}
3. PClientes
En esta sección encontrará el código fuente de la clase PClientes, que define la interfaz gráfica que enumera los
clientes.
// propiedades no gráficas
// ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
private ControlCliente controlCliente;
private Conexion conexion;
// propiedades gráficas
// ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
private JTable tabla;
private JTextField txtCodigo;
private JTextField txtFechaCreacion;
private JTextField txtNombre;
private JTextField txtApellido;
private JTextField txtDireccion;
private JTextField txtTelfijo;
private JTextField txtMovil;
private JTextField txtEmail;
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 7/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
this.conexion = conexion;
controlCliente = new ControlCliente(conexion);
tabla.setModel(controlCliente.getModelo());
controlCliente.getModelo().addTableModelListener(this);
controlCliente.addPropertyChangeListener(evt ‐> {
System.out.println("Cambio " +evt.getPropertyName());
System.out.println("Antiguo valor " +evt.getOldValue());
System.out.println("Nuevo valor " +evt.getNewValue());
});
// gestión del aspecto de las columnas del JTable
// ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
TableColumnModel modeloColumna = tabla.getColumnModel();
TableColumn nombres = modeloColumna.getColumn(1);
nombres.setCellRenderer(new NegritaRenderer());
TableColumn tarjetas = modeloColumna.getColumn(3);
tarjetas.setCellRenderer(new BooleanoRenderer());
TableColumn fechas = modeloColumna.getColumn(4);
fechas.setCellRenderer(new InstantRenderer());
tabla.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
// gestión del clic sencillo en alguna fila de la tabla
// para poner los datos
// en los campos correspondientes
int numFila = tabla.getSelectedRow();
if (numFila >= 0) {
ModeloClientes modelo = controlCliente.getModelo();
Cliente cliente = modelo.getCliente(numFila);
txtCodigo.setText(cliente.getCodigo());
txtApellido.setText(cliente.getApellido());
txtNombre.setText(cliente.getNombre());
tarjetaFidelidad.setSelected(client.isTarjetafidelidad());
// se pone la fecha de creación con el formato dd‐MM‐yyyy
String strFecha =
GestionFechas.fechaEnCadenaES(
cliente.getFechaCreacion());
txtFechaCreacion.setText(strFecha);
}
return this;
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 8/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
@Override
protected void configurarAccion(
AbstractAction accion) {
action.putValue(Action.NAME, "Modificar");
action.putValue(Action.LARGE_ICON_KEY,
getImage(
"/imagenes/gestion/Save‐48.png"));
}
@Override
protected void accionPrincipal(String codigo,
String apellido,
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 9/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
String nombre,
boolean fidelidad,
Instant creadoEl) {
try {
boolean modificado =
controlCliente.modificar(
codigo, apellido, nombre,
fidelidad, creadoEl);
if (modificado) {
cancelar.actionPerformed(null);
}
} catch (ParseException e) {
e.printStackTrace();
}
}
};
edicion.getLblTitulo().setText("Edición");
edicion.getLblTitulo().setIcon(getImagen(
"/imagenes/gestion/cliente/User‐Modify‐64.png"));
edicion.setAccionCancelar(accionCancelar);
edicion.setAccionExport(accionExport);
btnModificar.setIcon(getImagen(
"/imagenes/gestion/Data‐Edit‐48.png"));
cambiarPanel(edicion, "Modificación del cliente "
+cliente.getNombre() +" "
+cliente.getApellido()
+"["+cliente.getCodigo() +"]");
}
}
}
}
4. PCliente
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 11/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
btnBuscar.setIcon(getImage(
"/imagenes/gestion/Search‐48.png"));
cambiarPanel(busqueda, "Búsqueda de cliente(s)");
}
}
public AccionAñadir() {
putValue(NAME, "Añadir");
putValue(SHORT_DESCRIPTION,
"Añadir un nuevo cliente");
}
btnAnadir.setIcon(getImage(
"/imagenes/gestion/Add‐New‐48.png")));
cambiarPanel(anadir, "Añadir un nuevo cliente");
}
}
En esta sección encontrará el código fuente de la clase PCliente, que permite visualizar un cliente determinado.
// propriedades no gráficas
// ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
private ControlCliente controlCliente;
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 12/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
// propriedades gráficas
// ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
private JTextField txtCodigo;
private JFormattedTextField fechaCreacion;
private JTextField txtNombre;
private JTextField txtApellido;
private JTextField txtDireccion;
private JTextField txtTelfijo;
private JTextField txtMovil;
private JTextField txtEmail;
@Override
public String valueToString(Object value)
throws ParseException {
String text = "";
if (value instanceof Instant) {
Instant instant = (Instant) value;
text =
GestionFechas.fechaEnCadenaES(instant);
}
return text;
}
@Override
public Object stringToValue(String text)
throws ParseException {
try {
return
GestionFechas.cadenaESenFecha(text);
} catch (ParseException e) {
return null;
}
}
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 13/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
this.controlCliente = controlCliente;
return this;
}
PCliente(Cliente cliente) {
JFormattedTextField.AbstractFormatter formatter =
new Formateador();
fechaCreacion = new JFormattedTextField(formatter);
createContents();
if (cliente != null) {
txtCodigo.setText(cliente.getCodigo());
// se impide la modificación
// de los campos código y fecha de creación
txtCodigo.setEditable(false);
fechaCreacion.setValue(cliente.getFechaCreacion());
fechaCreacion.setEditable(false);
txtApellido.setText(cliente.getApellido());
txtNombre.setText(cliente.getNombre());
tarjetafidelidad.setSelected(cliente.isTarjetaFidelidad());
}
}
...
}
public AccionPrincipal() {
configurarAccion(this);
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 14/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
txtCodigo.requestFocus();
} else {
JOptionPane.showMessageDialog(null,
"La introducción del código de cliente"
+ " es obligatoria",
"Verifique lo introducido",
JOptionPane.ERROR_MESSAGE);
}
}
JLabel getLblTitulo() {
return lblTitulo;
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 15/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Introducción
Este capítulo está dedicado a los otros dos módulos importantes del proyecto, los módulos artículo y pedido.
En la gestión de clientes, se ha manipulado una sola tabla de la base de datos. Esta vez, el trabajo abarca un
entorno multitabla.
Se usa también el design pattern MVC. Se expusieron y explicaron los conceptos subyacentes en el capítulo Modelo
MVC.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Clases asociadas
A diferencia del módulo cliente, los módulos artículo y pedido hacen intervenir las relaciones entre diferentes
objetos.
Por ejemplo, un pedido hace referencia a líneas de pedido, que se almacenan físicamente en una tabla separada.
Esta referencia se establece gracias al sistema de clave primaria clave foránea: para cada línea de pedido de la
tabla correspondiente, existe una columna que indica la clave primaria de la tabla asociada. Esta columna
almacena la clave foránea del pedido para cada línea.
JPA permite gestionar directamente este principio de clave, y por lo tanto preservar la integridad referencial de la
base de datos: la base de datos queda en un estado coherente.
Sin embargo es importante comprender este principio ya que las consultas utilizadas para interrogar a la base de
datos deben tener en cuenta este mecanismo.
El tipo Entidad hace referencia a una base de datos que modeliza el dominio, en el package entidad, y a una clase
CRUD, que describe las operaciones posibles, situada en el package entidad.crud.
El tipo control hace referencia a una clase que modeliza el modelo del componente gráfico asociado, situado en el
package control.modelo y a una clase que enlaza el CRUD con los modelos en el package control.
Para no hacer este capítulo innecesariamente pesado, puede descargar las clases asociadas desde la página
Información.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/1
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
la clase Articulo,
la clase ArticuloCrud,
1. Visualización
Para mostrar los datos de los artículos en el JTable de la clase FArticulos, los pasos son idénticos a los vistos para
la clase PClientes del capítulo Modelo MVC:
package control.modelo;
import java.util.ArrayList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
import entidad.Articulo;
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
El control de artículos se hace gracias a una clase específica. La clase gráfica no conocerá este tipo, y por lo
tanto no tendrá que gestionar directamente los accesos a la base de datos.
Esta manera de trabajar permite obtener un código modular y asignar funcionalidades bien identificadas a las
diferentes clases de la aplicación.
Contrariamente a la clase ControlCliente, no existe la gestión de las excepciones a este nivel. En efecto, no
debería existir aquí la visualización de un cuadro de diálogo en caso de error: la visualización gráfica no es
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
package control;
import java.awt.Window;
import java.time.Instant;
import java.util.Collections;
import java.util.List;
import control.connection.Conexion;
import control.estado.JasperFacade;
import control.modelo.ModeloArticulos;
import dialogo.FExport;
import entidad.Articulo;
import entidad.crud.ArticuloCrud;
crud.crear(unArticulo);
elModeloArticulo.creado(unArticulo);
return true;
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
ahora);
boolean modif = crud.modificar(codigo, referencia,
designacion,
cantidad, precioUnitario);
if (modif) {
elModeloArticulo.modificado(numeroLinea, unArticulo);
}
return modif;
}
Para simplificar el desarrollo, se crea una clase que utiliza un objeto ControlArticulo, que efectuará estas
operaciones de visualización en caso de error.
Para ello, el patrón de diseño Decorator es una fuente de inspiración: la nueva clase recibe como parámetro un
objeto de tipo ControlArticulos y tiene exactamente los mismos métodos que esta.
Para respetar el espíritu del patrón de diseño Decorator, se debería crear una interfaz que formaliza la
funcionalidad de un control de artículo implementado por las clases ControlArticulo y ControlAticuloDecorado.
package control;
import java.awt.Window;
import java.util.function.Function;
import javax.swing.JOptionPane;
import control.connection.Conexion;
import control.modelo.ModeloArticulos;
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
JOptionPane.ERROR_MESSAGE);
return false;
}
}
Este método recibe dos parámetros: un objeto que implementa la interfaz Runnable que posee un único
método llamado run(), y un objeto que implementa la interfaz Function que posee un único método apply
que recibe como parámetro la eventual excepción y devuelve una cadena de caracteres que describe el error.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 6/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Estas dos interfaces tienen un único método y por lo tanto son interfaces funcionales. Es posible crear
expresiones lambdas para simplificar la legibilidad del código.
@Override
public String apply(Exception e) {
return "Ninguna supresión realizada "
+ "en la BD.\n\n"
+ e.getMessage();
}
});
}
Ahora queda hacer que la ventana gráfica utilice un objeto de tipo ControlArticuloDecorado.
...
tabla.setModel(controlArticulo.getModelo());
2. Añadir
Se introduce una variación con respecto al código del módulo cliente: añadir (y todas las demás operaciones sobre
los artículos) se hace directamente desde la ventana FArticulos.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 7/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Seleccione el botón Añadir y agregue una acción que se ejecutará al hacer clic encima.
public AccionAnadir() {
putValue(NAME, "Añadir");
putValue(SHORT_DESCRIPTION,
"Añadir un nuevo artículo");
}
Primero se recupera el texto correspondiente al código del artículo, introducido por el usuario.
Se realiza después una comprobación: si el código está vacío, es imposible crear el artículo y por lo tanto aparece
un mensaje de error.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 8/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Si se ha creado el artículo correctamente, se borran los datos introducidos por el usuario gracias al método
borrarIntroduccion().
El botón Borrar no suprime el artículo. Su cometido es borrar todos los datos introducidos por el
usuario.
public AccionBorrar() {
putValue(NAME, "Borrar");
putValue(SHORT_DESCRIPTION,
" Borrar los datos introducidos");
}
3. Modificación
Para activar el botón Modificar, antes es preciso hacer doble clic en alguna de las filas del JTable para
seleccionarla. Los valores aparecen entonces en los campos de la zona de introducción de datos.
a. Selección de un artículo
Seleccione el componente gráfico de tipo JTable en modo Design. Añada un escuchador de eventos para
el clic del ratón gracias a la operación Add Event Handler mouse mousePressed del menú
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 9/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
contextual.
Añada el código siguiente en el método mousePressed que acaba de crear con WindowBuilder.
tabla.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if (e.getClickCount() == 2) {
int linea = tabla.getSelectedRow();
txtCodigo.setText(String.valueOf(
table.getValueAt(linea, 0)));
txtCategoria.setText(String.valueOf(
table.getValueAt(linea, 1)));
txtDesignacion.setText(String.valueOf(
table.getValueAt(linea, 2)));
sliderCantidad.setValue(
table.getValueAt(linea,3));
txtPrecioUnitario.setValue(
table.getValueAt(linea,4));
boton_modo_anadir_o_edicion(false);
}
}
});
Este método recupera los valores del artículo a partir de las celdas del JTable.
tabla.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if (e.getClickCount() == 2) {
int numeroLinea = tabla.getSelectedRow();
Articulo articulo =
controlArticulo.getModelo()
.getArticulo(numeroLinea);
txtCodigo.setText(articulo.getCodigo());
txtCategoria.setText(
articulo.getCategoria().getCodigo());
txtDesignacion.setText(articulo.getDesignacion());
sliderCantidad.setValue(articulo.getCantidad());
txtPrecioUnitario.setValue(
articulo.getPrecioUnitario());
boton_modo_anadir_o_edicion(false);
}
}
});
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 10/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
b. Guardar la modificación
public AccionModificar() {
putValue(NAME, "Modificar");
putValue(SHORT_DESCRIPTION,
"Modificar el artículo seleccionado");
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 11/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
cantidad, precio);
if (modif) {
borrarIntroduccion();
boton_modo_anadir_o_edicion(true);
}
}
}
}
}
Como antes, se realizan dos comprobaciones para validar la introducción de datos por parte del usuario. Si todo
es correcto, los botones Añadir y Borrar se vuelven a habilitar para añadir un nuevo artículo.
4. Eliminar
Al igual que para el módulo de clientes, se utiliza el botón Eliminar que se encuentra en la parte izquierda para
eliminar un artículo.
public AccionEliminar() {
putValue(NAME, "Eliminar");
putValue(SHORT_DESCRIPTION,
"Eliminar el artículo seleccionado");
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 12/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
}
}
}
}
5. Búsqueda
A diferencia del módulo de cliente, se pone en marcha una búsqueda asistida para los artículos directamente en la
ventana.
Cada vez que se pulsa alguna tecla se realiza una actualización de la lista de artículos visibles en la tabla.
La búsqueda propuesta se realiza sobre los campos Código, Código Categoría y Designación de los artículos.
Tecleando una segunda letra, como una «u», la lista se reduce y solo se muestra la línea correspondiente:
txtBusqueda.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent event) {
String busqueda = txtBusqueda.getText();
controlArticulo.buscar(busqueda);
}
});
Si el usuario presiona varias teclas, esto desencadenará consultas a la base de datos evitables.
Una versión un poco más evolucionada hace uso de clases especializadas en la gestión de la ejecución del código
en paralelo: las clases del package java.util.concurrent.
Añada este código en la clase FArticulos y organice los imports con [Ctrl][Shift] O para que la clase compile.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 13/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
@Override
public void run() {
System.out.println("Búsqueda de " +busqueda);
controlArticulo.buscar(busqueda);
this.futuro = null;
}
txtBusqueda.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent event) {
String busqueda = txtBusqueda.getText();
buscador.planificar(busqueda);
}
});
Este código planifica una búsqueda pasados 300 milisegundos en un hilo de ejecución dedicado, que se obtiene
gracias al objeto de tipo ScheduledExecutorService.
Si ya existe una búsqueda planificada, se anula. Esto evita realizar búsquedas innecesarias.
@Override
public void dispose() {
executor.shutdown();
super.dispose();
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 14/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Se explicará la gestión de las acciones de vista previa, impresión y exportación en el último capítulo Aplicación
final.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 15/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
la clase Articulo,
la clase ArticuloCrud,
1. Visualización
Para mostrar los datos de los artículos en el JTable de la clase FArticulos, los pasos son idénticos a los vistos para
la clase PClientes del capítulo Modelo MVC:
package control.modelo;
import java.util.ArrayList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
import entidad.Articulo;
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
El control de artículos se hace gracias a una clase específica. La clase gráfica no conocerá este tipo, y por lo
tanto no tendrá que gestionar directamente los accesos a la base de datos.
Esta manera de trabajar permite obtener un código modular y asignar funcionalidades bien identificadas a las
diferentes clases de la aplicación.
Contrariamente a la clase ControlCliente, no existe la gestión de las excepciones a este nivel. En efecto, no
debería existir aquí la visualización de un cuadro de diálogo en caso de error: la visualización gráfica no es
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
package control;
import java.awt.Window;
import java.time.Instant;
import java.util.Collections;
import java.util.List;
import control.connection.Conexion;
import control.estado.JasperFacade;
import control.modelo.ModeloArticulos;
import dialogo.FExport;
import entidad.Articulo;
import entidad.crud.ArticuloCrud;
crud.crear(unArticulo);
elModeloArticulo.creado(unArticulo);
return true;
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
ahora);
boolean modif = crud.modificar(codigo, referencia,
designacion,
cantidad, precioUnitario);
if (modif) {
elModeloArticulo.modificado(numeroLinea, unArticulo);
}
return modif;
}
Para simplificar el desarrollo, se crea una clase que utiliza un objeto ControlArticulo, que efectuará estas
operaciones de visualización en caso de error.
Para ello, el patrón de diseño Decorator es una fuente de inspiración: la nueva clase recibe como parámetro un
objeto de tipo ControlArticulos y tiene exactamente los mismos métodos que esta.
Para respetar el espíritu del patrón de diseño Decorator, se debería crear una interfaz que formaliza la
funcionalidad de un control de artículo implementado por las clases ControlArticulo y ControlAticuloDecorado.
package control;
import java.awt.Window;
import java.util.function.Function;
import javax.swing.JOptionPane;
import control.connection.Conexion;
import control.modelo.ModeloArticulos;
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
JOptionPane.ERROR_MESSAGE);
return false;
}
}
Este método recibe dos parámetros: un objeto que implementa la interfaz Runnable que posee un único
método llamado run(), y un objeto que implementa la interfaz Function que posee un único método apply
que recibe como parámetro la eventual excepción y devuelve una cadena de caracteres que describe el error.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 6/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Estas dos interfaces tienen un único método y por lo tanto son interfaces funcionales. Es posible crear
expresiones lambdas para simplificar la legibilidad del código.
@Override
public String apply(Exception e) {
return "Ninguna supresión realizada "
+ "en la BD.\n\n"
+ e.getMessage();
}
});
}
Ahora queda hacer que la ventana gráfica utilice un objeto de tipo ControlArticuloDecorado.
...
tabla.setModel(controlArticulo.getModelo());
2. Añadir
Se introduce una variación con respecto al código del módulo cliente: añadir (y todas las demás operaciones sobre
los artículos) se hace directamente desde la ventana FArticulos.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 7/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Seleccione el botón Añadir y agregue una acción que se ejecutará al hacer clic encima.
public AccionAnadir() {
putValue(NAME, "Añadir");
putValue(SHORT_DESCRIPTION,
"Añadir un nuevo artículo");
}
Primero se recupera el texto correspondiente al código del artículo, introducido por el usuario.
Se realiza después una comprobación: si el código está vacío, es imposible crear el artículo y por lo tanto aparece
un mensaje de error.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 8/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Si se ha creado el artículo correctamente, se borran los datos introducidos por el usuario gracias al método
borrarIntroduccion().
El botón Borrar no suprime el artículo. Su cometido es borrar todos los datos introducidos por el
usuario.
public AccionBorrar() {
putValue(NAME, "Borrar");
putValue(SHORT_DESCRIPTION,
" Borrar los datos introducidos");
}
3. Modificación
Para activar el botón Modificar, antes es preciso hacer doble clic en alguna de las filas del JTable para
seleccionarla. Los valores aparecen entonces en los campos de la zona de introducción de datos.
a. Selección de un artículo
Seleccione el componente gráfico de tipo JTable en modo Design. Añada un escuchador de eventos para
el clic del ratón gracias a la operación Add Event Handler mouse mousePressed del menú
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 9/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
contextual.
Añada el código siguiente en el método mousePressed que acaba de crear con WindowBuilder.
tabla.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if (e.getClickCount() == 2) {
int linea = tabla.getSelectedRow();
txtCodigo.setText(String.valueOf(
table.getValueAt(linea, 0)));
txtCategoria.setText(String.valueOf(
table.getValueAt(linea, 1)));
txtDesignacion.setText(String.valueOf(
table.getValueAt(linea, 2)));
sliderCantidad.setValue(
table.getValueAt(linea,3));
txtPrecioUnitario.setValue(
table.getValueAt(linea,4));
boton_modo_anadir_o_edicion(false);
}
}
});
Este método recupera los valores del artículo a partir de las celdas del JTable.
tabla.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if (e.getClickCount() == 2) {
int numeroLinea = tabla.getSelectedRow();
Articulo articulo =
controlArticulo.getModelo()
.getArticulo(numeroLinea);
txtCodigo.setText(articulo.getCodigo());
txtCategoria.setText(
articulo.getCategoria().getCodigo());
txtDesignacion.setText(articulo.getDesignacion());
sliderCantidad.setValue(articulo.getCantidad());
txtPrecioUnitario.setValue(
articulo.getPrecioUnitario());
boton_modo_anadir_o_edicion(false);
}
}
});
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 10/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
b. Guardar la modificación
public AccionModificar() {
putValue(NAME, "Modificar");
putValue(SHORT_DESCRIPTION,
"Modificar el artículo seleccionado");
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 11/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
cantidad, precio);
if (modif) {
borrarIntroduccion();
boton_modo_anadir_o_edicion(true);
}
}
}
}
}
Como antes, se realizan dos comprobaciones para validar la introducción de datos por parte del usuario. Si todo
es correcto, los botones Añadir y Borrar se vuelven a habilitar para añadir un nuevo artículo.
4. Eliminar
Al igual que para el módulo de clientes, se utiliza el botón Eliminar que se encuentra en la parte izquierda para
eliminar un artículo.
public AccionEliminar() {
putValue(NAME, "Eliminar");
putValue(SHORT_DESCRIPTION,
"Eliminar el artículo seleccionado");
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 12/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
}
}
}
}
5. Búsqueda
A diferencia del módulo de cliente, se pone en marcha una búsqueda asistida para los artículos directamente en la
ventana.
Cada vez que se pulsa alguna tecla se realiza una actualización de la lista de artículos visibles en la tabla.
La búsqueda propuesta se realiza sobre los campos Código, Código Categoría y Designación de los artículos.
Tecleando una segunda letra, como una «u», la lista se reduce y solo se muestra la línea correspondiente:
txtBusqueda.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent event) {
String busqueda = txtBusqueda.getText();
controlArticulo.buscar(busqueda);
}
});
Si el usuario presiona varias teclas, esto desencadenará consultas a la base de datos evitables.
Una versión un poco más evolucionada hace uso de clases especializadas en la gestión de la ejecución del código
en paralelo: las clases del package java.util.concurrent.
Añada este código en la clase FArticulos y organice los imports con [Ctrl][Shift] O para que la clase compile.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 13/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
@Override
public void run() {
System.out.println("Búsqueda de " +busqueda);
controlArticulo.buscar(busqueda);
this.futuro = null;
}
txtBusqueda.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent event) {
String busqueda = txtBusqueda.getText();
buscador.planificar(busqueda);
}
});
Este código planifica una búsqueda pasados 300 milisegundos en un hilo de ejecución dedicado, que se obtiene
gracias al objeto de tipo ScheduledExecutorService.
Si ya existe una búsqueda planificada, se anula. Esto evita realizar búsquedas innecesarias.
@Override
public void dispose() {
executor.shutdown();
super.dispose();
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 14/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Se explicará la gestión de las acciones de vista previa, impresión y exportación en el último capítulo Aplicación
final.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 15/15
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Imprimir y exportar
Crear informes sofisticados, producir archivos con distintos formatos o elaborar gráficos con Java requiere tiempo y
el dominio de numerosas clases y métodos adicionales. Conscientes de este hecho, varias empresas proponen
herramientas de reporting que permiten realizar de manera más rápida y segura este tipo de documentos.
Se ha elegido la librería JasperReports de la empresa JasperSoft ya que es un muy buen sistema de reporting
open source dedicado a aplicaciones Java. Esta herramienta puede funcionar de manera independiente a Eclipse o
en colaboración con este. Jaspersoft Studio es el editor gráfico WYSIWYG (What You See Is What You Get)
integrado en Eclipse que se encarga de la creación de plantillas de informes.
1. Funcionamiento de JasperReports
Para generar informes, hay necesariamente que pasar por tres etapas: la creación del archivo Jasper describiendo
el contenido del informe, la compilación de este archivo en un formato explotable por Jasper en producción y
alimentarlo con datos para rellenar el informe.
JasperReports se basa en principio sobre archivos XML (eXtensible Markup Language). No es necesario dominar
XML para esta etapa ya que la creación se realiza mediante Jaspersoft Studio, que genera el archivo
correspondiente, pero es recomendable ya que llegados a cierto punto resulta más productivo intervenir
directamente en el archivo XML.
Al final de esta etapa, se obtiene un archivo XML con extensión jrxml que representa la plantilla del informe
desde el cual se pueden producir varios documentos con distintos formatos.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
A partir del archivo jrxml, llamado también plantilla jrxml, Jasper procede a su compilación que permite verificar si
toda la plantilla está bien construida, sobre todo si es coherente con la norma XML y si los parámetros son
correctos.
Si la compilación finaliza correctamente, se genera un archivo binario con la extensión Jasper. Se puede entonces
utilizar este archivo para representar los datos dependiendo de las elecciones tomadas en el momento de la
creación del archivo jrxml con Jaspersoft Studio.
También es posible compilar en tiempo de ejecución el archivo binario Jasper. Se elige esta última opcíon para la
aplicación Luna.
Se pueden utilizar varias fuentes de datos en el archivo Jasper: XML, CSV, un conjunto de registros que
provengan de una base de datos SQL, objetos Java con formato JavaBeans, etc.
Estas fuentes de datos servirán para generar el informe con un formato apropiado.
El documento final está entonces listo para su uso. Solo queda elegir entre todos los tipos de archivo de salida
propuestos por Jasper: PDF, DOCX, HTML, ODT, ...
A continuación se procede con la instalación de Jaspersoft Studio, el editor WYSIWYG de JasperSoft, dentro de
Eclipse.
Abra el Eclipse Marketplace desde el menú eligiendo la opción Help Eclipse Marketplace.
En el campo de texto Find, introduzca «jaspersoft» y después haga clic en el botón Go presente en la
misma línea.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
El marketplace de Eclipse propone entonces los programas correspondientes a la búsqueda: Jaspersoft Studio
debe lógicamente ser el primero en aparecer.
Eclipse propone entonces un asistente de instalación. Después de resolver los features a instalar, haga
clic en Confirm para continuar con la instalación, deje a Eclipse trabajar sobre todo lo que tiene que
instalar (esto puede tomar su tiempo), acepte los términos de licencia y haga clic en Finish.
Eclipse descarga entonces el programa (esto también puede tomar algo de tiempo), la instalación finaliza
y Eclipse le pide reiniciar el programa.
Después de que Eclipse se haya reiniciado, Jaspersoft Studio está disponible en Eclipse.
a. Preparación
Para facilitar la creación de este informe, se crea una pequeña clase que importa directamente los datos de
clientes de la base de datos.
Esta clase no se utilizará en producción, pero permite visualizar el informe durante la fase de diseño.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
package control.informe;
import control.connection.Conexion;
import entidad.crud.ClienteCrud;
Esta clase contiene un único método estático llamado createBeanCollection que devuelve una colección de
objetos JavaBeans que servirán para alimentar el informe.
Un JavaBean es un objeto Java clásico que sigue convenciones de escritura de los métodos que acceden a sus
propiedades: los accesores empiezan todos por getXXX() y los mutadores por setXXX(). En el caso de la clase
Cliente, se accede y modifica la propiedad «apellido» con los métodos getApellido() y setApellido().
Existen varias técnicas para rellenar los datos del informe: conectarse directamente a la base de datos,
proporcionar objetos beans ,... Se utilizará esta última técnica.
Cree un adaptador de datos para los clientes seleccionando la opción File New Data Adapter del
menú.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Asigne un nombre a este adaptador. Para ello introduzca el nombre de la clase FabricaClientes con el
método createBeanCollection y haga clic en Finish.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 6/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
JasperReports todavía no tiene en cuenta los objetos del package java.time, como el tipo Instant. Hay que
proporcionarle objetos de tipo java.util.Date para que pueda tratar correctamente la información temporal.
Añada un método getCreacion() que devuelva un objeto de tipo java.util.Date en la clase Cliente.
Esto tendrá como efecto definir un campo creacion en JasperReport para la clase Cliente, que se utilizará en el
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 7/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
// ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
// Método utilizado por Jasper
// ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
@Deprecated
public Date getCreacion() {
return fecha;
}
// ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
informe.
La anotación @Deprecated tiene por objetivo prevenir a los desarrolladores de que este método no debería
utilizarse (aparece tachado en Eclipse).
Elija un modelo (un template): por ejemplo el modelo vacío (existen otros modelos disponibles si lo desea).
Haga clic en Next.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 8/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Elija el nombre del informe (Clientes.jrxml) y su ubicación (la carpeta jasper) y haga clic en Next.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 9/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Elija la fuente de datos del informe. Será el adaptador que acaba de crear.
Al contrario de lo indicado, es posible especificar más adelante los campos a mostrar en este informe.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 10/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 11/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Jaspersoft Studio propone una perspectiva dedicada a la creación de informes, pero no resulta muy funcional en la
versión utilizada para este libro. Puede sin embargo crear un informe, simplemente asegurándose de que la vista
Outline esté abierta. Si no fuera el caso, ábrala con la opción del menú Window Show view Outline.
Haga clic en el icono del botón DataSet and Query editor dialog para indicar los campos que se
utilizarán en el informe.
Haga clic en la pestaña Java Bean, introduzca la clase entidad.Cliente en el campo Class Name y elija
los campos que se incluirán en el informe (codigo, tarjetaFidelidad, apellido, nombre, creacion). Haga clic
después en Add selected field(s) para que el informe los tome en cuenta.
Modifique el ancho de las secciones (como Title y Detail 1), y deslice los campos desde la vista Outline
hasta la sección Detail 1.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 12/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Haga clic en la pestaña Preview debajo del editor para ver el informe.
Para modificar el formato de un campo, selecciónelo, haga un clic derecho y elija Show Properties.
Seleccione la sección Text Field y haga clic en el botón «...» situado junto a Pattern. Esto abre una
ventana que permite elegir el formato de datos a aplicar, por ejemplo una fecha.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 13/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
También es posible elegir en esta vista de propiedades el tipo de fuente, el tamaño, la alineación del texto
(izquierda, centrada,...), el color de fondo y del texto, los bordes.
Modifique los campos hasta que esté satisfecho y guarde regularmente el informe.
Añada una imagen en el informe deslizando un elemento Image desde la paleta hacia el informe, por
ejemplo en la sección Title.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 14/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Seleccione la opción Workspace resource, elija la imagen a mostrar y haga clic en OK.
El problema con las imágenes es que se parametrizan los archivos «a fuego». Si los usuarios no tienen
exactamente el mismo árbol de carpetas en su disco duro, los informes no se podrán compilar.
Por lo tanto se añade un parámetro que especifica el nombre de la carpeta de imágenes del informe para permitir
su uso en producción.
En la vista Outline, navegue hacia la sección Parameters, efectúe un clic con el botón derecho y elija la
opción Create Parameter.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 15/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Llame al parámetro imagenesDir y asegúrese de que está marcada la opción Is For Prompting (la
descripción es incompleta).
Seleccione después la imagen. La vista Properties se actualiza con las propiedades de la imagen.
Haga clic en el botón situado junto a la propiedad Expression. Se muestra un cuadro de diálogo que
permite modificar la ruta de la imagen.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 16/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Seleccione Parameters en el árbol de la izquierda. Haga doble clic después en el parámetro imagenesDir
del árbol central (deslice el árbol si el parámetro no aparece).
Se modifica la expresión: JasperReports añade los caracteres $P y pone el nombre del parámetro entre
llaves, pero un error «The current expression is not valid. Please verify it!» aparece.
Modifique la expresión para obtener este resultado (admitiendo que el nombre de la imagen es Moon
256.png) y haga clic en Finish.
$P{imagenesDir}+"/Moon‐256.png"
Haga clic en la pestaña Preview. JasperReports le solicita ahora que defina el parámetro imagenesDir para
poder generar la visualización.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 17/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Para finalizar haga clic en el botón verde debajo del parámetro. Se genera el informe con la imagen.
Una vez creado el informe tal y como usted desee, acuérdese de desmarcar la opción Is For Prompting del
parámetro imagenesDir.
Para añadir librerías externas, revise si es necesario el capítulo La caja de herramientas de Eclipse.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 18/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
De una clase que cubra el conjunto de procesos de creación de un informe Jasper enmascarando toda la
complejidad de este proceso.
Copie este archivo en la carpeta jasper del proyecto. Si Eclipse está abierto, seleccione esta carpeta o el
proyecto y presione la tecla [F5] para actualizar los recursos y hacer que Eclipse tome conciencia del nuevo
archivo.
En referencia a los principios de funcionamiento de Jasper, las etapas a realizar esta vez con el código Java se
determinan según la siguiente lista:
Mostrar el documento final con los datos, en función del tipo de salida elegido.
Cree primero la clase Sistema en el package control.utilidades. Servirá para encontrar el archivo jrxml.
package control.utilidades;
return getProperty(SEPARADOR);
}
La línea import static permite importar los métodos estáticos de una clase. Es posible invocarlos, entonces, como
si fuesen parte de la propia clase.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 20/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
misParametros.put("imagenesDir",
carpetaJasper + "imagenes");
JRDataSource source =
new JRBeanCollectionDataSource(elementos);
return JasperFillManager.fillReport(report,
misParametros,
source);
} catch (Exception e) {
JOptionPane.showMessageDialog(null,
"Error en la compilación del informe: \n"
+ e.getMessage()
+ "\nContacte con su administrador",
"Error", JOptionPane.ERROR_MESSAGE);
return null;
}
}
Proceda con los numerosos imports en una sola vez con el atajo de teclado [Ctrl][Shift] O (la letra O).
El método cargarYCompilar() realiza las tres primeras etapas mencionadas anteriormente. Recibe como
parámetro el nombre del informe a compilar, los datos para alimentar el informe en forma de colección y
parámetros opcionales.
En este método, se utiliza una colección particular: HashMap que asocia cada elemento de la colección con una
clave única de tipo String (y no con un índice numérico como con los ArrayList).
La anotación del último parámetro permite añadir un número cualquiera de parámetros a un método. Se trata de
lo que se llama los varargs. Es posible, por tanto, invocar a este método de las siguientes maneras:
JasperFacade.cargarYCompilar("nombreDelInforme.jrxml",
Collections.emptyList());
JasperFacade.cargarYCompilar("nombreDelInforme.jrxml",
Collections.emptyList(),
"param1", "valor1");
JasperFacade.cargarYCompilar("nombreDelInforme.jrxml",
Collections.emptyList(),
"param1", "valor1",
"param2", "valor2");
Una vez el objeto JasperPrint creado, es fácil realizar las operaciones de visualización previa, de impresión y de
exportación gracias a las librerías Jasper.
a. Vista previa
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 21/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
El primer método permite, con la ayuda del método estático viewReport() de la clase JasperViewer,
visualizar directamente el informe en una nueva ventana.
Los demás métodos no presentan mayor dificultad, se añaden para cubrir todo el espectro de funcionalidades
esperado.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 22/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
c. Exportar a PDF
Para poder exportar los informes en los diferentes formatos fácilmente, se utilizan los enums de Java.
Cree un nuevo enum FormatoExport en el package control.informe, con ayuda de la opción File New
Enum del menú.
package control.informe;
import java.io.File;
import javax.swing.ImageIcon;
import net.sf.jasperreports.engine.JRAbstractExporter;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.export.SimpleExporterInput;
import net.sf.jasperreports.export.SimpleOutputStreamExporterOutput;
@SuppressWarnings("rawtypes")
JRAbstractExporter crearExporter() {
return null;
}
FormatoExport() {
}
}
Este enum describe los diferentes formatos disponibles para exportar: se dispone de cinco formatos. Ni más, ni
menos.
Para cada uno de estos formatos, existe un método simple export(JasperPrint, File) para exportar un
informe de tipo JasperPrint a un archivo del disco duro de tipo java.io.File.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 23/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
PDF {
@Override
void export(JasperPrint print, File fichero)
throws JRException {
JasperExportManager.exportReportToPdfFile(
print,
fichero.getAbsolutePath());
}
},
DOCX{
@Override
JRDocxExporter crearExporter() {
return new JRDocxExporter();
}
},
Complete el enum con los formatos ODT y ODS. Las clases de exportación son respectivamente
JROdtExporter y JROdsExporter.
Queda llamar al método export() del enum FormatExport desde la clase JasperFacade.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 24/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
formato.name(), e.getMessage()),
"Error", JOptionPane.ERROR_MESSAGE);
}
}
}
Este método abre un cuadro de diálogo que permite elegir el nombre y la ubicación del archivo a guardar gracias
a las líneas:
El código formato.name() permite obtener el nombre del enum (PDF, ODT,...) y la instrucción
toLowerCase() permite transformar este nombre en minúsculas.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 25/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
JOptionPane.ERROR_MESSAGE);
e.printStackTrace();
}
}
El método es muy sencillo y permite efectuar todas las operaciones, y el código de la vista previa es casi el
mismo.
Este método plantea sin embargo un problema: la lectura de los clientes así como la carga y compilación de los
informes pueden demorar cierto tiempo, incluso mucho tiempo.
Esto quiere decir que la visualización gráfica de la aplicación se quedará bloqueada si se llama directamente a
este método desde la acción del botón de imprimir.
Para corregir este comportamiento, se utiliza la clase SwingWorker pero esta vez para la vista previa.
@Override
protected JasperPrint doInBackground()
throws Exception {
List<Cliente> clientes = crud.leer();
return
JasperFacade.cargarYCompilar("Clientes.jrxml",
clientes);
}
@Override
protected void done() {
try {
JasperPrint print = get();
JasperFacade.vistaPrevia(print);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
} catch (ExecutionException e) {
JOptionPane.showMessageDialog(null,
"Ninguna vista previa.\n\n"
+ e.getCause().getMessage(),
"Problema encontrado",
JOptionPane.ERROR_MESSAGE);
}
}
}.execute();
}
Void (con una mayúscula) es una clase Java correspondiente al seudotipo void (con minúsculas).
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 26/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
En modo Design, añada una acción para el botón de vista previa y en el método actionPerformed() de
esta acción la llamada al método vistaPrevia() de la clase ControlCliente.
public AccionVistaPrevia() {
putValue(NAME, "Vista Previa");
putValue(SHORT_DESCRIPTION,
"Vista previa antes de la impresión");
}
El compilador informa de un error ya que el método export() todavía no existe en la clase ControlCliente.
Cree el método export() con la ayuda del Quick Fix de Eclipse. Presione [Ctrl] 1 y elija la opción de
creación de nuevos métodos.
El cuadro de diálogo FExport permite mostrar una lista con los diferentes formatos permitidos.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 27/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
@Override
protected List<Cliente> doInBackground()
throws Exception {
return crud.leer();
}
@Override
protected void done() {
try {
List<Cliente> elementos = get();
FExport laVentana = new FExport(parent,
"clientes",
"Clientes.jrxml",
elementos);
laVentana.setModal(true);
laVentana.setLocationRelativeTo(null);
laVentana.setVisible(true);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
} catch (ExecutionException e) {
JOptionPane.showMessageDialog(null,
"Imposible exportar.\n\n"
+ e.getCause().getMessage(),
"Problema encontrado",
JOptionPane.ERROR_MESSAGE);
}
}
}.execute();
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 28/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Utiliza un modelo por defecto de tipo DefaultListModel, que se rellena con los valores del enum
FormatoExport en el bucle for. Para ello, todos los valores del enum se obtienen mediante el método
values().
package dialogo.aspecto;
import java.awt.Component;
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 29/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
import javax.swing.DefaultListCellRenderer;
import javax.swing.ImageIcon;
import javax.swing.JList;
import control.informe.FormatoExport;
public ExportRenderer() {
super();
}
setIcon(icono);
setText(txt);
return this;
}
}
El texto y la imagen de cada formato de exportación se obtienen desde el enum gracias a los métodos
getDescripcion() y getIcono().
A continuación, quedan por construir las contantes del enum FormatoExport con los parámetros adecuados.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 30/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 31/31
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Gráficos
Se ha elegido JFreeChart para crear gráficos que, como JasperReports, es open source y está enteramente escrito
en Java. JFreeChart es una librería que proporciona una API que permite a Java crear gráficos desde fuentes de
datos y cuya salida es una imagen JPEG o PNG.
Así se dispone de un editor gráfico para dibujar los gráficos, insertar eventuales parámetros y probar el resultado
final como se acaba de hacer con la vista previa, la impresión y la exportación de clientes.
Se crea un gráfico sencillo, que mostrará los porcentajes de número de pedidos por mes para un año dado.
Descargue las librerías de JFreeChart del sitio web de SourceForge en la dirección siguiente:
https://sourceforge.net/projects/jfreechart/files/
Descomprima el archivo y añada las siguientes librerías a las ya presentes en el proyecto luna.
Antes de empezar con la creación del informe, hay que interesarse en el modelo de datos.
Cada estadística posee dos propiedades: el nombre del mes y el número de pedidos de este mes.
Esta clase utiliza el enum Month del package java.time para conseguir el nombre del mes en cuestión.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
package dialogo.estadisticas;
import java.time.Month;
import java.time.format.TextStyle;
import java.util.Locale;
Se inyectará una colección de este tipo de datos en el informe. Las propiedades que interesan en el informe son
por lo tanto contador y mes.
Cree un nuevo adaptador de datos para probar el gráfico sin inyectar datos reales.
package dialogo.estadisticas;
import java.time.Month;
import java.util.ArrayList;
import java.util.List;
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
}
}
Abra el editor de datos, seleccione el adaptador que acaba de crear y elija la clase Estadistica en la pestaña
Java Bean. Seleccione después las propiedades contador y mes.
Añada los campos seleccionados con el botón Add selected field(s) y haga clic en OK.
En la paleta, seleccione el elemento gráfico (con un icono de gráfico circular) y deslícelo hasta la sección
Título.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Seleccione después el gráfico y muestre sus propiedades. En la sección Chart Plot, introduzca el formato de
la etiqueta como: {0}: {1} pedido(s) ({2}).
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
public AccionPedidos() {
putValue(NAME, "Pedidos");
putValue(SHORT_DESCRIPTION,
"Mostrar las estadísticas de pedidos");
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 5/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
estadisticas.setModal(true);
estadisticas.setLocationRelativeTo
(estadisticas.getParent());
estadisticas.setVisible(true);
}
}
El método showInputDialog abre un cuadro de diálogo que permite introducir texto simple.
new Worker(conexion).execute();
}
El constructor define la consulta a realizar para recuperar las estadísticas desde la base de datos.
Después instancia un Worker, que es una instancia de SwingWorker, para recuperar los resultados en segundo
plano.
@Override
protected Collection<Estadistica> doInBackground()
throws Exception {
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 6/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Se almacenan los datos en dos atributos de la clase principal para más comodidad: uno para el gráfico, otro para la
impresión.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 7/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 8/8
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Hay que crear un ejecutable (consulte el capítulo Toma de contacto de Eclipse, sección Primer ejecutable).
Para simplificar la vida respecto a todas las librerías a importar, se utiliza el proyecto Maven para recuperar todas las
dependencias, es decir todos los archivos jar necesarios para que la aplicación funcione.
En efecto un proyecto desarrollado en Eclipse se beneficia del soporte de la aplicación Eclipse en sí misma: las
librerías necesarias y ausentes se añaden de forma transparente. ¡Pero los usuarios de sus aplicaciones futuras no
conocerán tal vez nunca Eclipse! El objetivo es darles una aplicación que sea auto suficiente: uno o dos archivos
únicamente deberían bastar para ejecutar o instalar esta aplicación..
<project>
<modelVersion>4.0.0</modelVersion>
<name>ENI Java 8</name>
<groupId>com.eni</groupId>
<artifactId>java8</artifactId>
<version>1.0.0‐SNAPSHOT</version>
<repositories>
<repository>
<id>central</id>
<name>Maven Repository Switchboard</name>
<layout>default</layout>
<url>http://repo1.maven.org/maven2</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<name>Maven Plugin Repository</name>
<url>http://repo1.maven.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<updatePolicy>never</updatePolicy>
</releases>
</pluginRepository>
</pluginRepositories>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>6.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql‐connector‐java</artifactId>
<version>5.1.35</version>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>com.jgoodies</groupId>
<artifactId>jgoodies‐forms</artifactId>
<version>1.6.0</version>
</dependency>
<dependency>
<groupId>com.miglayout</groupId>
<artifactId>miglayout‐swing</artifactId>
<version>5.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j‐api</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback‐classic</artifactId>
<version>1.1.3</version>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>
UTF‐8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>
maven‐compiler‐plugin</artifactId>
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
<version>3.3</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Este archivo enumera todas las dependencias que necesita la aplicación Luna para funcionar sola.
Seleccione el proyecto y, en el menú contextual, elija la opción Configure Convert to Maven Project.
Al cabo de un momento, una M pequeña decora el icono del proyecto en el explorador de packages y un
nuevo elemento aparece en la lista del proyecto: Maven Dependencies.
Suprima todas las librerías que añadió en el Build Path: ahora están presentes en el proyecto gracias a Maven.
Haga clic en Next. En la siguiente pantalla, verifique que la opción Extract required libraries into generated
JAR está seleccionada.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Sitúe el archivo generado en una carpeta que puede llamar por ejemplo Luna.
Para finalizar, cierre Eclipse y pruebe la aplicación jar verificando que los informes Jasper funcionan.
¿Por qué separar los informes y no incorporarlos en el jar? Porque será más sencillo para hacer modificaciones sobre
los informes en este caso: no necesitará redistribuir la aplicación completa.
Los informes Jasper para los clientes y los artículos están disponibles para la descarga desde la página Información.
Llegados a este punto, se deja a su entender integrarlos en el proyecto Luna.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
Debe ser posible que varios usuarios trabajen simultáneamente sobre los mismos datos.
Las interfaces gráficas de los usuarios deben actualizarse cuando alguien modifica un dato.
¡La recompensa principal de una aplicación bella y agradable de utilizar es aún más trabajo, ya que proporciona
ideas a los usuarios! Espere a recibir feedback de los usuarios...
Con respecto al primer punto descrito, la aplicación debe poder gestionar varios usuarios que puedan encontrarse
en cualquier punto del planeta.
¡Sin embargo, un usuario situado en Buenos Aires o Katmandú no verá las mismas fechas de pedido que un
usuario situado en Madrid!
Esto es debido al hecho de que la información temporal almacenada en la base de datos no tiene en cuenta el huso
horario. La fecha del pedido se almacena con el formato 20150525 21:26:33. ¡Y al recuperar los datos por JPS,
esta fecha está combinada con el huso horario local del usuario!
Cambiar en la base de datos el formato de la fecha y hora por un dato de tipo long que representará el número de
milisegundos (o de segundos) transcurridos desde el 1 de Enero de 1970 a medianoche UTC.
Esta solución no es muy conveniente ya que entonces será imposible crear de manera sencilla el gráfico de
estadísticas. Para esto hay que guardar las fechas con el formato SQL TIMESTAMP.
Almacenar la información de huso horario en una columna adicional y combinar ambas informaciones para obtener
una fecha uniforme.
Esta solución tampoco es conveniente ya que impone también una gimnástica algorítmica al generar el informe de
estadísticas.
Llegados a este punto, se puede constatar que almacenar las fechas en base de datos es en realidad un
LocalDateTime según Java 8: una información temporal sin huso horario. Es por lo tanto posible evolucionar la
clase Cliente para sacar partido de esta situación y almacenar la fecha del pedido teniendo en cuenta el huso
horario UTC (ya que de todas maneras hay que elegir un huso horario, mejor coger el que esté normalizado).
...
private LocalDateTime fecha;
...
public Instant getInstant() {
return ZonedDateTime.of(fecha,
ZoneId.of("UTC")).toInstant();
}
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 1/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
setFecha(LocalDateTime.now(ZoneId.of("UTC")));
}
...
JPA no permite de manera nativa convertir un objeto de tipo LocalDateTime. Debe usar un convertidor.
package control.utilidades;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@Converter(autoApply = true)
public class ConvertidorLocalDateTime implements
AttributeConverter<LocalDateTime, Timestamp> {
@Override
public Timestamp convertToDatabaseColumn(
LocalDateTime entityValue) {
return Timestamp.valueOf(entityValue);
}
@Override
public LocalDateTime convertToEntityAttribute(
Timestamp databaseValue) {
return databaseValue.toLocalDateTime();
}
}
Después se anota esta clase con @Converter para que JPA lo tenga en cuenta. El parámetro autoApply permite
usar este convertidor para todos los atributos de tipo LocalDateTime en la aplicación.
...
<class>entidad.Linea</class>
<class>control.utilidades.ConvertidorLocalDateTime</class>
...
Si no es posible convertir automáticamente todos los tipos, las propiedades pueden anotarse con @Convert.
Por ejemplo, la propiedad fecha de la clase Pedido se puede anotar de la siguiente manera:
@Convert(converter=ConvertidorLocalDateTime.class)
private LocalDateTime fecha;
Las clases Articulo y Cliente deben también modificarse para que el atributo de fecha sea de tipo LocalDate.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 2/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
...
private LocalDate fecha;
...
public Instant getFecha() {
return ZonedDateTime.of(
fecha.atStartOfDay(),
ZoneId.of("UTC"))
.toInstant();
}
...
public void setFecha(Instant fecha) {
this.fecha = LocalDateTime.ofInstant(fecha,
ZoneId.of("UTC"))
.toLocalDate();
}
...
@Deprecated
public Date getCreacion() {
return Date.from(getFechaCreacion());
}
...
private LocalDate fecha;
...
public Instant getFechaCreacion() {
return ZonedDateTime.of(
fecha.atStartOfDay(),
ZoneId.of("UTC"))
.toInstant();
}
...
public void setFechaCreacion(Instant fecha) {
this.fecha = LocalDateTime.ofInstant(fecha,
ZoneId.of("UTC"))
.toLocalDate();
}
...
@Deprecated
public Date getCreacion() {
return Date.from(getFechaCreacion());
}
package control.utilidades;
import java.sql.Date;
import java.time.LocalDate;
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 3/4
16/12/2016 www.enitraining.com/client_net/pdfexport.aspx?exporttype=1
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@Converter(autoApply = true)
public class ConvertidorLocalDate implements
AttributeConverter<LocalDate, Date> {
@Override
public Date convertToDatabaseColumn(LocalDate entityValue) {
return Date.valueOf(entityValue);
}
@Override
public LocalDate convertToEntityAttribute(Date databaseValue) {
return databaseValue.toLocalDate();
}
}
...
<class>entidad.Linea</class>
<class>control.utilidades.ConvertidorLocalDate</class>
...
Esta iteración para crear una nueva funcionalidad (o corregir un bug, según se mire) ha necesitado la modificación
de tres clases y la creación de dos nuevas clases. El resto del código de la aplicación ha quedado como estaba y esto
gracias a la elección de un diseño adecuado al crear el código fuente.
http://www.enitraining.com/client_net/pdfexport.aspx?exporttype=1 4/4