Beruflich Dokumente
Kultur Dokumente
Desarrollo
componentes de
negocio con tecnología
empresarial JavaBeans
Profesional en
Plataforma
JAVA
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Contenido
Introducción y Objetivos ........................................................................................................ 1
Unidad 1. Análisis de Aplicaciones EJB...................................................................................... 2
Java y arquitectura de capas ............................................................................................... 2
Modulo WAR ...................................................................................................................... 3
Comunicación Capa Web+Capa Negocio ................................................................................ 4
Tecnología Enterprise Java Beans ......................................................................................... 5
Tipos de beans ................................................................................................................... 6
Especificaciones ................................................................................................................. 8
¿Qué servicios proveen los EJBs? ......................................................................................... 8
Evolución de la especificación EJB......................................................................................... 9
EJB 3.0 ........................................................................................................................... 10
Compatibilidad con EJB 2.x ................................................................................................ 11
Prestaciones y servicios de EJB 3........................................................................................ 12
Roles EJB en el equipo de desarrollo: .................................................................................. 13
Ventajas de la tecnología EJB ............................................................................................. 14
Unidad 2. Introducción a la Aplicación de Subasta ................................................................... 16
Introducción a los beans de sesión o “sessión beans” ............................................................ 16
Tipos de acceso: local, remoto o servicio web ...................................................................... 16
Patrones, Java y EJB ......................................................................................................... 18
Tipos de bean de sesión .................................................................................................... 20
¿Cuándo usar stateless, statefull o singleton? ...................................................................... 21
Invocación remota de un bean de sesión ............................................................................. 22
Invocación local de un bean de sesión ................................................................................. 23
Unidad 3. Implementación de los Beans de Sesión de EJB 3.0 ................................................... 25
Sesión con estado o stateful Session Bean ........................................................................... 25
La Clase Bean .................................................................................................................. 26
Ciclo de vida del bean con estado ....................................................................................... 28
Los bean sin estado o Stateless Session Bean ...................................................................... 29
Ciclo de vida bean sin estado ............................................................................................. 31
Acceso Local y remoto de un bean de sesión sin estado ........................................................ 31
Unidad 4. Implementación de Clases de Entidad. Conceptos Básicos .......................................... 33
Introducción a los beans de entidad .................................................................................... 33
Características generales de la API de Persistencia en Java .................................................... 35
Diferencias con los beans de sesión .................................................................................... 40
Requisitos para clases de entidad ....................................................................................... 41
Campos y Propiedades persistentes en clases de entidad ...................................................... 41
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Sobre la identidad de los objetos ........................................................................................ 43
Unidades de persistencia ................................................................................................... 44
Contextos de persistencia .................................................................................................. 45
Ciclo de vida de los Entity Beans ........................................................................................ 45
Entity Manager................................................................................................................. 45
Obteniendo un Entity Manager ........................................................................................... 46
Obteniendo un Entity Manager en aplicaciones JSE. .............................................................. 47
Persistiendo objetos.......................................................................................................... 47
Transacciones usando JPA en aplicaciones JSE. .................................................................... 47
Operaciones básicas ......................................................................................................... 48
Persist ............................................................................................................................ 48
Remove .......................................................................................................................... 48
Refresh ........................................................................................................................... 48
Merge ............................................................................................................................. 49
Búsquedas simples ........................................................................................................... 49
Laboratorio: EJB de estado ................................................................................................ 49
Unidad 5. Implementación de Clases de Entidad: Modelado de Relaciones de Asociación de Datos . 57
Las claves principales de las entidades ................................................................................ 57
Variedad de relaciones entre entidades ............................................................................... 58
Dirección de Relaciones de Entidad ..................................................................................... 59
Múltiples claves primarias en una entidad ............................................................................ 60
Relaciones entre entities ................................................................................................... 62
Operaciones en cascada .................................................................................................... 69
Estrategias de recuperación de relaciones ........................................................................... 70
Consecuencias del uso de relaciones LAZY ........................................................................... 70
Laboratorio: EJB de entidad ............................................................................................... 71
Unidad 6. Implementación de Clases de Entidad: Modelado de Relaciones de Herencia ................ 78
Jerarquías de clases.......................................................................................................... 78
Superclases no persistentes ............................................................................................... 78
Estrategias de persistencia para jerarquías de clases ............................................................ 79
Unidad 7. Uso del Lenguaje de Consulta (Ql) de Java Persistence .............................................. 86
Interfaz Query y los objetos derivados de ella ...................................................................... 87
getSingleResult() ............................................................................................................. 90
executeUpdate() .............................................................................................................. 91
setMaxResults() y setFirstResult() ...................................................................................... 91
setHint() ......................................................................................................................... 91
setParameter(...) ............................................................................................................. 91
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
setFlushModel() ............................................................................................................... 92
Composición del lenguaje QL ............................................................................................. 93
Sentencia Where .............................................................................................................. 95
Literales:......................................................................................................................... 95
Parámetros de entrada: .................................................................................................... 95
Sentencia ORDER BY ........................................................................................................ 97
Restricciones ................................................................................................................... 97
Laboratorio: Persistencia POJO ........................................................................................... 97
Unidad 8. Desarrollo de Aplicaciones Java EE Mediante el Uso de Mensajes ................................ 111
Dominios de Mensajes ..................................................................................................... 112
Dominio punto a punto(PTP) ............................................................................................. 112
Qué son las colas de mensajes .......................................................................................... 113
La cola de mensajes ........................................................................................................ 113
El API de JMS. Tipos de destinos ....................................................................................... 113
Objetos administrativos ................................................................................................... 114
Clientes JMS ................................................................................................................... 117
Unidad 9. Desarrollo de Beans Controlados por Mensajes ........................................................ 118
Beans dirigidos por mensajes o Message Driven Bean (MDB) ................................................ 118
Diseño de una clase para un MDB: .................................................................................... 119
Ciclo de Vida de MDB ....................................................................................................... 121
Diferencias con los beans de sesión y de entidad ................................................................. 121
Un ejemplo completo ....................................................................................................... 122
Laboratorio: Crear Servicio Mensajería ............................................................................... 124
Unidad 10. Interceptores ..................................................................................................... 135
Introducción. Interceptores y Entity Listener ....................................................................... 135
Interceptor ..................................................................................................................... 137
Deployment Descriptor..................................................................................................... 139
Entity Listener ................................................................................................................ 141
Unidad 11. Transacciones .................................................................................................... 144
Container Managed Transaction ........................................................................................ 146
@TransactionAttribute ..................................................................................................... 146
Bean Managed Transaction ............................................................................................... 150
Unidad 12. Excepciones....................................................................................................... 152
Manejo de excepciones en el contenedor ............................................................................ 154
Excepciones manejadas por el bean ................................................................................... 155
Manejo de excepciones por el cliente ................................................................................. 156
Excepciones en transacción .............................................................................................. 157
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Unidad 13. Temporizador .................................................................................................... 159
TimedObject y @Timeout ................................................................................................. 159
Interfaz TimerService ...................................................................................................... 161
Interfaz Timer ................................................................................................................. 162
Creación de un objeto timer.............................................................................................. 163
Recomendaciones finales .................................................................................................. 164
Unidad 14. Seguridad ......................................................................................................... 166
Autorización declarativa ................................................................................................... 168
Autorización programática ................................................................................................ 171
Responsabilidades del administrador .................................................................................. 174
Unidad 15. Uso de las Mejores Prácticas de la Tecnología EJB .................................................. 176
Con o sin estado ............................................................................................................. 176
Sin estado, pero ¿qué ocurre con los atributos?................................................................... 178
Con estado, pero ¿qué ocurre con el rendimiento? ............................................................... 178
Elegir ............................................................................................................................. 178
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Introducción y Objetivos
Introducción
La tecnología Enterprise JavaBeans incorpora una serie de mejoras como los beans de sesión únicos,
la vista interfaz, Java Naming and Directory Interface (JNDI), los beans de sesión asíncronos y el
servicio de temporizador, las cuales vienen a simplificar el proceso de crear componentes
empresariales.
Objetivo
Este módulo proporcionará los conocimientos para consturir aplicaciones robustas con la tecnología
Enterprise JavaBeans.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Unidad 1. Análisis de Aplicaciones EJB
Objetivo
Conocer los conceptos básicos que definen la programación de Enterprise JavaBeans y su
arquitectura.
Como observamos entonces ya tenemos una capa claramente definida que es la del Cliente en el
cual se realizaran todas las peticiones a las capas superiores y estas a su vez enviaran las
respuestas a esas solicitudes.
Pero definiendo aun más tenemos que:
El servidor web es el encargado de realizar todos los procesos lógicos como crear consultas
crear formularios, ejecutar procesos, etc, un servidor web comúnmente utilizado es Apache.
El Servidor de Bases de Datos es en el que, como su nombre lo dice, tenemos todas las
tablas, vistas y consultas que necesitamos para nuestro sistema, un servidor común es
PostgreSQL.
Y la seguridad junto con las transacciones que contienen toda la lógica de negocio entre la
aplicación y la base de datos.
Ahora que sucede en la arquitectura de tres capas, tenemos las transacciones con el servidor web
realizándose en un mismo lugar, o en este caso una misma capa, y los datos se mantienen a su vez
también en una capa aun más superior y alejada del cliente, para dar un nivel más de seguridad.
Observemos el siguiente dibujo, en el cual ya se definen las 3 capas básicas del desarrollo de
aplicaciones web:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
En esta arquitectura lo principal es que, separamos la segunda capa en dos: Capa Web que queda
solamente con el Servidor WEB y las Transacciones se realizan en la capa de Negocio:
Esto nos permitirá un mejor control de todos los elementos que entran en juego a la hora de
desarrollar aplicaciones Web.
Las aplicaciones J2EE se basan completamente en el modelo de aplicaciones de 4 niveles.
Todas las aplicaciones constan de 3 partes básicas, que son:
Módulos EJB: encargado de tener la lógica del negocio y transacciones. En otras palabras
podemos decir que es el encargado de ejecutar programas, consultas a la base de dato
principalmente. (capa Negocio)
Modulo WAR: que es la encargada de tener todos los elementos de interfaz como páginas
web, servlets, applets. (capa WEB).
Aplicación EAR: contiene en su interior toda la configuración de la aplicación J2EE, eso
incluye el modulo WAR y EJB.
Modulo WAR
Este modulo como dijimos anteriormente es el encargado de la comunicación Cliente-Servidor WEB,
en el realizan las peticiones a las capas superiores, en el tenemos tres elementos básicos que son
los:
JSP: Java Server Pages (pagina web java)
Servlets: Componente web desarrollada con el objetivo de procesar requerimientos de un cliente o
requests y genera respuestas con contenido web dinámico. Para ser ejecutados es necesaria la
utilización de un servidor que soporte Servlets y su container.
Aplicaciones Java: códigos Java en su forma pura, que tienen métodos y atributos y que pueden ser
utilizados en aplicaciones JSP y Servlets.
Muchas veces se tiende a utilizar los JSP de la misma forma que paginas PHP, lo cual no es
recomendable debido a la gran cantidad de código que se genera y lo cual produce mucho desorden
por esto se hará gran énfasis a respetar la arquitectura J2EE, en donde debemos tener cada parte
donde corresponde, vale decir, Interfaces en la capa web, peticiones y respuestas en la capa de
negocio y las conexiones de la base de datos en la capa de datos.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Comunicación Capa Web+Capa Negocio
Cuando se comienza el desarrollo de estas aplicaciones, cuesta un poco saber donde realizar la
comunicación de cada una de las capas, como dijimos anteriormente uno tiene a escribir todo ese
código en las aplicaciones JSP, lo que no era recomendable, por lo que debemos hacerlo de la
siguiente forma:
Se deben utilizar los Servlets como ente comunicador de ambas capas, para poder mantener la
arquitectura de la aplicación y el orden.
Más adelante veremos que esta comunicación se realiza de una forma muy sencilla en la que la
herramienta de desarrollo, en este caso NetBeans, la realiza en 2 sencillos pasos.
Cabe señalar que es preciso tener conocimientos previos de HTML y de transferencia de información
entre páginas web, para poder llevar a cabo el desarrollo de la aplicación en la capa web.
Entonces a modo de resumen, Como conocimiento básico, se conoce el modelo de 3 capas, en el
cual tenemos, el cliente o navegador, servidor web y la capa de datos:
Tenemos, un navegador, que es el lugar donde se encuentra el cliente. Un servidor web, que es
donde se ejecutan las operaciones de negocio y comunicaciones con la BD, y se genera el HTML
dinámico que posteriormente será mostrado en los navegadores.
Servidor de Bases de Datos, donde se encuentra toda la información y de donde se retornan las
consultas.
Para el modelo de 4 capas tenemos que las operaciones de negocio y generación de HTML dinámico
que se envían al navegador se encuentran separadas.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Y en donde entran en juego elementos como los Servlets y EJB.
Dicho contenedor de componentes viene a ser una suerte de sistema operativo donde residen los
diferentes componentes. Hemos visto que en Java existe un modelo de programación de objetos
remotos denominado RMI. Pues bien, con RMI es posible enviar peticiones a objetos que están
ejecutándose en otra máquina virtual Java. De este modo un componente EJB sería un objeto
remoto RMI que reside en un contenedor EJB, el cual le proporciona un conjunto de servicios
adicionales.
Dada esta arquitectura de desarrollo tan particular, cuando desarrollemos componentes deberemos
dedicarle bastante atención al despliegue (deployment) del componente tanto como a su desarrollo.
El despliegue puede definirse como la incorporación del componente a nuestro contenedor EJB y a
nuestro entorno de trabajo (bases de datos, arquitectura del software, etc.). El despliegue se define
de forma declarativa, mediante un fichero XML (descriptor del despliegue, deployment descriptor) en
el que se definen todas las características del bean.
Una de las virtudes de esta forma de operar mediante componentes es que la industria ha generada
diversas expectativas comerciales. Así, han surgido diferentes empresas dedicadas a implementar y
vender componentes específicos a terceros. Aun así es difícil crear componentes genéricos que
pueden emplearse en multitud de ocasiones. Lo habitual es que cada dominio de un problema o
aplicación exija sus propios y específicos componentes, adaptados a sus necesidades y dificultades
concretas. La estandarización aunque deseable, resulta ciertamente complicada. No obstante hay un
cierto campo de negocio para este tipo de desarrollos como puede verse en esta web:
http://www.componentsource.com
Dentro de Java existe la especificación de Enterprise JavaBeans (EJBs), la cual está destinada a
ofrecer una arquitectura para el desarrollo y estructuración de aplicaciones de procesamiento de
transacciones, basados en objetos distribuidos y componentes de software del lado del servidor.
Pues bien, dichos componentes son llamados enterprise beans. Es decir, los objetos distribuidos que
se encuentran en Enterprise JavaBeans Containers y proveen servicios remotos a clientes
distribuidos a través de la red.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Como puede verse en la ilustración anterior, un Enterprise javabeans no es lo mismo que un java
beans:
- Javabeans es un componente utilizado en Java que permite agrupar funcionalidades que
forman parte de una aplicación.
- Puede agrupar información personal, datos sobre una petición, requerimientos de órdenes,
etc.
- Requiere ser integrado con otros componentes para ser funcional.
En general, un bean es una clase que obedece ciertas reglas:
- Debe tener un constructor por defecto y sin argumentos.
- Debe tener persistencia, es decir, implementar el interface Serializable.
- Debe tener introspección. Los IDE reconocen ciertas pautas de diseño, nombres de las
funciones miembros o métodos y definiciones de las clases, que permiten a la herramienta de
programación mirar dentro del bean y conocer sus propiedades y su conducta.
Tipos de beans
Cada EJB contiene muchas aplicaciones, entonces a cada uno de esos pequeños paquetes o beans
del EJB que contienen toda una lógica de programación para realizar alguna actividad en particular.
Existen tres tipos de Beans que son:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Entonces más detalladamente tenemos:
Beans de Sesión: representan sesiones interactivas con uno o más clientes, pueden mantener un
estado, pero solo durante el tiempo que el cliente interactúa con el bean. Esto significa que los
beans de sesión no almacenan sus datos en una BD después que el cliente termine el proceso. Se
dividen en dos
- Beans de sesión Con Estado: almacenan datos específicos obtenidos durante la conexión
con el cliente, cada bean de sesión con estado por lo tanto almacena el estado conversacional
de un cliente con el que interactúa. Este estado conversacional se modifica conforme el
cliente va realizando llamadas a los métodos de negocio del bean. El estado conversacional
no se guarda cuando el cliente termina la sesión. Ósea este beans usado va cambiando
conforme el cliente lo necesite, algunos métodos que contienen estos son los set y los get, un
ejemplo claro es el carrito de compras que a medida que vamos avanzando se le van
agregando más cosas, o en un inicio de sesión cuando necesitamos tener en alguna parte los
datos de la persona que ha hecho login.
- Beans de sesión Sin Estado: no se modifican con las llamadas de los clientes solo reciben
datos y devuelven resultados pero no modifican internamente el estado del bean. Estos
beans por lo general son usados para encapsular procesos de negocio, más que datos de
negocio (tarea de los beans de entidad), también puede usarse como puente de acceso a una
BD o a un bean de entidad. Cuando trabajamos con cliente-servidor estos beans podrían
proporcionar al interface de usuario los datos necesarios, estos beans de sesión sin estado
son de uso muy frecuente.
Beans de Mensajes: pueden escuchar mensajes de un servicio de mensajes JMS, estos nunca se
comunican directamente con el cliente y no necesitan objetos EJBObject. Un ejemplo podría ser
ListenerNuevoCliente que se activa cada vez que se envía un mensaje que se ha ingresado un nuevo
cliente.
Beans de Entidad: modelan conceptos o datos de negocio, que pueden expresarse como nombres.
Representan “cosas”, objetos del mundo real como Hoteles, habitaciones, expedientes, estudiantes y
otros. Un bean de entidad puede representar incluso cosas abstractas como una reserva, describen
tanto el estado como la conducta de objetos del mundo real y permite a los desarrolladores
encapsular reglas de datos y negocio asociadas con un concepto específico. Por ejemplo un bean de
entidad Estudiante encapsula los datos y reglas de negocio asociadas a un estudiante. Esto hace
posible manejar de forma consistente y segura los datos asociados a un concepto. Los beans pueden
contener información por ejemplo de un estudiante sin necesariamente estar conectados con una
BD. El contenedor se encarga de sincronizar las variables de instancia del bean con la BD. Debido a
que los beans de entidad se guardan en un mecanismo de almacenamiento se dice que es
persistente, Persistente significa que el estado del bean existe más tiempo que la duración de la
aplicación. Estos beans se dividen en dos:
- Persistencia gestionada por el Bean (BMP): contienen el código que accede a la BD.
- Persistencia Gestionada por el Contenedor (CMP): la relación entre las columnas de la
BD y el bean se describe en el fichero de propiedades del bean, y el contenedor EJB se ocupa
de la implementación.
Cada uno de ellos será objeto de minucioso análisis en las próximas unidades. Ahora, de momento,
veamos en la siguiente ilustración la posición que ocupan los diferentes tipos EJBs dentro de la capa
de negocio o Business Tier de una aplicación. Al mismo tiempo, observamos dónde deberían
colocarse opcionalmente los componentes JavaBeans en la misma arquitectura de la aplicación:
JavaBeans Components.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Especificaciones
La especificación exige un modelo de programación constituido por convenciones o protocolos y un
conjunto de clases e interfaces sobre las cuales de apoya el EJB API. El modelo de programación de
EJB provee a los desarrolladores y proveedores de beans, un conjunto de contratos que definen un
estándar común para el desarrollo. El principal logro de estos contratos es asegurar la portabilidad
entre distintos proveedores y así soportar un gran conjunto de funcionalidades.
Más concretamente, podríamos decir que Enterprise Java Beans (EJB) es una plataforma para
construir aplicaciones de negocio portables, reusables y escalables usando el lenguaje de
programación Java. Desde el punto de vista del desarrollador, un EJB es una porción de código que
se ejecuta en un contenedor EJB, que no es más que un ambiente especializado (runtime) que
provee determinados componentes de servicio.
Los EJBs presentan una doble naturaleza:
Son componentes: desde el punto de vista que encapsulan comportamiento y permite
reutilizar porciones de código.
Son una suerte de framework: desplegados dentro de un contenedor, proveen servicios para
el desarrollo de aplicaciones enterprise, servicios que son invisibles para el programador y no
ensucian la lógica de negocio con funcionalidades trasversales al modelo de dominio (a
menudo requerimientos no funcionales o aspectos).
La ventaja de estos componentes radica en que no tenemos que desarrollarlos nosotros, sino que
podemos reutilizarlos para que incorporen todas estas características. Evidentemente, si en la
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
aplicación que estemos desarrollando no resultan necesarios estos servicios porque sólo vamos a
emplear una interfaz web, bastará utilizar simplemente páginas JSP y JDBC.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
EJB 3.0
El objetivo de Enterprise JavaBeans (EJB) 3.0 es simplificar el desarrollo de aplicaciones Java y
estandarizar el API de persistencia para la plataforma Java. EJB 3.0 está cambiando drásticamente
el modelo de programación de EJBs.
En este sentido, EJB 3.0 es una tecnología basada en J2EE que proporciona una arquitectura
escalable, transaccional y multiusuario. No obstante, su complejidad es un obstáculo para su
adopción por lo que herramientas como Hibernate y Toplink que han sido adoptadas como
soluciones en la capa del modelo para la persistencia, antes de EJB 3.0.
No obstante, con la introducción de EJB 3.0 se consigue:
- La estandarización de una estructura de persistencia (EJB es una especificación no un
Framework ni una herramienta).
- Hace más fácil el desarrollo de EJB.
El objetivo de Enterprise JavaBeans (EJB) 3.0 es simplificar el desarrollo de aplicaciones Java y
estandarizar el API de persistencia para la plataforma Java.
- Elimina la necesidad de construir interfaces EJB y descriptores de despliegue innecesarios, y
en lugar de esto, permite que los desarrolladores los generen especificando anotaciones
(similar a XDoclet). Esta aproximación libera a los desarrolladores de crear descriptores de
despliegue y de crear interfaces para los componentes
- Simplifica los EJBs para asemejarse a los POJOs o Java Beans.
- Elimina la necesidad de tener interfaces de componentes para los EJBs.
- Los beans de entidad no requieren ninguna interfaz de componente.
- Los beans de entidad utilizarán ahora una interfaz POJO.
- Elimina la necesidad de implementar métodos callback de ciclo de vida innecesarios.
En la especificación 3.0, los EJB no son más que POJOs (clases planas comunes y corrientes de
Java) con algunos poderes especiales implícitos, que se activan en runtime cuando son ejecutados
en un contenedor de EJBs.
Los servicios que debe proveer el contenedor de EJBs deben ser especificados por el programador a
través de metadata de configuración que puede escribirse como:
Anotaciones de Java5 intercaladas en el código de las clases.
Descriptores XML (archivos XML separados).
A partir de EJB 3 se puede usar cualquiera de estas dos técnicas. Las técnicas no son exclusivas,
pueden coexistir anotaciones con descriptores XML y, en el caso de superponerse la metadata, los
XML tendrán prioridad y podrán sobrescribir las anotaciones.
Una anotación transforma un simple POJO en un EJB.
Algunos ejemplos de los contenedores más populares que hay actualmente en el mercado son:
Glassfish, de Sun Microsystem, JBoss Application Server, de Red Hat, BEA Weblogic Server y Oracle
Application Server, ambos de Oracle y WebSphere de IBM.
Como hemos dicho, hasta cierto punto la nueva especificación EJB 3.0 supone el fin para los
descriptores XML. Se incorpora el uso de metadatos, denominados anotaciones agregadas en JDK
5.0 como parte de la especificación JSR 175 JCP.
Dichas anotaciones son un tipo de programación orientado a atributos. Una idea similar a XDoclet
pero ahora sometida la especificación. Al igual que existen los modificadores public/private de Java,
las anotaciones pueden ser usadas en clases, campos y métodos.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Ejemplo:
import javax.ejb.*;
@Stateless
public class MyAccountBean implements MyAccount
{
@Tx(TxType.REQUIRED)
@MethodPermission({"customer"})
public void deposit(double money) {...}
}
@Stateless nos indica que el bean es stateless.
@Tx es un atributo que indica la demarcación transaccional para el método deposit.
@MethodPermission especifica a los usuarios quienes están permitidos para usar el método deposit.
Cambios generales:
- EJB 3.0 elimina la necesidad de las interfaces home y de componente, y el requisito de que
las clases bean implementen la interfaz javax.ejb.EnterpriseBean. Los EJBs pasan a ser ahora
POJOs.
- Simplificación de la configuración a través de valores por defecto y del uso de anotaciones de
metadatos en lugar de descriptores de despliegue.
- Se soporta la inyección de dependencias para utilizar EJBs, recursos y variables de entorno.
- Soporte métodos de ciclo de vida mejorados, y de clases listener para callbacks.
- Soporte de métodos de intercepción.
- Búsqueda e invocación de métodos simplificada.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Prestaciones y servicios de EJB 3
EJB es el único estándar Java que se ocupa de la lógica de negocio del lado del servidor, y esto es
fundamental para J2EE. El comité de especificación de EJB reconoce que EJB se quedó corto en
alcanzar algunas de sus ambiciosas metas, y necesitó de una modernización. Esta modernización
está muy enfocada en la facilidad de uso, principalmente mediante la simplificación de los
requerimientos para los desarrolladores.
Sin embargo, el comité de especificación también ha identificado un número de mejoras funcionales
críticas que facilitan el uso y la eliminación de ciertos anti-patrones J2EE.
Afortunadamente, la comunidad Java conoce ahora mucho más los problemas relacionados a la
construcción de aplicaciones web y empresariales que hace cinco años.
El comité de especificación pensó mucho como simplificar ciertos casos de usos generales
extremadamente comunes, como por ejemplo:
Actualizando datos
1. recuperar un dato
2. mostrarlo en pantalla
3. aceptar la entrada del usuario
4. actualizar el dato
5. comunicar el éxito al usuario
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Mensajería: Mediante los MDBs es posible desacoplar por completo dos componentes para
que se comuniquen de forma asincrónica, sin reparar demasiado en los mecanismos de la
JMS API que los MDBs encapsulan.
Transacciones: EJB soporta el manejo de transacciones declarativas que permiten agregar
comportamiento transaccional a un componente simplemente usando anotaciones o XMLs de
configuración. Esto significa que cuando un método de un EJB (Session Bean o MDB) se
completa normalmente, el contenedor se encargará de confirmar la transacción y efectuar los
cambios que se realizaron en los datos de forma permanente. Si algo fallara durante la
ejecución del método por causa de una excepción o de cualquier otro problema, la
transacción haría un rollback, de modo que es la aplicación actuaría como si el método jamás
se hubiera invocado.
Seguridad: EJB soporta integración con la Java Authentication and Authorization Service
(JAAS) API, haciendo casi transparente el manejo transversal de la seguridad. Esta
característica es aplicable a todos los Session Beans.
Interceptors: EJB introduce un framework liviano y simple para AOP (programación
orientada a aspectos). No es tan robusto y completo como otros, pero es lo suficientemente
útil para que sea utilizado por los demás servicios del contenedor para brindarnos de forma
invisible los crosscutting concerns de seguridad, transacciones, thread-safely. Además,
nosotros, como programadores, podemos agregar nuevos aspectos como logging o auditoria
y demás de forma configurable.
Acceso Remoto: Es posible acceder de forma remota a distintos EJBs de forma sencilla,
simplemente mediante la Inyección de Dependencia. El procedimiento para inyectar un
componente local o uno remoto es exactamente el mismo, abstrayéndonos de las
complicaciones específicas de RMI o similares. Este servicio aplica únicamente a los Session
Beans.
Web Services: Un Stateless Session Bean puede publicar sus métodos como web services
mediante una sencilla anotación.
Persistencia: EJB 3 provee la especificación JPA para el mapeo de objetos (Entities) a
tablas.
Catching and Performance: JPA provee de forma transparente un importante número de
servicios que permiten usar un caché de entidades en memoria y una lectura y escritura
sobre la base de datos altamente performante.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Administrador del sistema: configura y administra la infraestructura de computación y de
red del negocio
Proporcionador del Contenedor EJB y Proporcionador del Servidor EJB: un fabricante
(o fabricantes) especializado en manejo de transacciones y de aplicaciones y otros servicios
de bajo nivel. Desarrollan el servidor de aplicaciones.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
hacen posible la integración de los enterprise beans con sistemas no Java, como sistemas
ERP o aplicaciones mainframes.
Recursos educativos y herramientas de desarrollo. El hecho de que la especificación
EJB sea un estándar hace que exista una creciente oferta de herramientas y formación que
facilita el trabajo del desarrollador de aplicaciones EJB.
Entre las ventajas que aporta esta arquitectura al cliente final, destacamos la posibilidad de elección
del servidor, la mejora en la gestión de las aplicaciones, la integración con las aplicaciones y datos
ya existentes y la seguridad.
Elección del servidor. Debido a que las aplicaciones EJB pueden ser ejecutadas en
cualquier servidor J2EE, el cliente no queda ligado a un vendedor de servidores. Antes de que
existiera la arquitectura EJB era muy difícil que una aplicación desarrollada para una
determinada capa intermedia (Tuxedo, por ejemplo) pudiera portarse a otro servidor. Con la
arquitectura EJB, sin embargo, el cliente deja de estar atado a un vendedor y puede cambiar
de servidor cuando sus necesidades de escalabilidad, integración, precio, seguridad, etc. lo
requieran.
Existen en el mercado algunos servidores de aplicaciones gratuitos (JBOSS, el servidor de
aplicaciones de Sun, etc.) con los que sería posible hacer unas primeras pruebas del sistema,
para después pasar a un servidor de aplicaciones con más funcionalidades.
Gestión de las aplicaciones. Las aplicaciones son mucho más sencillas de manejar
(arrancar, parar, configurar, etc.) debido a que existen herramientas de control más
elaboradas.
Integración con aplicaciones y datos ya existentes. La arquitectura EJB y otras APIs de
J2EE simplifican y estandarizan la integración de aplicaciones EJB con aplicaciones no Java y
sistemas en el entorno operativo del cliente. Por ejemplo, un cliente no tiene que cambiar un
esquema de base de datos para encajar en una aplicación. En lugar de ello, se puede
construir una aplicación EJB que encaje en el esquema cuando sea desplegada.
Seguridad. La arquitectura EJB traslada la mayor parte de la responsabilidad de la seguridad
de una aplicación del desarrollador de aplicaciones al vendedor del servidor, el Administrador
de Sistemas y al Desplegador (papeles de la especificación EJB) La gente que ejecuta esos
papeles están más cualificados que el desarrollador de aplicaciones para hacer segura la
aplicación. Esto lleva a una mejor seguridad en las aplicaciones operacionales.
Actividades
“Recuerde que para un seguimiento óptimo de la unidad es imprescindible realizar las actividades
que encontrará en la unidad correspondiente de la plataforma eLearning”.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Unidad 2. Introducción a la Aplicación de
Subasta
Objetivo
Conocer los conceptos básicos que definen la programación de Enterprise JavaBeans en su faceta de
sesión.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
- Tipo de cliente: si los beans son accedidos por aplicaciones clientes, debería permitir acceso
remoto (ya que general mente están fuera del servidor que corre a los beans). Si los beans
son accedidos por componentes web u otros Enterprise beans, el tipo de acceso depende de
cómo se desee distribuir los componentes.
- Distribución de los componentes: depende de la escala del sistema (uno o muchos
computadores diferentes al de computador donde el Enterprise vean fue desplegado). En un
escenario distribuido debería ser remoto.
- Rendimiento: siempre se debe buscar el mejor rendimiento de la aplicación. Hay que
considerar retardo por red en sistemas distribuidos
Puede ser declarado el acceso como local con la anotación @Local y después puede ser accedida
por otros beans en el mismo contenedor EJB. Mientras que para declarar un acceso remoto
usaremos la anotación @Remote y después puede ser accedida por las aplicaciones fuera del
contenedor de EJBs. No necesita declarar excepciones remotas de RMI, porque puede ser generada
desde una clase de bean por el contenedor.
Si todos los métodos de la clase del bean van a estar disponibles para los clientes, cuando un bean
invoca un método en la interfaz de negocio, la interacción actúa como un sustituto, no con la clase
del vean.
El sustituto, entonces, encamina la invocación y responde a través del contenedor y este inyecta
servicios del middleware basados en metadatos del bean. Esos metadatos vienen especificados
como anotaciones o en el descriptor de despliegue XML.
En resumen, las características básicas de un bean de sesión serían las siguientes:
Un ciclo de vida corto porque si el servidor falla la sesión se pierde.
No manejan persistencia.
No es compartido entre clientes
Pueden actualizar y crear entidades, estas últimas son persistentes.
Un cliente (WEB como JSP o servlet y un cliente aplicación standalone) interactúa con un
session bean a través de la invocación de sus métodos (esta invocación se llama sesión)
Un componente session bean es un POJO anotado.
Un session bean está compuesto por una o más interfaces y una clase de implementación.
Un cliente puede acceder a un session bean solamente a través de métodos definidos en la
interfaz del bean.
La interfaz de define la vista al cliente de un bean.
Un Session Bean puede ser invocado a través de RMI a través de una interfaz:
o Remota
o Local
En un nivel de descripción más profundo, podríamos decir que los EJBs de Sesión representan
procesos de negocio, es decir, funcionalidades de la aplicación que implementan un interfaz de
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
negocio (bussines interface). A través de ella, gestionan la interacción con los clientes y encapsulan
el flujo y manipulación de la información en el servidor.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
El uso de un bean de sesión como una
fachada (facade) para encapsular la
complejidad de las interacciones entre
Session Façade/ Session Entity Façade/ los objetos de negocio y participantes
Distributed Façade: en un flujo de trabajo. El Session
Façade maneja los objetos de negocio y
proporciona un servicio de acceso
uniforme a los clientes.
El beans de fachada tiene como cometido principal controlar las diferentes llamadas a
procedimientos que se producen en la capa de negocio, esto nos permitirá un mejor flujo de
información y ejecución de procedimientos.
Imaginemos un sistema de ventas que está diseñado más o menos de la siguiente manera,
tendremos mas de un JSP que realizaran las llamadas y peticiones a varios Servlets los que a su vez
llamaran a más de un beans de encargado de generar consultas, ingresos, etc. y a su vez estos
realizaran consultas a los distintos EJB ya sean de compra, ventas o de personas.
Gráficamente este modelado quedaría así:
En la imagen anterior identificamos a un cliente, varios archivos JSP y varios servlets que se
comunican con la capa de negocio, produciendo muchas llamadas entre las diferentes capas. Esto
situación contribuye a ralentizar el sistema o a generar mucho mas desorden, lo que conlleva a un
nivel de complejidad mayor en el desarrollo, porque complica la búsqueda y modificación del código,
localización de errores, etc.
El objetivo será, por tanto, pasar a este otro planteamiento:
Un solo beans encargado de la comunicación entre la capa web y negocio. Para ello crearemos un
beans que genere las respuestas a las solicitudes de cada servlet, y es aquí donde entra en juego el
beans de fachada, que nos permitirá disponer en una sola llamada todas las peticiones, y poder
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
representar a todos los demás beans necesarios. Además mediante HTML Dinámico, generamos
HTML que siempre cambiará en función de la llamada recibida y así poder tener una sola página JSP
en vez de varias.
En definitiva, este patrón denominado façade, literalmente, sirve a los clientes como una "fachada"
de los servicios proporcionados por otros componentes disponibles en el servidor.
En dicho contexto, los bean de sesión ofrecen operaciones síncronas (petición-respuesta) y procesos
de negocio ejecutados en respuesta a la solicitud del cliente. En ese momento, el contenedor de
EJBs crea e inicializa sus instancias, inyectándoles las referencias necesarias y las asigna a los
clientes a medida que estos los van requiriendo. Esta última operación es el pool de EJBs.
En un instante de tiempo dado, sólo un cliente tiene acceso a los métodos de la instancia del EJB,
por lo que hay un control de la concurrencia de llamadas. Y, además, también el contenedor de EJBs
garantiza que cada método del bean se ejecute dentro de una transacción atómica.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Un nivel de detalle más descriptivo del empleo de EJB’s de sesión en una arquitectura típica de Java
lo tenemos en la siguiente imagen:
Para incrementar el rendimiento, se podrían utilizar stateless session bean en alguna de las
siguientes situaciones:
- El estado del bean no tiene datos para un cliente en específico.
- En un único método de invocación, el bean realiza una tarea genérica para todos los clientes.
Por ejemplo, se podría utilizar un stateless session bean para mandar un email que confirme
una compra online.
- El bean implementa un servicio web.
Finalmente, otras características compartidas o no por los beans con y sin estado las vemos
indicadas en el siguiente cuadro. Si bien su correspondiente explicación será expuesta en las
próximas unidades.
Características Sin estado (stateless) Con estado (statefull)
Estado conversacional No Sí
Gestión de problemas Improbable Posible
Eventos de ciclo de vida PostConstruct PostCosntruct,
PreDestroy PreDestroy, Prepassive,
PostActive
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Temporizador Sí No
SessionSynchronization No Sí
para Transacciones
Servicios Web Sí No
PersistenceContext No Sí
Extendida
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Invocación local de un bean de sesión
Un cliente local puede invocar un session bean a través de una interfaz local. En este caso el cliente
reside en la misma instancia del bean de sesión.
El cliente local debe correr en la misma JVM que los enterprise bean que accede. Dicho cliente local
puede ser un componente web u otro enterprise bean.
Conviene subrayar que la interfaz local define el ciclo de vida de los métodos del vean y dicha
interfaz por defecto es local
Actividades
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
“Recuerde que para un seguimiento óptimo de la unidad es imprescindible realizar las actividades
que encontrará en la unidad correspondiente de la plataforma eLearning”.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Unidad 3. Implementación de los Beans de
Sesión de EJB 3.0
Objetivo
Conocer la programación de Enterprise JavaBeans en sus diferentes tipos de sesión.
La interacción del cliente con el bean se divide en un conjunto de pasos. En cada paso se añade
nueva información al estado del bean. Cada paso de interacción suele denominarse con nombres
como setNombre o setDireccion, siendo nombre y direccion dos variables de instancia del bean.
Algunos ejemplos de beans de sesión con estado podrían ser:
Un ejemplo típico es un carrito de la compra, en donde el cliente va guardando uno a uno los
ítems que va comprando.
Un enterprise bean que reserva un vuelo y alquila un coche en un sitio Web de una agencia
de viajes.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
El estado del bean persiste mientras que existe el bean. A diferencia de los beans de entidad, no
existe ningún recurso exterior al contenedor EJB en el que se almacene este estado.
Debido a que el bean guarda el estado conversacional con un cliente determinado, no le es posible al
contenedor crear un almacén de beans y compartirlos entre muchos clientes. Por ello, el manejo de
beans de sesión con estado es más pesado que el de beans de sesión sin estado.
En general, se debería usar un bean de sesión con estado si se cumplen las siguientes
circunstancias:
El estado del bean representa la interacción entre el bean y un cliente específico.
El bean necesita mantener información del cliente a lo largo de un conjunto de invocaciones
de métodos.
El bean hace de intermediario entre el cliente y otros componentes de la aplicación,
presentando una vista simplificada al cliente.
La sesión finaliza si el cliente remueve el bean o finaliza su sesión.
La Clase Bean
Una stateful session bean se define mediante la anotación @Stateful
Cada stateful session bean necesita implementar la interfaz serializable, para que de esta manera el
contenedor pueda serializar las instancias del bean y almacenarlas para preservar el estado cuando
las instancias no son usadas.
Ejemplo:
@Stateful
public class BeanComercial implements Comercial, Serializable {
public String simbolo = "";
public int cantidad = 0;
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
protected Vector<Producto> cesta= new Vector<Producto>();
public void insertarProducto(Producto producto) throws CestaException {
if (producto== null)
throw new CestaException("Cesta ist null");
producto setUnidadesPedidas(best.getUnidades());
cesta.add(producto);
}
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Ciclo de vida del bean con estado
El ciclo de vida responde a una serie de fases sucesivas. A su vez, Durante ese período de vida el
bean soporta callback o retrollamadas de los siguientes eventos de ciclo de vida: construcción,
destrucción, activación y pasivación.
- Creación del bean de sesión: Se produce cuando un cliente busca obtener una referencia a un
vean. El contenedor decide instanciar el vean, por lo que se llama a Class.newInstance().
Entonces el contenedor inyecta cualquier dependencia de contexto requerida. A su vez, el
contenedor llama a métodos de retrollamada opcionales etiquetados con @PostConstruct
- Uso de un bean de sesión: El contenedor puede llamar a métodos de negocio en nombre del
cliente. Conviene recordar que el cliente llama a un representante, mientras que el
contenedor llama a la instancia del bean. Hay dos estados asociados a este momento del
ciclo de vida: Activación y “pasivación”.
o “pasivación”, surge después de llamar a cualquier método opcional @PrePassivate o
bien cuando se alcanza un límite en el número de beans instanciados.
o Activación, después de llamar a cualquier método opcional @PostActivate o bien cuando
un cliente invoca un método de negocio
- Destrucción del bean de sesión: Se produce cuando un cliente llama a un método anotado
con @remove o cuando vence el temporizador. El contenedor decide eliminar el bean,
entonces llama a todos los métodos de retrollamada etiquetados con @PreDestroy. Hay que
señalar también no se llama cuando se cae el servidor es necesario que la aplicación guarde
proactivamente sus datos. En ese punto, la instancia del bean quedará lista para la
recolección de basura.
EJB 3.0 define varias anotaciones que permite describir cada método cuando ocurre uno de los
eventos ya mencionados: PostContruct, PreDestroy, PrePassivate, ProActive, Init.
@PrePassivate: El contenedor podría cambiar el estado de la sesión a pasivo y almacenar su
estado a un caché. Se llama a este método antes de que ocurra este suceso.
@PostActivate: se utiliza para restaurar el estado de un Session Bean en estado pasivo. El
método asociado a esta anotación es llamado cuando la sesión esté nuevamente activada.
@Init: designa métodos de inicialización para un Session Bean.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Los bean sin estado o Stateless Session Bean
Se trata de un bean que por lo general contará con una seria de métodos que realizaran un trabajo
determinado e independiente y que el resultado de las operaciones realizadas dentro de cada uno de
los métodos no dependerá de ningún estado relativo a la conversación que mantiene el cliente con el
bean. Todo Bean sin estado debe tener una interfaz. Si el desarrollador no la crea el contenedor se
encargara de crearla.
Los beans de sesión sin estado no se modifican con las llamadas de los clientes. Los métodos que
ponen a disposición de las aplicaciones clientes son llamadas que reciben datos y devuelven
resultados, pero que no modifican internamente el estado del bean. Esta propiedad permite que el
contenedor EJB pueda crear una reserva (pool) de instancias, todas ellas del mismo bean de sesión
sin estado y asignar cualquier instancia a cualquier cliente. Incluso un único bean puede estar
asignado a múltiples clientes, ya que la asignación sólo dura el tiempo de invocación del método
solicitado por el cliente.
Una de las ventajas del uso de beans de sesión, frente al uso de clases Java u objetos RMI es que
no es necesario escribir los métodos de los beans de sesión de una forma segura para threads
(thread-safe), ya que el contenedor EJB se va a encargar de que nunca haya más de un thread
accediendo al objeto. Para ello usa múltiples instancias del bean para responder a peticiones de los
clientes.
Cuando un cliente invoca un método de un bean de sesión sin estado, el contenedor EJB obtiene una
instancia de la reserva. Cualquier instancia servirá, ya que el bean no puede guardar ninguna
información referida al cliente. Tan pronto como el método termina su ejecución, la instancia del
bean está disponible para otros clientes. Esta propiedad hace que los beans de sesión sin estado
sean muy escalables para un gran número de clientes. El contenedor EJB no tiene que mover
sesiones de la memoria a un almacenamiento secundario para liberar recursos, simplemente puede
obtener recursos y memoria destruyendo las instancias.
Los beans de sesión sin estado se usan en general para encapsular procesos de negocio, más que
datos de negocio (tarea de los entity beans). Estos beans suelen recibir nombres como
ServicioBroker o GestorContratos para dejar claro que proporcionan un conjunto de procesos
relacionados con un dominio específico del negocio.
Es apropiado usar beans de sesión sin estado cuando una tarea no está ligada a un cliente
específico. Por ejemplo, se podría usar un bean sin estado para enviar un e-mail que confirme un
pedido on-line o calcular unas cuotas de un préstamo.
También puede usarse un bean de sesión sin estado como un puente de acceso a una base de datos
o a un bean de entidad. En una arquitectura cliente-servidor, el bean de sesión podría proporcionar
al interfaz de usuario del cliente los datos necesarios, así como modificar objetos de negocio (base
de datos o bean de entidad) a petición de la interfaz. Este uso de los beans de sesión sin estado es
muy frecuente y constituye el denominado patrón de diseño session facade que ya vimos
anteriormente.
Algunos ejemplos de bean de sesión sin estado podrían ser:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Un componente que comprueba si un símbolo de compañía está disponible en el mercado de
valores y devuelve la última cotización registrada.
Un componente que calcula la cuota del seguro de un cliente, basándose en los datos que se
le pasa del cliente.
Dado que no mantiene un estado conversacional con el cliente, cuando un cliente invoca los
métodos de un stateless bean, las variables de instancia del bean pueden contener un estado
específico del cliente, pero sólo por la duración de la invocación. Cuando el método finaliza, el
estado del cliente específico no debería mantenerse.
Ahora bien, las instancias pueden estar compartidas por los clientes. El contenedor tiene un pool de
instancias, cuando el cliente invoca un método se asigna una instancia, cuando la libere es
retornada al pool.
Ofrecen mejor escalabilidad para aplicaciones con gran cantidad de clientes e, incluso puede
implementar un web service, pero no otros tipos de Enterprise beans.
Un stateless session bean se define solo añadiendo la anotación @Stateless.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
e.printStackTrace ();
}}// ... ...
public void service (Request req, Response rep) {
// ... ... double res = tr.buy("SNPS",1000);
}
Dentro de este tipo de beans y a partir de la versión EJB 3.1 está el singleton session bean, una
variante de un stateless session bean. Su nombre proviene del hecho de que es instanciado una vez
por aplicación y existe durante todo el ciclo de vida de la aplicación. Como característica principal
destaca que un vean de este tipo está diseñado para circunstancias en las cuales una sola instancia
de enterprise bean necesita ser compartida y es concurrentemente accedida por los clientes. Los
beans de sesión singleton mantienen su estado entre las invocaciones de los clientes, pero no es
requerido mantener su estado durante las caídas del servidor.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Actividades
“Recuerde que para un seguimiento óptimo de la unidad es imprescindible realizar las actividades
que encontrará en la unidad correspondiente de la plataforma eLearning”.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Unidad 4. Implementación de Clases de
Entidad. Conceptos Básicos
Objetivo
Conocer el papel desempeñado por las clases de entidad en la tecnología EJB.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Todas estas configuraciones las vamos a realizar a través de anotaciones, y el API que se encarga
de gestionar todos los aspectos relativos a la persistencia es JPA ( Java Persistent API ). El siguiente
dibujo ilustra el lugar de las entidades y JPA dentro de EJB 3 frente a los otros tipos de beans
existentes en la especificación.
Vamos a ver un ejemplo genérico antes de entrar en más detalles. Crearemos una entidad llamada
Team, para lo cual veremos que lo único que vamos a tener en cuenta es establecer una anotación
@Entity al principio de la clase, una anotación @Id para indicar cuál es la propiedad que representa
la clave primaria, y por último una anotación @OneToMany para indicar que un equipo puede estar
compuesto por muchos jugadores y que éstos sólo pueden pertenecer a un equipo:
@Entity
public class Team {
@Id
public int getId() {
return id;
}
@OneToMany
public Collection getPlayers() {
return players;
}
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
public void setPlayers( Collection value ) {
players = value;
}
A continuación vamos a crear la Entidad Player, que al igual que la entidad anterior va a tener una
anotación @Entity, otra anotación @Id, y por último una anotación @ManyToOne para establecer
una relación bidireccional entre las dos entidades:
public class Player {
@Id
public int getId() {
return id;
}
@ManyToOne
public Team getTeam() {
return team;
}
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Los componentes del Java Persistence API se encuentran en el paquete de javax.persistence
JPA Está basado en anotaciones los cuales cuentan con la siguiente estructura:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Si no se describe la información de la columna utilizando la anotación @Column se mapeará
utilizando el tipo equivalente en la base de datos y buscará un campo con el mismo nombre
en la base de datos. Por ejemplo como ocurre en la siguiente ilustración:
La diferencia entre usar int e Integer en una propiedad de una clase radica en que los int no
soportan nulos, mientras que los objetos Integer, si.
Veamos un ejemplo sencillo donde se aplican muchas de las características que acaban de
enumerarse:
@Entity
@Table(name = "articulo")
@NamedQueries(
{
@NamedQuery(name = "Articulo.buscarTodos",
query = "SELECT a FROM Articulo a "),
@NamedQuery(name = "Articulo.buscarPorPK",
query = "SELECT a FROM Articulo a " +
"WHERE a.idArticulo = :idArticulo")
})
public class Articulo implements Serializable {
@Id
@Column(name = "id_articulo", nullable = false)
// NO OLVIDAR EL GENERADOR
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer idArticulo;
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
@Column(name = "precio")
private BigDecimal precio;
//Constructores
//Métodos Sets y Gets (Son necesarios)
}
Para que un Java Bean pueda ser guardado en una base de datos debe ser anotado como @Entity.
Una entidad debe cumplir una serie de restricciones como, por ejemplo, tener un constructor vacío,
ya sea público o protegido, poseer un campo que sea clave primaria, @Id. Veamos un ejemplo:
@Entity
@Table(name="USUARIOS")
public class Usuario {
@Id
private String nick;
...
public Usuario() { }
// Getters y Setters
}
La mayoría de motores de persistencia para JPA permiten omitir la declaración del constructor vacío.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
La anotación @Table nos permite especificar el nombre de la tabla donde se persistirá la entidad,
permite seguir la especificación de clases en singular y tablas en plural, si no está incluida la entidad
se persistirá en una tabla que coincida con el nombre de la clase. La anotación @Id marca el
identificador de la clase, es decir, su clave primaria.
Además, JPA define que los tipos primitivos, clases "envoltorio" de tipos primitivos,
java.lang.String, byte[], Byte[], char[], Character[], java.math.BigDecimal,
java.math.BigInteger, java.util.Date, java.util.Calendar, java.sql.Date, java.sql.Timestamp
son mapeados de forma automática en columnas, es decir, no es necesario marcarlos. Además
todos aquellos objetos que sean de tipo Serializable y estén marcados con la anotación @Basic.
Si lo que queremos es que una variable, de las que son mapeadas de forma automática, no sea
tomada como un campo deberemos usar la anotación @Transient.
JPA incluye formas de mapear cada campo indicando nombres de columna y otros valores.
@Entity
@Table(name="USUARIOS")
public class Usuario {
@Id
private String nick;
@Column(name="PASSWORD", nullable=false)
private String pass;
@Column(name="E-MAIL", nullable=false)
private String mail;
@Lob
@Basic(fetch=FetchType.LAZY)
private byte[] imagen;
...
public Usuario() { }
// Getters y Setters
}
La anotación @Column permite definir varias propiedades. La propiedad name especifica el nombre
de la columna donde va a ser persistido el campo, si esta propiedad no está presente el framework
de persistencia usará el nombre de la variable como nombre de la columna. La propiedad nullable
indica si la columna acepta valores null o no, si no se incluye el valor por defecto es true. Además
esta anotación soporta otras muchas propiedades como pueden ser columnDefinition, length,
precision, scale, insertable, updatable y table.
La anotación @Basic permite, además de lo dicho anteriormente, definir las propiedades optional y
fetch. La propiedad optional funciona igual que la propiedad nullable de la anotación @Column. La
propiedad fetch permite definir cuando se debe cargar el campo en cuestión, el valor
FetchType.Lazy indica que el campo se va a cargar de forma "perezosa". El valor FetchType.EAGER
indica que el valor será cargado cuando se cargue el resto del objeto. El valor por defecto de la
propiedad fetch es FetchType.EAGER.
La anotación @Lob indica que el contenido de un campo básico será guardado como LOB (Large
OBject). Si por ejemplo utilizamos esta anotación para marcar un campo que contenta un String o
caracteres el framework lo mapeará a una columna CLOB (Character Large OBject). Si es de otro
tipo será mapeado a una columna de tipo BLOB (Binary Large OBject).
Son muchas las ventajas de usar beans de entidad en lugar de acceder a la base de datos
directamente. El uso de beans de entidad nos da una perspectiva orientada a objetos de los datos y
proporciona a los programadores un mecanismo más simple para acceder y modificar los datos. Es
mucho más fácil, por ejemplo, cambiar el nombre de un estudiante llamando a student.setName()
que ejecutando un comando SQL contra la base de datos. Además, el uso de objetos favorece la
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
reutilización del software. Una vez que un bean de entidad se ha definido, su definición puede
usarse a lo largo de todo el sistema de forma consistente. Un bean Estudiante proporciona un forma
completa de acceder a la información del estudiante y eso asegura que el acceso a la información es
consistente y simple.
La representación de los datos como beans de entidad puede hacer que el desarrollo sea más
sencillo y menos costoso.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Requisitos para clases de entidad
Una clase de entidad debe seguir los siguientes requisitos:
La clase debe ser anotada con javax.persistence.Entity.
La clase debe tener una visibilidad pública o protegida y ningún argumento en su constructor.
La clase no debe ser declarada como final. Tampoco ninguno de sus métodos o variables
persistentes instancia debe ser declarada como final.
Si una instancia de la entidad ha de ser pasada por valor como un objeto individual, a través
de la interfaz de negocio de un bean de sesión, entonces la clase debe implementar la
interfaz Serializable.
Las entidades pueden extender tanto en otras clase de entidad como de no-entidad y a su
vez, las clases que no sean entidad extenderse a clases de entidad.
La variables de instancia persistente admiten ser declaradas como privadas, protegidas o
privadas dentro del paquete, y sólo se puede acceder a ellas directamente mediante los
métodos de la clase de entidad. Los clientes deben acceder al estado de la entidad a través
de métodos de acceso o de negocios.
Anotaciones:
@NotNull: Campo no puede ser nulo. En caso de serlo, se lanzará un error de validación.
@Pattern: Si es que el valor no cumple con la expresión regular, se lanzará un error de validación.
@Past: Asegura que el valor sea una fecha del pasado. Se lanzará un error de validación en caso
contrario.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
o java.sql.TimeStamp
o Tipos serializables definidos por el usuario
o byte[]
o Byte[]
o char[]
o Character[]
Tipos enumerados
Otras entidades y/o colecciones de entidades.
Clases anidadas.
Las entidades pueden hacer persistentes tanto los campos como las propiedades persistentes. Por
ejemplo, si las anotaciones de mapeo son aplicadas a las variables de instancia de la entidad, la
entidad hace los campos persistentes. Ahora bien, si la asignación de anotaciones se aplica a los
métodos getter de la entidad para las propiedades del Bean, entonces la entidad hace las
propiedades persistentes. No se puede aplicar anotaciones de mapeo a los campos y las propiedades
en una sola entidad.
a) Campos persistentes: Si la clase de entidad utiliza campos persistentes, la ejecución de
dicha persistencia accede directamente a la instancia de la variable perteneciente a una clase
de tipo entidad. Todos aquellos campos no anotados con javax.persistence.Transient o no
marcados como transitorios se considerarán persistentes dentro del almacén de datos. Así
pues, la anotación del mapeo relacional y el objeto deberá aplicarse a las variables de
instancia.
b) Propiedades persistentes: Si la entidad utiliza las propiedades persistentes, entonces
dicha entidad debe seguir las convenciones del método típicas de los componentes de
JavaBeans. Es decir, se utilizarán los habituales métodos getter y setter. Así, para todas las
propiedades persistentes property del tipo Type de la entidad, hay un método getter
getProperty y un método setter setProperty. Si la propiedad es un valor lógico, entonces
puede utilizar isProperty en lugar de getProperty. Por ejemplo, si una entidad cliente utiliza
las propiedades persistentes y tiene una variable de instancia privada llamada Nombre
llamada, la clase define un método getNombre y setNombre para recuperar y asignar el
estado de la variable de instancia Nombre.
La firma del método para un solo valor las propiedades persistentes son los siguientes:
Type getProperty()
void setProperty(Type type)
Colección con valores de campos persistentes y las propiedades deben utilizar la lista de interfaces
reconocidas en Java con independencia de que la entidad utilice campos persistentes o propiedades.
Las interfaces de colección que disponibles para su uso son:
java.util.Collection
java.util.Set
java.util.List
java.util.Map
Si la clase de entidad utiliza campos persistentes, el tipo en las firmas método anterior debe ser uno
de estos tipos de colección. No obstante, algunas variantes genéricas de estos tipos de colección
también se pueden utilizar. Por ejemplo, si la entidad Cliente tiene una propiedad persistente que
contiene un conjunto de números de teléfono, sería posible definir los siguientes métodos:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Set <PhoneNumber> getPhoneNumbers () {}
void setPhoneNumbers (Set <PhoneNumber>) {}
El objeto o su mapeo relacionar será aplicado a los métodos getter. Ahora bien dichas anotación de
mapeo no se pueden aplicar a los campos o propiedades que a su vez lleven la notación @Transient
o estén marcadas como transient.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Si volvemos a ejecutar nuestro programa de prueba:
A a1 = new A(1);
A a2 = new A(1);
System.out.println(a1 == a2); // Muestra false.
System.out.println(a1.equals(a2)); // Ahora muestra true.
Dentro de JPA, sin embargo, las comparaciones de identidad no se realizan utilizando el método
equals de cada clase, sino el método equals de la clave primaria. Es decir, cuando JPA debe
determinar si dos objetos representan la misma entidad, no compara los objetos sino sus
identificadores. Sin embargo, siguiendo los conceptos de diseño orientado a objetos, es conveniente
que implementemos el método equals para nuestras entidades.
También es conveniente sobrecargar el método hashCode. El valor devuelto por este método es
utilizado por algunas estructuras (por ejemplo, las colecciones java.util.HashSet y
java.util.HashMap).
Un escenario común en el cual es necesario implementar ambos métodos, es al utilizar una entidad
persistente con las clases de la API de colecciones. Estas clases hacen uso de los métodos equals y
hashCode para organizar los objetos en sus estructuras internas. Si los métodos no están
implementados correctamente, se podría obtener un comportamiento inesperado de estas clases.
Unidades de persistencia
Una unidad de persistencia define el conjunto de entities y su configuración de mapeo que se asocia
a un mismo origen de datos. En cada aplicación puede configurarse más de una unidad de
persistencia.
Las unidades de persistencia se definen en un archivo con cumbre persistence.xml. Se debe definir
al menos una unidad de persistencia. En aplicaciones JEE, se coloca en el directorio META-INF de un
archivo .jar, ejb-jar, .ear o .war; en aplicaciones JSE debe estar disponible en el classpath, dentro
del directorio META-INF. En este xml se declara para, cada unidad de persistencia, su nombre (el
cual utilizaremos dentro de la aplicación), una descripción, el proveedor de persistencia asociado, las
propiedades de configuración del mismo, el tipo de transacción (JTA o Local), y cualquier otra
información requerida por el proveedor de persistencia.
A continuación vemos un ejemplo de este archivo:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
xmlns=http://java.sun.com/xml/ns/persistence
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="ar.com.fit.planner">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
<property name="hibernate.connection.username" value="fit-planner" />
<property name="hibernate.connection.password" value="fit-planner" />
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/fit-
planner" ></property>
<!-- Descomentar la siguiente linea para que Hibernate genere el esquema de base de
datos -->
<!-- <property name="hibernate.hbm2ddl.auto" value="create-drop" /> -->
</properties>
</persistence-unit>
</persistence>
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Contextos de persistencia
Para cada unidad de persistencia, el motor de JPA establece un contexto de persistencia, que define
el contexto en el cual "viven" y se administran las instancias de las clases persistentes definidas en
la unidad de persistencia. La definición en la especificación de JPA dice: “es un conjunto de
instancias de entidades en donde para cada identidad persistente existe una única instancia de
entity asociada”.
Todo el ciclo de vida de los entities se produce dentro de un contexto de persistencia dado.
New: el entity existe en memoria pero no tiene asociada una identidad persistente en la BD
o un contexto de persistencia.
Managed: el entity tiene asociado una identidad y un contexto de persistencia y el
EntityManager mantiene sincronizado su estado al momento de commit de la transacción o
bien al llamar al método flush().
Detached: el entity no es administrado por el EntityManager, no está asociado a ningún
contexto de persistencia. Una entidad administrada (managed) se desvincula al finalizar la
vida del contexto de persistencia (scope, sesión) o cuando se serializa (por ej. al ser enviado
entre capas)
Removed: el entity puede estar asociado al contexto de persistencia pero está marcado para
eliminación al momento de finalizar la transacción.
Persisted: el entity bean existe en la base de datos, pero no ha sido cargado en memoria.
Entity Manager
El Entity Manager es la fachada de servicios de JPA. Cada Entity Manager permite acceder a un
contexto de persistencia, y cada contexto de persistencia se refiere a una unidad de persistencia.
Los servicios del Entity Manager operan sobre entidades. Todo cliente que quiera interactuar con
entidades persistentes debe primero obtener una instancia del Entity Manager. Provee operaciones
de búsqueda, actualización, borrado y persistencia de nuevas entidades.
Se incorpora dentro del ciclo de vida de la entidad tal y como se refleja en la siguiente ilustración:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
New: Aquí el objeto Java no persiste
Managed (persist()): Se asocia con un contexto de persistencia. La clave primaria hace que se
cree una fila en la tabla asociada en la base de datos
Detached: El objeto conserva sus datos pero no se sincroniza más con la base de datos. Un objeto
detached puede volverse a gestionar en el contexto de persistencia con merge()
Removed (remove() or by cascading): El objeto conserva sus datos pero se instruye a la base
datos que borre la fila asociada. Con flush() se fuerza a escribir datos en base de datos.
Veamos algunos fragmento de código de ejemplo para entender cómo se implementan los diferentes
estados desde el Entity Manager:
En este primer fragmento hacemos persistente el objeto:
public Order createNewOrder(Customer customer) {
Order order = new Order(customer);
entityManager.persist(order);
return order;
}
En este segundo fragmento de código realizamos una búsqueda para localizar el objeto y una vez
hallado lo borramos:
public void removeOrder(Long orderId) {
Order order = entityManager.find(Order.class, orderId);
entityManager.remove(order);
}
En el tercer fragmento realizamos una búsqueda mediante una sentencia de SQL a través del Entity
Manager:
public List findWithName(String name) {
return em.createQuery( "SELECT c
FROM Customer c
WHERE c.name LIKE :custName") .setParameter("custName", name)
.setMaxResults(10) .getResultList();
}
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Obteniendo un Entity Manager en aplicaciones JSE.
Intentaremos realizar algunas operaciones con las clases persistentes que hemos definido hasta el
momento. Dado que aún no hemos armado la aplicación JEE completa, haremos una pequeña clase
de prueba, en la que accederemos al Entity Manager utilizando la interfaz
javax.persistence.EntityManagerFactory.
En primer lugar, debemos obtener una instancia de la interfaz EntityManagerFactory, para lo cual se
utiliza la clase bootstrap javax.persistence.Persistence. Esta clase se encarga de examinar el archivo
persistence.xml, y entregar una instancia de EntityManagerFactory asociada a una unidad de
persistencia determinada. Veamos un ejemplo:
EntityManagerFactory emf = javax.persistence.Persistence.createEntityManagerFactory("ar.com.fit.p
lanner");
El método javax.persistence.Persistence.createEntityManagerFactory(String) recibe como parámetro
el nombre de una unidad de persistencia definida en el archivo persistence.xml. Una vez que
tenemos la instancia de EntityManagerFactory, podemos pedirle el EntityManager:
EntityManager em = emf.createEntityManager();
Hay dos tipos de Entity Manager:
a) Container-Managed Entity Managers: el ciclo de vida del Entity Manager es manejado por
el contenedor y el contexto es propagado automáticamente a todos los componentes de la
aplicación que usan la instancia en una transacción. Ejemplo:
@PersistentContext
EntityManager em;
b) Application-Managed Entity Managers: el ciclo de vida del Entity Manager es manejado
por la aplicación y el contexto no es propagado a los otros componentes. Ejemplo:
@PersistentUnit
EntityManagerFactory emf;
EntityManager em = emf.createEntityManager();
Ya podemos comenzar a operar con nuestros Entity Beans.
Persistiendo objetos
En este ejemplo vemos cómo crear un objeto utilizando una clase persistente, y cómo persistirlo con
el EntityManager:
em.getTransaction().begin();
Cliente cliente1 = new ClienteUnipersonal("Mario", "López");
em.persist(cliente1);
em.getTransaction().commit();
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Operaciones básicas
Sobre los objetos resulta posible realizar algunas operaciones básicas. Pasamos a enumerar y
explicar las principales.
Persist
La operación EntityManager.persist(Object) recibe como parámetro un objeto de una clase anotada
con @Entity y lo persiste en la base de datos.
Ejemplo:
Person p = new Person(“Pepe”, “Perez”);
em.persist(p);
O bien:
LineItem li = new LineItem(...);
order.getLineItems().add(li);
em.persist(li);
return li;
Persiste, también, todos los objetos relacionados, cuya relación haya sido anotada con
CascadeType.PERSIST o CascadeType.ALL. Por lo tanto, con una única operación JPA puede persistir
todo un árbol de objetos, según las definiciones de las relaciones. En ese caso, el método persist es
propagado a todas las entidades relacionadas a la llamada de la entidad que tienen el elemento
cascade en ALL o PERSIST en la anotación de la relación:
Order.java
@OneToMany(cascade=ALL, mappedBy=“order")
public Collection<LineItem> getLineItems(){return
lineItems;}
Las operaciones de persistencia se traducen, en algún momento, en sentencias INSERT en la base
de datos. El proveedor de persistencia elige cuándo ejecutar estas sentencias. Hibernate, por
ejemplo, las ejecuta al realizar el commit de la transacción. En una aplicación JSE, esto ocurre
cuando se llama explícitamente al método entityManager.getTransaction().commit().
Después de realizar esta operación sobre un Entity Bean, el objeto queda en estado Managed.
Remove
La operación EntityManager.remove(Object) recibe un objeto de una clase anotada con @Entity, y lo
borra de la base de datos. Después de realizar esta operación, el objeto queda en estado Removed.
Al terminar la transacción, se ejecuta una sentencia DELETE.
Ejemplo:
em.remove(p.getAddress());
o bien combinando una búsqueda con la eliminación posterior del objeto encontrado:
Order order = em.find(...);
em.remove(order);
Refresh
La operación EntityManager.refresh(Object) recibe un objeto de una clase anotada con @Entity y
sincroniza su estado con el de la base de datos. Es decir, actualiza el objeto obteniendo los valores
de sus atributos y relaciones de la base de datos. Se ejecuta una sentencia SELECT. Después de
ejecutar esta operación, el objeto queda en estado Managed.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Merge
La operación EntityManager.merge(Object) sincroniza el estado de un objeto en la base de datos. Al
terminar la transacción, se ejecuta una sentencia UPDATE. El objeto no debe estar en estado
Removed. Después de ejecutar esta operación, el objeto queda en estado Managed.
Búsquedas simples
La forma más sencilla de buscar un Entity Bean es utilizar el método find(Class, Object) de la
interfaz EntityManager. Este método permite buscar un Entity por su clave primaria.
Para buscar una cliente por ejemplo, podríamos usar el siguiente código:
Cliente cliente = entityManager.find(Cliente.class, new Long(1));
El primer parámetro del método find es la clase del Entity que deseamos buscar. El segundo
parámetro, la clave primaria del Entity. La clase que pasemos como parámetro debe ser una
anotada con @Entity; no podemos usar, por ejemplo, una clase anotada con @MappedSuperclass.
En el ejemplo vemos que estamos accediendo a un cliente de manera polifórmica; es decir, no
conocemos a priori si el cliente es corporativo o unipersonal. Dado que la clase Cliente extiende a
Persona, también podríamos haber hecho:
Persona = entityManager.find(Persona.class, new Long(1));
Esta búsqueda debería devolver el mismo Entity, dado que toda la jerarquía de clases que extiende
a Persona comparte la misma clave primaria. Pero dado que estamos haciendo una búsqueda
polimórfica, no podemos saber, a priori, si la consulta devolverá un Cliente o un Empleado.
Enunciado
Vamos a realizar una aplicación que buscará un empleado y devolverá sus datos utilizando para la
lógica un Bean de session.
Para ello, utilizaremos la tecnología EJB y un proyecto empresarial.
Lo primero que vamos a realizar será crearnos un nuevo proyecto Enterprise Application.
Debemos hacer que el bean sea remoto debido a que estará ubicado en otro “proyecto”, que es el
proyecto EJB de la aplicación.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Lo llamaremos ProyectoEJBEmpleados
Crearemos el proyecto marcando la opción para generar los proyectos de tipo WAR y EJB y
utilizaremos el servidor GlassFish Server versión 3.
Lo primero que vamos a realizar será traernos las librerías para trabajar con datos. Para ello, sobre
el proyecto EJB, agregaremos la librería de Oracle.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
A continuación, sobre el proyecto EJB, crearemos un nuevo Session Bean.
Una vez creado el Bean, vamos a implementar un método de acción que llamaremos
posteriormente.
Para ello, debemos ir al proyecto EJB, sobre la carpeta Enterprise Bean, seleccionamos la opción Add
Bussines Method para crearnos un método de negocio que llamaremos posteriormente.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Escribimos el nombre del método, en nuestro caso buscarEmpleado, e indicamos que el tipo que
devolverá es de la clase String.
También le indicaremos que el método recibirá un dato de tipo int al que llamaremos idempleado.
@Override
public String buscarEmpleado(int idempleado) {
try
{
DriverManager.registerDriver(new oracle.jdbc.OracleDriver());
Connection cn = DriverManager.getConnection("jdbc:oracle:_
thin:@localhost:1521:XE", "SYSTEM", "12345");
Statement sentencia = cn.createStatement();
ResultSet rs = sentencia.executeQuery("SELECT ENAME, JOB, SAL FROM EMP");
String dato = "";
if (rs.next())
{
dato = "<table border='1'>";
dato += "<tr>";
dato += "<td>"+rs.getString(1)+"</td>";
dato += "<td>"+rs.getString(2)+"</td>";
dato += "<td>"+rs.getString(3)+"</td>";
dato += "</tr>";
dato += "</table>";
}else
{
dato = "<h1>No existe el empleado buscado</h1>";
}
return dato;
}catch (Exception ex)
{
return ex.toString();
}
}
}
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Ahora vamos a agregar un Servlet que implementará la funcionalidad del componente EJB que nos
hemos creado.
Una vez que nos ha creado el servlet, debemos implementar la llamada, para ello utilizaremos el
asistente de código que nos permite insertar código generado dinámicamente.
Sobre el código del servlet, seleccionamos la opción Insert Code
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Nos aparecerán una serie de opciones, nosotros seleccionaremos la opción que llama a un Bean.
Marcaremos la opción Call Enterprise Bean.
Al seleccionar dicha opción, nos muestra una ventana dónde debemos indicar el Bean que vamos a
llamar desde el servlet. Buscamos el bean en el proyecto EJB y pulsamos sobre OK.
Podremos comprobar en el código que nos genera las librerías necesarias para realizar la llamada al
Bean de Session.
Ahora vamos a implementar el servlet para poder escribir el saludo escrito y enviado desde el EJB:
package paqueteservlets;
import java.io.IOException;
import java.io.PrintWriter;
import javax.ejb.EJB;
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import paquetebean.BeanBuscadorEmpleadosRemote;
@WebServlet(name="ServletEmpleado", urlPatterns={"/ServletEmpleado"})
public class ServletEmpleado extends HttpServlet {
@EJB
private BeanBuscadorEmpleadosRemote beanBuscadorEmpleados;
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
</head>
<body>
<form name="form1" action="ServletEmpleado">
Escriba el ID del empleado a buscar
<input type="text" name="txtnumero"><br>
<input type="submit" value="Buscar Empleado">
</form>
</body>
</html>
Debemos recordar que las aplicaciones empresariales son un conjunto de elementos y contenedores
de los objetos, por lo que debemos ejecutarla en su conjunto con la tecla F6.
Cuando ejecutemos nuestra aplicación, veremos la página inicial index.jsp con el diseño que
hayamos realizado.
Una vez que pulsemos sobre el botón, comprobaremos que la respuesta desde el EJB llega
correctamente.
Actividades
“Recuerde que para un seguimiento óptimo de la unidad es imprescindible realizar las actividades
que encontrará en la unidad correspondiente de la plataforma eLearning”.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Unidad 5. Implementación de Clases de
Entidad: Modelado de Relaciones de
Asociación de Datos
Objetivo
Conocer el papel desempeñado por las relaciones y su modelado dentro las clases de entidad en la
tecnología EJB.
Por su parte, las claves principales compuestas deben corresponderse con una sola propiedad o un
solo campo persistente o un único conjunto de propiedades persistentes o campos. Si la clase es
mapeada a múltiples campos o propiedades, los nombres y tipos de los campos correspondientes a
la clave principal deben coincidir con los de la clase entidad. Además, estas claves compuestas han
de definirse en un clase con clave principal e indicarse mediante la notación
javax.persistence.EmbeddedId y javax.persistence.IdClass.
La clave principal, bien como propiedad o bien como campo de una clave primaria compuesta, debe
ser uno de los siguientes tipos del lenguaje Java:
tipos primitivos de Java
java.lang.String
java.util.Date (el tipo temporal debería ser FECHA)
java.sql.Date
Por ejemplo, veamos el siguiente diagrama y podremos entender en la práctica los diferentes
algunos de los diferentes tipos de relaciones enumerados.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Uno a uno: Cada instancia de la entidad se relaciona con una sola instancia de otra entidad. Por
ejemplo, para el modelo de un almacén físico en el que cada elemento depositado responde a un
control también único. Uno-a-uno utiliza la anotación javax.persistence.OneToOne anotación en la
propiedad persistente correspondiente o en el campo.
Uno a varios: Una instancia de la entidad puede estar relacionada con varias instancias de las otras
entidades. Un pedido de cliente, por ejemplo, puede estar constituido por varios elementos. Las
relaciones uno a muchos usan la anotación javax.persistence.OneToMany en cada propiedad o
campo correspondiente que se quiera hacer persistente.
Muchos-a-uno: Varias instancias de una entidad pueden estar relacionadas con una única instancia
de la otra entidad. Esta relación es la contraria de una relación uno-a-muchos. En el ejemplo de
código que hemos puesto antes, desde la perspectiva de LineItem la relación con la Order es de
muchos a uno.
Muchos-a-uno utiliza como anotación javax.persistence.ManyToOne en cada propiedad o campo
correspondiente que quiera hacerse persistir.
Muchos-a-muchos: Los casos de cada entidad pueden estar relacionados con varias instancias de
la otra. Por ejemplo, en la universidad cada curso tiene muchos alumnos, y cada estudiante puede
tomar varios cursos. Por lo tanto, en una solicitud de inscripción, los cursos y los estudiantes
tendrían una relación muchos-a-muchos. Para esta circunstancia se emplea la anotación
javax.persistence.ManyToMany sobre la propiedad o el campo correspondiente que queramos hacer
persistente.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
1) Con la anotación @ClassId
@Entity
@IdClass(EntidadPK.class)
public class Entidad {
@Id
private int id;
@Id
private String nombre;
...
public Entidad() { }
// Getters y Setters
}
public class EntidadPK {
int id;
String nombre;
@Embeddable
public class EntidadPK {
int id;
String nombre;
public EntidadPK() { }
Tipos de relaciones
Existen cuatro tipos de relaciones que podemos establecer con JPA:
1:1
1:N
N:1
N:N
Para cada tipo de relación, existen además dos variantes: unidireccional y bidireccional. En total,
esto nos da ocho tipos de relaciones. Desde el punto de vista de la implementación, sin embargo,
los casos unidireccionales son muy similares a los bidireccionales. Exploraremos algunos ejemplos
de cada uno, pero nos focalizaremos en los casos relaciones bidireccionales. Analizaremos, además,
las consecuencias que estas implementaciones tienen en el modelo relacional resultante.
Relaciones 1:1
Cada instancia de una entidad está relacionada a una sola instancia de otra entidad. Se indica con la
anotación @OneToOne
@Entity
public class Pasajero {
...
@OneToOne
@JoinColumn(name="id_pasajero")
private Boleto boleto;
...
}
@Entity
public class Boleto {
...
@OneToOne(mappedBy="boleto")
private Pasajero pasajero;
...
}
En nuestro modelo de dominio, cada empleado tiene asociado un currículum:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Para implementar esta relación, debemos agregar en la clase Empleado un atributo que la
represente, utilizando la anotación @OneToOne:
@OneToOne(cascade = CascadeType.ALL, optional = false)
@JoinColumn(name = "curriculum")
private Curriculum curriculum;
La anotación @OneToOne, entonces, se utiliza para describir relaciones 1:1. Si pensamos en el
modelo de datos necesario para persistir la relación, tenemos dos opciones:
1. Incluir en la tabla de empleados una clave foránea a la tabla de currículum.
2. Incluir en la tabla de currículum una clave foránea a la tabla de empleados.
En este caso, dado que la relación es unidireccional con sentido Empleado -> Curriculum, lo natural
es que la tabla empleados tenga la clave foránea a la table curriculum. Para indicar la información
de esta clave foránea en la clase Empleado, utilizamos la anotación @JoinColumn. Según el código
que hemos visto, entonces, la columna con la clave foránea se llama curriculum. La especificación
de EJB 3.0 denomina a la clase que contiene la definición de la clave foránea en cualquier tipo de
relación como clase dueña de la relación.
El modelo de datos queda, entonces:
Dado que la relación es unidireccional, en la clase Curriculum no debemos incluir ninguna anotación
en particular. Pero si quisiéramos que fuera bidireccional, tendríamos que agregarle un atributo para
representarla, de la siguiente manera:
@OneToOne(mappedBy="curriculum")
private Empleado empleado;
Vemos que en este extremo de la relación no estamos definiendo la clave foránea. Como habíamos
dicho, la clave foránea sólo es necesaria en una tabla; en este caso, elegimos colocarla en la table
empleados. Dentro de la clase Curriculum, en cambio, utilizamos el atributo mappedBy para indicar
el nombre de la propiedad en la clase Empleado que define la relación.
En general, entonces, en una relación bidireccional existen dos extremos; uno dueño de la relación,
y otro subordinado. El extremo dueño utiliza la anotación @JoinColumn para definir la clave foránea.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
El extremo subordinado utiliza el atributo mappedBy de la anotación correspondiente para indicar el
nombre de la propiedad correspondiente en el extremo dueño.
¿Pero por qué necesitamos el atributo mappedBy? En nuestro caso, la relación entre ambas clases
se da en un único atributo para ambos extremos, por lo que el proveedor de persistencia podría
utilizar una convención y deducir el atributo correspondiente. La especificación no indica esta
convención, sin embargo, y por lo tanto se utiliza el atributo mappedBy para vincular los atributos
correspondientes a los dos extremos de la misma relación.
Analizaremos el atributo cascade de la anotación OneToMany más adelante.
Relaciones 1: N
Una entidad puede relacionarse a múltiples instancias de otra clase. Un ejemplo común es una
factura que tiene muchos detalles. Se indica con la anotación @OneToMany y comúnmente está
asociada a un Set de elementos.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Ejemplo de asociación Bidireccional Definición del one to many
@Entity
public class Autobus {
...
@OneToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE}, mappedBy="autobus")
private Set<Boleto> boletos;
...
}
Definición del lado inverso de la relación
@Entity
public class Boleto {
...
@ManyToOne
@JoinColumn(name="id_autobus", nullable=false)
public Autobus getAutobus() {
return autobus;
}
...
}
Attributos de un @OneToMany
cascade
Opcional
Default: Un array vacío que contiene elementos del tipo CascadeType. Por default JPA no asigna
ninguna operación de cascada en una asociación, sin embargo se pueden utilizar los siguientes tipos
de operaciones en Cascada:
ALL - Se realizan los cambios en las entidades señaladas por la operación en cualquier operación de
persistencia realizada.
MERGE - Si se realiza una operación de actualización en la entidad se realizarán los cambios en las
entidades señaladas por el @OneToMany.
PERSIST -Si se realiza una operación de escritura en la entidad se agregarán las entidades
señaladas por el @OneToMany..
REFRESH - Si se realiza una actualización en la entidad se realizarán los cambios en las entidades
señaladas por el @OneToMany..
REMOVE - Si se realiza una operación que implique eliminar una entidad entidad se eliminarán las
entidades señaladas por el @OneToMany..
mappedBy
Opcional: Depende si es unidireccional o bidireccional
Default: Si la relación es unidireccional el proveedor de persistencia determinará el campo que
contiene la unión. Si la relación es bidireccional, es necesario definir el mappedBy en la forma
inversa de la relación. Es decir se utilizará por ejemplo el @ManyToOne que defina el lado opuesto
de la relación.
A su vez, la clase Curriculum contiene una lista de ItemCurriculum. Cada item representa una
entrada del curriculum:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
En la representación del modelo relacional para esta relación, la tabla que almacena los items tendrá
la clave foránea a la tabla de los currículum. Aún así, la entidad que define la relación debe ser
Curriculum, no ItemCurriculum, dado que la relación es unidireccional con sentido Curriculum
ItemCurriculum. Entonces, debemos usar la anotación @JoinColumn en la clase Curriculum.
El modelo de datos para esta relación queda definido, entonces, de la siguiente manera:
@ManyToOne(optional = false)
@JoinColumn(name = "curriculum")
private Curriculum curriculum;
Relaciones N:1
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Muchas instancias de una clase se relacionan a una sola instancia de otra clase (es la versión inversa
de One to Many). Se indica con la anotación @ManyToOne
@Entity
public class Boleto {
...
@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name="id_autobus", referencedColumnName="id_autobus")
private Autobus autobus;
...
}
Para las relaciones N:1, el modelo relacional es similar al de las relaciones 1:N. La tabla que
representa el extreno N de la relación contiene una clave foránea a la tabla que representa el
extreno 1. En este caso, sin embargo, el extremo dueño de la relación es el extreno N. Por lo tanto,
la anotación @JoinColumn debe usarse en la clase que represente el extremo N.
La anotación para indicar este tipo de relación es @ManyToOne.
En nuestro modelo, el ejemplo de este tipo de relaciones es la existente entre Empleado y
CategoriaEmpleado, definida en el siguiente model de clases:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Si analizamos el ejemplo de una relación 1:N bidireccional, vemos que la implementación es igual a
la de un relación N:1 bidireccional.
Relaciones N:N
Las instancias de una entidad se pueden relacionar con múltiples entidades de otra clase y
viceversa. Un ejemplo sería un estudiante que tiene muchas clases y una clase que tiene muchos
estudiantes a su vez. Se indica con la anotación @ManyToMany.
@Entity
public class Autobus {
...
@ManyToMany(fetch=FetchType.EAGER)
@JoinTable(
name="autobus_gasto", schema="public"
, joinColumns={
@JoinColumn(name="id_autobus", referencedColumnName="id_autobus"),
}
, inverseJoinColumns={
@JoinColumn(name="id_concepto_gasto",
referencedColumnName="id_concepto_gasto"),
}
)
private Set<ConceptoGasto> conceptoGastos;
@Entity
public class ConceptoGasto {
...
@ManyToMany(mappedBy="conceptoGastos")
private <Set>Autobus autobuses;
...
}
Como hemos visto para definir relaciones N:N, existe la anotación @ManyToMany, usada en ambos
extremos. El modelo relacional requiere que exista una tabla de relación que contenga registros con
pares de claves foráneas a cada tabla involucrada. La definición de esta tabla y sus claves foráneas
se realiza con la anotación @JoinTable, en la clase del extremo dueño de la relación.
Tomaremos como ejemplo la relación entre las clases Cliente y Proyecto, definida en el siguiente
modelo de clases:
Para persistir esta relación, necesitamos utilizar una tabla que contenga las relaciones entre ambas
clases. Esta tabla tendrá claves foráneas a las tablas en las que se persisten las clases Cliente y
Proyecto. El modelo de datos para esta relación es:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Para implementar esta relación, seleccionamos a la clase Proyecto como "dueña" de la misma. Esto
significa que en atributo clientes de esta clase vamos a definir la tabla de relación a utilizar, y las
claves foráneas.
@ManyToMany
@JoinTable(name = "clientes_proyectos",
joinColumns = {@JoinColumn(name = "proyecto", referencedColumnName = "codigo") },
inverseJoinColumns = { @JoinColumn(name = "cliente", referencedColumnName = "codigo") })
private Set<Cliente> clientes = new HashSet<Cliente>();
Con el atributo name de la anotación @JoinTable indicamos el nombre de la tabla de relación. Con
el atributo joinColumn definimos la clave foránea en la tabla clientes_proyectos a la tabla
proyectos. Con el atributo inverseJoinColumns definimos la clave foránea en la tabla
clientes_proyectos a la tabla clientes. Si alguna de las clases tuviera una clave compuesta, en
estos atributos deberíamos especificar todas las columnas de dicha clave.
En la clase Cliente sólo tenemos que anotar la relación con @ManyToMany, y utilizar el atributo
mappedBy para indicar que la relación está definida en el atributo clientes de la clase Proyecto:
@OneToMany(mappedBy = "clientes")
private Set<Proyecto> proyectos = new HashSet<Proyecto>();
Operaciones en cascada
Cuando trabajamos con las entidades, en ocasiones creamos un árbol de objetos, y queremos
persistir todo ese árbol al mismo tiempo. Para no tener que solicitar a JPA que persista los objetos
uno a uno, podemos indicar en la definición de las relaciones que algunas operaciones se propaguen
en cascada. Esto significa que, por ejemplo, persistiendo el objeto "raíz" del árbol que creamos, JPA
propagará la operación de persistencia a todos los objetos relacionados. Esta propagación se define
para cada relación en particular; de manera que, para una clase determinada, podemos definir qué
operaciones (si alguna) se propagarán para qué relaciones.
En nuestra aplicación, un Empleado está compuesto por un Curriculum, y cada Curriculum está
compuesto por un conjunto de ItemCurriculum. Es natural que queramos crear el empleado, su
curriculum, y los ítems del curriculum, y persistir todos estos objetos en una única operación.
Asimismo, en algún momento modificaremos los datos del empleado, y agregaremos ítems a su
currículum. También querremos, en este caso, propagar la operación de actualización.
Para definir, en una relación con cualquier cardinalidad, qué operaciones deben propagarse, se
utiliza el atributo cascade en la anotación de la relación. Este atributo puede tener los siguientes
valores:
CascadeType.PERSIST: se propaga la operación de persistir.
CascadeType.MERGE: se propaga la operación de sincronizar la base de datos con las
modificaciones realizadas al objeto.
CascadeType.REFRESH: se propaga la operación de actualizar el objeto con los datos de la base
de datos.
CascadeType.REMOVE: se propaga la operación de eliminar el objeto.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
CascadeType.ALL: valor de convenciencia que inca que se propagan todas las operaciones
anteriores.
En el atributo cascade se puede utilizar más de uno de estos valores. Por ejemplo, una relación 1:N
podría anotarse de la siguiente manera:
@OneToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE})
Con estos valores en el atributo cascade, se propagarán las operaciones de persistencia y
sincronización de la base de datos, pero no las otras dos.
Si se utiliza el valor CascadeType.ALL, no debe utilizarse ningún otro valor.
Por defecto, JPA no propaga ninguna operación. Por lo tanto, si para una relación determinada
no especificamos CascadeType.PERSIST o CascadeType.ALL, deberemos persistir todos los objetos
explícitamente, uno a uno.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
semánticamente incorrecto, aún si esa colección estuviera vacía en la base de datos. En otras
palabras, si el Curriculum no tuviera items cargados, la última línea debería mostrar 0 sin generar
un error. Que una colección esté vacía no es lo mismo que la colección no exista. Lo mismo ocurre
con la relación Empleado -> Curriculum. Según el modelo de dominio, un Empleado siempre tiene
un Curriculum asociado, con lo cual es correcto que el programador realice operaciones sobre
e.getCurriculum() sin verificar previamente si el getter devuelve null.
Para no violar esta semántica, los proveedores de persistencia utilizan proxies. Estos proxies se
encargan de encapsular el comportamiento de obtener el objeto real de la relación (en los casos de
relaciones 1:1 y N:1) o la colección (en los casos de relaciones 1:N y N:N). A través de los proxies,
el proveedor de persistencia puede "interceptar" la llamada a algún método de ese objeto o
colección, y obtenerlos sólo cuando se los utilice.
Saber esto es importante. Si intentamos pasar un Entity Bean entre servidor y cliente, es
fundamental saber si el proveedor de persistencia inyectó un proxy para alguna relación; los proxies
no funcionarán en la capa del cliente, sino solamente en la capa del servidor.
Enunciado
Vamos a realizar una aplicación que buscará un departamento y devolverá sus datos utilizando para
la lógica un Bean de entidad.
Para ello, utilizaremos la tecnología EJB y acceso a la base de datos mediante unidades de
persistencia.
Lo primero que vamos a realizar será crearnos un nuevo proyecto web Application.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Dejaremos el nombre de la unidad de persistencia por defecto WebApplication3PU.
Seleccionaremos un proveedor, Toplink en nuestro ejemplo y configuraremos un acceso a datos
como hemos aprendido en el módulo. Nuestro origen de datos se llama CONEXIONORACLE.
Marcaremos None como estrategia de generación de la tabla.
Es imprescindible poner NONE para que no elimine los registros ni las tablas del servidor, de esta
forma, todo quedará administrado y mantendrá los datos originales de las tablas.
Veremos que nos ha creado el fichero persistence.xml para administrar nuestros objetos Entity de la
base de datos.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Sobre nuestro paquete generado, vamos a agregar los objetos de la base de datos para trabajar.
Agregaremos un objeto Entity Classes from Database de la carpeta Persistence.
Buscamos las tablas que vayamos a agregar como entidades de la base de datos. En nuestro
ejemplo, utilizaremos la tabla DEPT.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Dejamos los valores por defecto en la siguiente pantalla.
Ahora vamos a agregar un servlet para poder hacer las consultas sobre las entidades.
Agregamos un servlet y escribimos el siguiente código:
package packageJTA;
import java.io.IOException;
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
import java.io.PrintWriter;
import java.util.*;
import javax.persistence.*;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class NewServlet extends HttpServlet {
private void Pintar(PrintWriter out, short iddept)
{
EntityManagerFactory emf = Persistence.createEntityManagerFactory("WebApplication3PU");
EntityManager em = emf.createEntityManager();
Query consulta = em.createQuery("SELECT d FROM Dept d");
List<packageJTA.Dept> listadept = consulta.getResultList();
Dept dep = em.find(Dept.class,iddept);
if (dep == null)
{
out.println("<h4>No se ha encontrado el departamento</h4>");
}else{
out.println("Departamento encontrado: "+ dep.getDname());
}
out.println("<h4>Lista de departamentos</h4>");
out.println("<table border='1'>");
for (packageJTA.Dept d : listadept)
{
out.println("<tr>");
out.println("<td>"+d.getDeptno()+"</td>");
out.println("<td>"+d.getDname()+"</td>");
out.println("<td>"+d.getLoc()+"</td>");
out.println("</tr>");
}
out.println("</table>");
out.println("<a href=index.jsp'>Volver a la busqueda</a>");
}
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
out.println("<body>");
out.println("<h1>EXCEPCION</h1>");
out.println(ex.toString());
out.println("</body>");
out.println("</html>");
} finally {
out.close();
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
}
Después una página JSP para la llamada al servlet:
<%@page import="java.util.List"%>
<%@page import="javax.transaction.Transaction"%>
<%@page import="javax.persistence.*"%>
<%@page import="packageJTA.*"%>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
</head>
<body>
<form name="form1" action="NewServlet">
Numero departamento:
<input type="text" name="txtnumero"/>
<input type="submit" value="Buscar departamento"/>
</form>
</body>
</html>
Y el resultado es:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Actividades
“Recuerde que para un seguimiento óptimo de la unidad es imprescindible realizar las actividades
que encontrará en la unidad correspondiente de la plataforma eLearning”.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Unidad 6. Implementación de Clases de
Entidad: Modelado de Relaciones de Herencia
Objetivo
Conocer el papel desempeñado por las relaciones y su modelado dentro las clases de entidad en la
tecnología EJB.
Jerarquías de clases
En todo modelo de clases, realizamos generalizaciones y abstracciones que resultan en jerarquías de
clases. Elaboramos tales jerarquías por varios motivos. Uno de ellos es generalizar un
comportamiento y/o un estado, y abstraerlos en una superclase, para hacer uso de ellos en las
subclases a través de la herencia. Otro motivo es obtener un tratamiento polimórfico de los objetos.
En EJB 3.0, JPA permite instancias de clases que pertenezcan a una jerarquía de manera sencilla e
intuitiva. En EJB 2.x, realizar esto requería soluciones propietarias, poco intuitivas y escalables,
difíciles de implementar y mantener.
En el contexto de JPA, esto plantea dos problemáticas diferentes, que analizaremos a continuación.
Superclases no persistentes
Para casos en los que queremos hacer uso de herencia de clases, sin utilizar polimorfismo, las
superclases no representan supertipos de entidades, sino que sólo existen para abstraer un
comportamiento y/o un estado. En nuestra aplicación, un ejemplo de este tipo de abstracciones es
la clase Persona. En esta clase abstraemos el estado y el comportamiento de las entidades que
representan tanto personas físicas como jurídicas.
En este caso, no haremos uso de polimorfismo en el sentido en que nunca pretenderemos obtener y
operar con instancias de esta clase directamente. Nunca buscaremos una instancia de Persona.
Buscaremos, por ejemplo, instancias de Cliente o Empleado.
Para indicar a JPA que una superclase pertenece a una jerarquía de clases persistente, pero que no
representa un supertipo de entidades, utilizamos la anotación @MappedSuperclass.
Veamos la definición de la clase Persona:
import java.util.*;
import javax.persistence.*;
/**
* Superclase para las entidades que representan personas.
*
* @author Juan Garcia
*/
@MappedSuperclass
public abstract class Persona {
@TableGenerator(name = "secuencias", table="secuencias", pkColumnName = "nombre",
valueColumnName_
= "valor", pkColumnValue = "persona")
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "secuencias")
private Long codigo;
@Embedded
private Direccion direccion;
@OneToMany(cascade = CascadeType.ALL)
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
@JoinColumn(name = "persona", updatable = false, insertable = false)
private Set<Contacto> contactos = new HashSet<Contacto>();
@Version
private long version;
// Otros atributos y métodos.
}
Utilizamos las anotaciones que ya estudiamos de JPA para describir los atributos persistentes. Las
subclases de Persona (Cliente y Empleado) podrán definir otros atributos de persistencia (por
ejemplo, en qué tabla se persisten) y también heredar los atributos definidos en Persona.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Para implementar esta estrategia, utilizamos las anotaciones @Inheritance y
@DiscriminatorColumn en la superclase:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Table(name="clientes")
@DiscriminatorColumn(name = "tipo", discriminatorType = DiscriminatorType.STRING, length = 20)
public abstract class Cliente extends Persona {
// Atributos y métodos
}
Con estas anotaciones definimos:
La estrategia de persistencia para la jerarquía.
El nombre de la tabla única donde se persistirá toda la jerarquía.
El nombre de la columna usada para discriminar entre las subclases concretas.
En las subclases, sólo debemos indicar el valor utilizado en la columna discriminadora para
identificar a la clase al recuperar instancias:
@Entity
@DiscriminatorValue("corporativo")
public class ClienteUnipersonal extends Cliente {
// Atributos y métodos
@Entity
@DiscriminatorValue("unipersonal")
public class ClienteCorporativo extends Cliente {
// Atributos y métodos
}
El modelo de datos involucra una única tabla:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Estrategia joined table
En esta estrategia, cada clase de la jerarquía de la jerarquía anotada con @Entity es persistida en
una tabla distinta. Para recuperar una instancia completa, JPA debe realizar operaciones join entre
todas las tablas involucradas. Esta estrategia permite utilizar el modelo de datos más correcto desde
un punto de vista de normalización de base de datos, con el potencial impacto en performance,
debido a las N operaciones join. Este problema pude no ser trivial si hay muchas clases en la
jerarquía, y muchos registros en la cada una de ellas.
En cada clase anotada con @Entity, entonces, podemos utilizar la anotación @Table para indicar la
tabla en la que se deberá persistir la clase. Todas las tablas deberán definir las columnas de clave
primaria definidas en la superclase.
Veremos como ejemplo la jerarquía de clases utilizada para representar los proyectos. En esta
jerarquía hay tres clases:
Proyecto: superclase para todos los tipos de proyecto.
ProyectoSimple: proyecto al que puede ser adjudicado a una única compañía; subclase de
Proyecto.
ProyectoCombinado: proyecto que puede ser adjudicado a muchas compañías; subclase de
Proyecto.
El modelo de clases está representado en el siguiente diagrama:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Para implementar esta estrategia, debemos utilizar el valor InheritanceType.JOINED en el
atributo strategy de la anotación @Inheritance en la superclase de la jerarquía, la clase Proyecto:
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "tipo")
@Table(name = "proyectos")
public abstract class Proyecto {
// Atributos y métodos
}
Dado que la clase Proyecto está anotada con @Entity, los atributos definidos en esta clase serán
persistidos en una tabla particular; en este caso, la tabla proyectos. Por otro lado, en las clases
ProyectoSimple y ProyectoCombinado establecemos la información de persistencia para sus
atributos particulares, e indicamos la tabla en la que se persistirán:
@Entity
@Table(name = "proyectos_simples")
@DiscriminatorValue("simple")
public class ProyectoSimple extends Proyecto {
// Atributos y métodos
}
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
@Entity
@Table(name = "proyectos_combinados")
@DiscriminatorValue("combinado")
public class ProyectoCombinado extends Proyecto {
// Atributos y métodos
}
El próximo diagrama representa el modelo de datos resultante. Existe una tabla proyectos para la
clase Proyecto, una tabla proyectos_combinados para la clase ProyectoCombinado y una tabla
proyectos_simples para la clase ProyectoSimple.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Aunque la jerarquía tiene tres clases, existirán solamente dos tablas: una para ContactoEMail y otra
para ContactoTelefono. Estas tablas tendrán, además de las particulares para la clase que
corresponda, las columnas para almacenar los atributos definidos en la clase Contacto.
Para implementar esta estrategia, en la clase Contacto debemos usar el valor
InheritanceType.TABLE_PER_CLASS en el atributo strategy de la anotación @Inheritance:
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@IdClass(ContactoPK.class)
public abstract class Contacto {
// Atributos y métodos.
}
Luego, definimos las clases ContactoTelefono y ContactoEMail, indicando las tablas en las que se
persistirá cada una con la anotación @Table.
@Entity
@Table(name="contactos_telefono")
public class ContactoTelefono extends Contacto {
// Atributos y métodos.
@Entity
@Table(name="contactos_email")
public class ContactoEMail extends Contacto {
// Atributos y métodos.
}
El modelo de datos contiene entonces solamente dos tablas para las dos clases concretas, con todos
los atributos de las superclases y clases embebidas.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Comparando el modelo de datos utilizado para estrategia con los utilizados para los dos anteriores,
vemos que este es más difícil de mantener. Un cambio en cualquier superclase requiere la
modificación de tantas tablas como clases concretas tenga la jerarquía.
La elección de una estrategia de persistencia para una jerarquía de clases debe surgir de un análisis
cuidadoso de las características de la jerarquía y el uso que tendrá, para determinar cuál es el más
eficiente.
Actividades
“Recuerde que para un seguimiento óptimo de la unidad es imprescindible realizar las actividades
que encontrará en la unidad correspondiente de la plataforma eLearning”.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Unidad 7. Uso del Lenguaje de Consulta (Ql)
de Java Persistence
Objetivo
Conocer y manejar el lenguaje de consultas EJB QL.
Introducción
Una de las ventajas de la persistencia gestionada por contenedor es que le abstrae del almacén
persistente subyacente: no necesitamos saber cómo se almacenan nuestros beans o qué tipo de
base de datos se está utilizando. Sin embargo, una consecuencia de este requisito es que
necesitamos un lenguaje independiente de la base de datos para expresar las consultas. Por
ejemplo, si queremos tener una consulta que realice la siguiente búsqueda:
“Encontrar todos los empleados cuyo apellido sea García”.
Pues bien, antes de EJB 2.0, no existia un lenguaje estándar de este tipo, por lo que todos los
desarrolladores de contenedores EJB diseñaban el suyo propio. Sin embargo, con EJB 2.0 se
introdujo el lenguaje EJB QL, EJB Query Language.
EJB QL es un lenguaje cuya sintaxis se parece a SQL aunque también es diferente en algunos
aspectos. Igual que las relaciones, las instrucciones QL EJB son especificadas en los descriptores de
implementación y el contenedor general de la instrucci6n SQL correspondiente que no tiene que por
que ser SQL, para llevar a cabo una consulta.
El reto que EJB QL intenta resolver es asociar dos mundos diferentes:
• objetos Java
y filas de bases de datos.
Hoy por hoy, las bases de datos relacionales (RDBMS) todavía prevalecen, pero esto podria cambiar
en el futuro a medida que dichas bases de datos sea orientadas a objetos (OODBMS) y se hagan
cada vez más populares. EJB QL intenta abstraer este posible cambio de paradigma de modo que
con cualquier lenguaje de consulta que se utilice para tornar datos de la base de datos, nuestro
desarrollo EJB siga trabajando.
Realizar consultas en los Entity Beans disfruta de la misma importancia que definir la manera de
guardarlos, en unidades anteriores vimos algunas búsquedas y operaciones realizadas a través de
los métodos de la clase EntityManager.
En aquella ocasión los métodos indicaban que había dos maneras de buscar datos.
- La primera consiste en utilizar el lenguaje de consulta EJBL (Enterprise JavaBeans Query
Language), que veremos con detalle a continuación. Esta forma es siempre preferible puesto
que ya no trabaja a nivel de tablas sino a nivel de los Beans y por lo tanto es totalmente
independiente de la estructura de base de datos como decíamos algunos párrafos atrás .
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
- La otra opción consiste en echar mano del SQL nativos de aquella base de datos sobre la que
queramos operar. Si no queda más remedio podremos optar por esta posibilidad. Algo que
puede motivar su empleo podría ser que debamos incluir en la consulta columnas de las
tablas de bases de datos que no están administradas por ningún Entity Bean. A estas
columnas no se puede acceder mediante EJB QL.
Hay por lo tanto una primera limitación importante en el lenguaje QL: Solo podemos consultar
aquellas columnas que hayan sido incluidas dentro de un EJB.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
public int executeUpdateO;
public Query setMaxResults(int maxResult();
public Query setFirstResult(int startPosition) ;
public Query setHint(stri ng hintName, Object value);
public Query setParameter(String name, Object value);
public Query setParameter(string name, Date value, TemporalType témporaiType);
public Query setParameter(String name, Calendar value, TemporalType temporal Type);
public Query setparameter(i nt position, object value);
public Query setParaineter(int position, Date value, TemporalType temporal Type);
public Query setParameter(int position, calendar value, TemporalType temporal Type);
public Query setFlushMode(FlushModeType flushMode);
}
Cuando invocamos a los métodos createQuery() y createNameQuery() pertenecientes a la clase
EntityManager obtenemos una instancia de la interfaz Query. Veamos un fragmento de código
ilustrativo de su uso. En primer lugar vemos la entidad y sus correspondientes anotaciones con la
inclusión de una SELECT en una de ella. Dicha select que anotado e identificada con
EMPLEADO.ENCONTRARTODOS. De este modo, hemos ampliado la clase/tabla EMPLEADO con un
nuevo método/consulta:
@Entity
@NamedQueries({
@NamedQuery( name=”EMPLEADO.ENCONTRARTODOS”,
query=”SELECT P FROM EMPLEADO P ORDER BY P.NUM_EMPLEADO”)
})
@Table(name=”EMPLEADO”)
Public class EMPLEADO{
…
}
Acabamos de ver la declaración de la consulta mediante anotación. Veamos ahora cómo
obtendríamos una instancia de Query a partir de ella. Para ello necesitaremos un objeto derivado de
EntityManager que aquí llamaremos manager:
Query CONSULTA=manager.createNamedQuery( “EMPLEADO.ENCONTRARTODOS”)
Repasemos ahora los principales métodos de esta interfaz, su funcionalidad y empleo:
getResultList()
Podríamos decir que estamos ante el método más importante de esta interfaz. A través de él
realizamos una consulta y obtenemos un resultado. Dicho resultado es un objeto o instancia
derivado de la clase java.util.List. Podremos hacer uso de este objeto en diferentes contextos pero
para hacernos una idea de su empleo dentro de EJB conviene atender al siguiente fragmento de
código.
Por un lado vamos a tener declarada una entidad conforme a lo aprendido en las unidades
anteriores.
@Entity
@NamedQueries({
@NamedQuery( name=”EMPLEADO.ENCONTRARTODOS”,
query=”SELECT P FROM EMPLEADO P ORDER BY P.NUM_EMPLEADO”)
})
@Table(name=”EMPLEADO”)
Public class EMPLEADO{
…
}
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Como vemos hemos creado una entidad mediante anotaciones. Una tabla EMPLEADO a partir de la
clase EMPLEADO. Y Luego hemos dado nombre a una consulta “EMPLEADO.ENCONTRARTODOS”. El
contenido de dicha consulta y, verdadera consulta en sí misma, lo hemos especificado en el segundo
parámetro de la anotación: ”SELECT P FROM EMPLEADO P ORDER BY P.NUM_EMPLEADO”.
De este modo, cuando llamemos al método EMPLEADO.ENCONTRARTODOS, será como si llamamos
a la SELECT que hemos definido.
Veamos ahora como procederíamos a hacer uso de este método dentro del código:
Hacemos una función OBTENEREMPLEADOS que nos devolverá un objeto List con los datos
buscados. Es decir, un listado con todos los empleados de nuestra tabla y ordenados por el número
de empleado que tenga asignado cada uno de ellos.
}
Como vemos al pasarle al método createNamedQuery el nombre de nuestro método recuperamos
una instancia Query sobre la que ya podemos aplicar el método getResultList para obtener los
resultados buscados.
Hay que aclarar que al realizar la consulta de esta forma, lo que obtenemos en una lista de objetos
empleado y dentro de cada empleado tendríamos disponibles sus atributos. Pero podemos hacer que
la lista devuelta sea una enumeración de datos pertenecientes a un atributo concreto de dicha clase.
Basta con cambiar la consulta de esta forma:
”SELECT P.APELLIDOS FROM EMPLEADO P ORDER BY P.NUM_EMPLEADO”
Como vemos, a través de la P.APELLIDOS estamos accediendo en la consulta a un atributo concreto
del objeto y no a todo el objeto en sí.
El código completo quedaría así:
@Entity
@NamedQueries({
@NamedQuery( name=”EMPLEADO.TODOSAPELLIDOS”,
query=”SELECT P.APELLIDOS FROM EMPLEADO P ORDER BY P.NUM_EMPLEADO”)
})
@Table(name=”EMPLEADO”)
public class EMPLEADO{
…
}
En la llamada a lo anterior apenas tendríamos que cambiar al invocación al método correspondiente
dado que la Lista es capaz de almacenar referencias a instancias de objetos o bien a una relación de
atributos concretos, en nuestro caso simples String recuperables uno a uno a través de un Iterator.
public List OBTENEREMPLEADOS(){
Query CONSULTA=manager.createNamedQuery( “EMPLEADO.TODOSAPELLIDOS”)
List LISTA=CONSULTA.getResultList()
return LISTA
}
También podemos devolver varios valores por fila. Es decir, dos campos o atributos de una
tabla/clase. En ese caso, nuestra select adoptaría la siguiente definición:
”SELECT E.APELLIDOS, E.NOMBRE FROM EMPLEADO E ORDER BY E.NUM_EMPLEADO”.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
En el código ocuparía el lugar correspondiente dentro de la anotación de la entidad.
@Entity
@NamedQueries({
@NamedQuery( name=”EMPLEADO.TODOSAPELLIDOS”,
query=”SELECT E.APELLIDOS, E.NOMBRE FROM EMPLEADO E ORDER BY E.NUM_EMPLEADO”)
})
@Table(name=”EMPLEADO”)
public class EMPLEADO{
…
}
}
¿Qué pasaría si combinamos en la consulta campos procedentes de varias tablas? Nada de
particular, el objeto List almacenaría las instancias entregadas como nuevas filas de su lista.
@Entity
@NamedQueries({
@NamedQuery( name=”EMPLEADO.VENTASXEMPLEADOS”,
query=”SELECT E.APELLIDOS, V.VENTAS FROM EMPLEADO E, VENTAS V WHERE
E.NUM_EMPLEADO=V.NUM_EMPLEADO”)
})
@Table(name=”EMPLEADO”)
public class EMPLEADO{
…
}
@Table(name=”VENTAS”)
public class VENTAS{
…
}
Y la invocación no variaría en nada:
public List OBTENEREMPLEADOS(){
Query CONSULTA=manager.createNamedQuery( “EMPLEADO.TODOSAPELLIDOS”)
List LISTA=CONSULTA.getResultList()
return LISTA
getSingleResult()
Si estamos ante una consulta que solo puede recuperar una línea como resultado, entonces
podemos recogerla a través getSingleResult(). El resultado es del tipo object y se adaptará
al número de columnas seleccionadas previamente. Al igual que en el caso del método
getResultList(), se tratará o bien de una única instancia del tipo seleccionado y/o un
object[], que representará la lista de selección.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Si el resultado debe abarcar más de una línea, se produce un error del tipo
NonuniqueResultException. En cambio, si no se pudo seleccionar nada, el resultado será null.
executeUpdate()
Aparte de las consultas también se pueden utilizar en EJB QL, las instrucciones UPDATE y DELETE, que
pueden influenciar a toda una serie de Entity Beans. El método utilizado para ello
executeUpdate() devuelve el número de filas eliminadas o modificadas.
Este tipo de actualizaciones en lotes deben efectuarse siempre al inicio de una transacción, antes de
que se empiece a seleccionar beans por separado.
Es interesante que este tipo de instrucciones también se pueden definir mediante una anotación
@NamedQuery en la clase Bean
setMaxResults() y setFirstResult()
En muchas ocasiones resulta necesario al tratar datos, presentar su volumen de una manera
manejable mediante la paginación. En estos casos podremos utilizar los métodos
setMaxResultsO y setFirstResult(). Con setMaxResults() se transmite el número de
filas que se quieren obtener como máximo en el resultado. Con setFirstResult() se puede fijar
la posición inicial en la lista, empezando por 0, a partir de la cual debe empezar la selección de las
filas n.
Ambos métodos devuelven el Query como resultado y se pueden combinar entre ellos. Por ejemplo
el siguiente fragmento de código se sitúa en el registro cien devuelto por la consulta, pero nos
recupera los 25 registros siguientes a dicha posición y ninguno de los cien primeros salvo el último.
Query query = manager.createNamedQuery(“EMPLEADO.
TODOSEMPLEADOS”).setFirstResult(100).setMaxResults(25);
List lista = query.getResultList();
return lista;
setHint()
Resulta posible incluir requisitos específicos en la consulta mediante el método setHint(). Dicho
método emplea como primer parámetro una cadena de símbolos con el nombre de la propiedad y en
el segundo parámetro una instancia Object con el valor correspondiente. Veamos un ejemplo:
manager.createNamedQuery(“EMPLEADO.ENCONTRARAPELLIDO”).setHint(“org.hibernate.timeout”,
new integer(1000)).
En el código anterior invocamos en el primer parámetro un método determinado y el en segundo le
pasamos a dicho método un valor compatible.
setParameter(...)
A la hora de pasar parámetros a una consulta, disponemos en total de seis métodos diferentes con
el nombre setParameter. Tres de ellos trabajan con los parámetros en función de su denominación,
mientras que los otros tres operan sobre la posición de los mismos. Por lo tanto, contamos con dos
maneras básicas de operar:
- Identificando los parámetros con un nombre.
- Identificando los parámetros mediante su posición.
@Entity
@NamedQueries({
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
@NamedQuery( name=”EMPLEADO.VENTASXEMPLEADOS”,
query=”SELECT E.APELLIDOS, V.VENTAS FROM EMPLEADO E, VENTAS V WHERE
E.NUM_EMPLEADO=V.NUM_EMPLEADO”)
})
@Table(name=”EMPLEADO”)
public class EMPLEADO{
…
}
@Table(name=”VENTAS”)
public class VENTAS{
…
}
}
Cuando queremos definir un parámetro dentro de una expresión de EJB-QL debemos hacer uso de la
siguiente sintaxis “:nombre”, es decir, dos puntos y el identificador con el que queramos denominar
al parámetro. A su vez, agregamos dicho parámetro al código mediante la instrucción
setParameter(String, object). De este modo, quedarán conectadas la expresión EJB-QL y el código
en Java.
@Entity
@NamedQueries({
@NamedQuery(name=”EMPLEADO.ENCONTRARAPELLIDO”,
query=”SELECT a FROM EMPELADO E “ + “WHERE E.APELLIDO=?1”)
})
setFlushModel()
Se encarga de actualizar en la base de datos los cambios producidos en el beans. Cuando el
EntityManager ejecuta una consulta, comprueba si administra actualmente algún beans en el que se
haya producido algún tipo de alteración del que la base de datos no aún no ha tenido constancia. Si
estamos en esta situación, entonces se ejecutan primero las modificaciones en base de datos y
luego la consulta en sí. Este es el procedimiento por defecto que tiene el valor AUTO.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Ahora bien, también podemos personalizar este método setFlushModel mediante el valor COMMIT, el
cual traslada las modificaciones a la base de datos una vez han finalizado las transacciones y no
justo antes de realizar la consulta.
También el programador tiene a su disposición un método directo de actualización inmediata de la
base de datos llamado flush() y perteneciente a la clase EntityManager.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Los datos retornados serían todos los empleados. El método de búsqueda findall() y La expresión
FROM declararía una variable de identificación llamada e, omitiendo la palabra opcional AS. Si la
palabra AS fuera incluida, la expresión debería ser escrita como sigue:
FROM e AS empleados
Dentro de la sentencia FROM debemos tener presentes algunas consideraciones cuando queramos
emplear en ella una variable de identificador como acabamos de hacer. Así, un identificador es una
secuencia de uno o más caracteres válidos para EJB QL (letras, $, _).
El símbolo ‘?’ es un carácter reservado en EJB QL y no puede ser usado en una variable de
identificador.
Del mismo modo, las siguientes palabras están reservadas y no pueden ser usadas como
identificador:
AND
MEMBER
AS
NOT
BETWEEN
NULL
DISTINCT
OBJECT
EMPTY
OF
FALSE
OR
FROM
SELECT
IS
TRUE
IN
UNKNOWN
LIKE
WHERE
Así pues, una variable de identificación es un identificador declarado en la sentencia FROM. Aunque
las sentencias SELECT y WHERE pueden referenciar variables de identificación, estas no pueden
declararlas.
La sentencia FROM puede contener múltiples declaraciones, separadas por comas. Una declaración
puede referenciar otra variable de identificación que ha sido previamente declarada (a la izquierda).
En la siguiente sentencia FROM, la variable V referencia la variable E declarada anteriormente.
FROM EMPLEADO E, IN (E.VENTAS) AS V
Si una variable de identificación no es usada en la sentencia WHERE, esta declaración puede afectar
el resultado de la expresión QL. La siguiente query retorna todos los EMPLEADOS, tengan VENTAS o
no en su haber.
SELECT OBJECT(E) FROM EMPLEADOS E
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Por otra parte, el siguiente query declara la variable de identificación V, y busca todos los
EMPLEADOS que tengan alguna venta team:
SELECT OBJECT(E)
FROM EMPLEADO E, IN (E.VENTAS) AS V
La siguiente query retorna el mismo resultado que la anterior, pero la sentencia WHERE hace la
lectura mas fácil:
SELECT OBJECT(E)
FROM EMPLEADO E
WHERE E.VENTAS IS NOT EMPTY
Existen dos tipos de declaraciones:
Declaraciones de Rango Variable: Una variable de identificación puede extenderse sobre el
abstract schema type de un entity bean. En el ejemplo siguiente, una variable de identificación
llamada E representa el esquema del llamado EMPLEADO:
FROM EMPLEADO E
También puede incluir el operador opcional AS:
FROM EMPLEADO AS E
Declaraciones de miembros de colecciones: una variable de identificación puede representar
un miembro de una colección de entity beans. Una declaración de miembro de colección debe
incluir el operador IN, pero puede omitir el operador AS. En el siguiente ejemplo, la variable
de identificación V representa a un miembro de la colección VENTAS.
FROM EMPLEADO E, IN (E.VENTAS) AS V
Sentencia Where
La sentencia WHERE especifica una expresión condicional que limita el valor retornado por la
consulta. La expresión retorna todos los valores para los cuales la expresión es TRUE. La sentencia
WHERE es opcional, aunque se usa comúnmente en las queries. Si esta sentencia es omitida,
entonces la query retorna todos los valores.
Literales:
Cuando queremos incluir valores literales dentro de una consulta QL deberemos distinguir algunos
casos.
- Literales String: un literal string se cierra con comillas simples: ‘Jorge’. Si el string lleva una
comilla simple, esto se indica insertando dos comillas simples: ‘Jorge’’s’ .
- Literales numéricos: existen dos tipos, exactos y aproximados. Un literal numérico exacto es
un número sin punto decimal: 65, -233, +12. Un literal numérico aproximado es un valor
numérico con punto decimal: 57., -85.7, +2.1 .
Parámetros de entrada:
Ya vimos anteriormente que un parámetro de entrada es designado por un ‘?’ seguido por un
número entero. Por ejemplo, el primer parámetro de entrada es ?1, el segundo ?2, etc. Las
siguientes reglas se aplican a los parámetros de entrada:
- Sólo se pueden usar en la sentencia WHERE
- Su uso es restringido a expresiones condicionales
- Deben ser numerados, comenzando en 1.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
- El número de parámetros de entrada en la sentencia WHERE no debe exceder el número de
parámetros en entrada en el correspondiente finder o método de selección.
- El tipo de parámetro de entrada debe coincidir con el argumento correspondiente en el finder
o método de selección.
Dentro de WHERE podemos hacer uso de operadores aritméticos, de comparación y lógicos:
Con la expresión BETWEEN determinamos el rango de valores que devolverá nuestra consulta. Las
siguientes expresiones son equivalentes:
p.age BETWEEN 15 AND 19
p.age >= 15 AND p.age <= 19
Las siguientes expresiones también son equivalentes:
p.age NOT BETWEEN 15 AND 19
p.age < 15 OR p.age > 19
IN: La expresión IN determina si un string pertenece o no a cierta cadena de caracteres.
En el siguiente ejemplo, si el país es UK la expresión es verdadera. Si el país es Perú, entonces es
falsa.
o.country IN (‘UK’, ‘US’, ‘France’)
LIKE: la expresión LIKE determina caracteres específicos que buscaremos en un string. Ej:
- (%) representa cero o mas caracteres
- (_) representa cualquier carácter individual
- ESCAPE se puede utilizar para buscar los caracteres _ y %
En la siguiente tabla podemos ver algunos ejemplos de su empleo dentro de expresiones de QL y los
valores que encontraría o no encontraría en su búsqueda:
Tambien EJB QL incluye diferentes funciones útiles, las cuales se encuentran listadas a continuación:
Sintaxis de la función Operación realizada
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
indicando la posición donde
comenzamos a extraerla y el tamaño de
la subcadena a extraer
Sentencia ORDER BY
Como su nombre lo dice, la sentencia ORDER BY ordena los valores retornados por la consulta.
Se puede especificar si queremos los objetos ordenados ascendente o descendentemente con las
palabras ASC (default) y DESC.
Restricciones
Hay una serie de restricciones importantes que conviene conocer bien en el lenguaje QL de EJB:
- Los comentarios no están permitidos.
- Para comparar valores de fechas y tiempo en una query EJB QL, se debe usar el tipo long
para representar los valores en milisegundos.
- No se debe usar los objetos java.util.Date ni java.sql.Time en comparaciones EJB QL.
- Como el soporte para los tipos BigDecimal y BigInteger es opcional en contenedores EJB 2.1,
las aplicaciones que usen estos tipos en una consulta EJB QL pueden no ser soportadas.
- Dos entity beans de tipos diferentes no pueden ser comparados.
Enunciado
Vamos a realizar una aplicación que creará tablas en nuestra base de datos a partir de entidades de
un EJB.
Para ello, utilizaremos la tecnología EJB y acceso a la base de datos mediante unidades de
persistencia.
Vamos a visualizar un ejemplo en el que crearemos un acceso a la base de datos Oracle y nos
crearemos tablas desde entidades creadas en nuestro proyecto.
Nos creamos un nuevo proyecto.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Lo llamaremos AplicacionJTA.
Ahora sobre nuestro proyecto, vamos a agregarnos una unidad de persistencia que es el objeto
encargado de conectar nuestras entidades con la base de datos.
Seleccionamos New Other.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
En la pestaña de Persistence, seleccionamos el objeto Persistence Unit.
Ahora nos debemos de crear una conexión sobre la base de datos Oracle. Dicha conexión quedará
guardada en nuestro IDE NetBeans para futuras acciones.
Seleccionamos del desplegable Data Source la opción New Data Source…
Nos aparecerá una ventana en la que debemos seleccionar la conexión de la base de datos y el
nombre del JNDI. Como JNDI pondremos CONNECTIONORACLE y seleccionáramos la opción New
Database Connection…
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Tendremos un desplegable dónde nos mostrarán los diferentes drivers para conexión de base de
datos instalados con NetBeans. Seleccionamos New Driver.
Una vez que hemos seleccionado nuestro driver de Oracle, debemos configurar los elementos
necesarios para trabajar con el servidor.
Host: LOCALHOST
PORT: 1521
SERVICE ID: XE
USER NAME: SYSTEM
PASSWORD: 12345 (Password que hayamos creado para SYSTEM en Oracle)
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Pulsamos en siguiente y nos mostrará una pantalla en la que podríamos seleccionar el propietario de
la base de datos desde el que deseamos visualizar las tablas.
Dejamos por defecto nuestro usuario SYSTEM y finalizamos.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Como podemos comprobar en la unidad de persistencia, podríamos cambiar las opciones cuando lo
necesitemos.
Ahora nos vamos a crear un paquete para poner ahí las entidades.
Lo llamaremos packagejta
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Sobre nuestro paquete, incluiremos una nueva Entity Class desde la carpeta Persistence.
Nuestra clase se llamará Jugador y nos crearemos una clave primary key de tipo Integer.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Ahora vamos a implementar propiedades y características de la clase.
Nos creamos tres propiedades.
private String NombreJugador;
private String Posicion;
private int Calidad;
Ahora vamos a implementar algo el código de la clase con constructores para facilitarnos la
programación posterior.
public Jugador()
{
this.NombreJugador = "";
this.Posicion = "";
this.Calidad = 0;
}
public Jugador(int idjugador, String nombrejugador, String posicion, int calidad)
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
{
this.id = idjugador;
this.NombreJugador = nombrejugador;
this.Posicion = posicion;
this.Calidad = calidad;
}
La creación de la tabla jugador en la base de datos la realizaremos desde un Servlet.
Nos creamos un nuevo Servlet sobre el proyecto.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Lo llamaremos ServletJTA
Ahora vamos a utilizar el editor de NetBeans para generarnos los objetos de la unidad de
persistencia. Para ello, seleccionamos con el botón derecho sobre nuestro código Persistence Use
Entity Manager
Nos generará un método para acceder a la unidad de persistencia. Nosotros vamos a cambiar su
código para adaptarlo y lo renombraremos. Este es el códido del método que debemos realizar.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("AplicacionJTAPU");
public void CrearBBDD()
{
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
ArrayList<Jugador> jugadores = new ArrayList<Jugador>();
Jugador j = new Jugador();
j.setId(1);
j.setNombreJugador("Iniesta");
j.setCalidad(9);
j.setPosicion("Mediapunta");
jugadores.add(j);
j = new Jugador();
j.setId(2);
j.setNombreJugador("Xavi Alonso");
j.setCalidad(8);
j.setPosicion("Mediocentro");
jugadores.add(j);
j = new Jugador();
j.setId(3);
j.setNombreJugador("Iker Casillas");
j.setCalidad(10);
j.setPosicion("Portero");
jugadores.add(j);
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
out.println("<h1>"+e.toString()+"</h1>");
out.println("</body>");
out.println("</html>");
} finally {
out.close();
}
}
A continuación, vamos a escribir un formulario en la página index.jsp que realizará la llamada al
Servlet
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
</head>
<body>
<form name="form1" action="ServletJTA">
<input type="submit" value="Crear BBDD con jugadores">
</form>
</body>
</html>
Por último, agregamos el driver oracle
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Ejecutamos la aplicación completa con la tecla F6 y comprobaremos su resultado.
Actividades
“Recuerde que para un seguimiento óptimo de la unidad es imprescindible realizar las actividades
que encontrará en la unidad correspondiente de la plataforma eLearning”.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Unidad 8. Desarrollo de Aplicaciones Java EE
Mediante el Uso de Mensajes
Objetivo
Conocer y manejar los beans controlados o conducidos a través de mensajes.
Introducción
El servicio de mensajería es un método de comunicación entre componentes de software o
aplicaciones. Es un sistema de estilo peer-to-peer, donde un cliente puede enviar y/o recibir
mensajes de cualquier otro cliente. Cada cliente se conecta a un agente de mensajería que le
permite crear, enviar, recibir y leer mensajes. Un servicio de mensajería permite comunicación
distribuida "libremente unida".
Un emisor envía un mensaje y el receptor lo recibe, pero no necesariamente deben estar disponibles
al mismo tiempo. Es más, tanto emisor como receptor deben conocer solamente el formato del
mensaje y dónde debe éste ser enviado. Esta característica lo diferencia de sistemas
"estrechamente unidos", como RMI, que requiere que la aplicación sepa los métodos de la aplicación
remota. El servicio de mensajería también se diferencia de los emails dada su naturaleza de
comunicación. La Java Message Service API (JMS) permite a las aplicaciones crear, enviar, recibir
y leer mensajes.
Por lo tanto, JMS es el API de Servicios de Mensajería de Java, hoy en día es un estándar de
mensajería para sistemas empresariales de mensajes, que permite a los componentes de
aplicaciones basados en la plataforma de Java 2 crear, enviar, recibir y leer mensajes.
JMS es considerado un middleware (capa media) en la comunicación entre
dos aplicaciones, haciendo posible que esta comunicación sea confiable de manera síncrona y
asíncrona.
Define un set de interfaces y semánticas para que los programas Java puedan comunicarse con
otros sistemas de mensajería. Fue creada como un estándar y apunta a la portabilidad de las
aplicaciones JMS, lo que no quita que uno pueda hacer aplicaciones de mensajería sofisticadas.
Además de permitir una comunicación "libremente unida", permite que sea asíncrona y confiable.
La comunicación asincrónica es una de las principales características de JMS y de los sistemas de
mensajería en general, pues permite que la aplicación origen envíe el mensaje, para que el sistema
o servidor de mensajes (middleware) lo reciba y lo envíe a la aplicación destino cuando esta se
conecte al servidor; como se puede entender a diferencia de una comunicación sincrónica
(cliente/servidor) en este tipo de comunicación no se requiere que la aplicación destino este
presente al momento del envío del mensaje.
La arquitectura de una aplicación JMS se compone de:
- Un proveedor JMS, que implementa las interfaces y provee características administrativas y
de control.
- Clientes JMS o nativos, que son aplicaciones o componentes que emiten y reciben mensajes.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
- Mensajes, que son los objetos comunicados.
- Objetos administrados, que son objetos preconfigurados para ser usados por el cliente. Éstos
son o "connection factories" o "destinations".
Dominios de Mensajes
La especificación JMS implementa dos modelos o dominios de mensajes, los cuales define el tipo de
aplicación que se puede desarrollar, y estos son:
- Dominio punto a punto.
- Dominio publicador/suscriptor.
La posibilidad de implementación depende del proveedor JMS que se utilice, el proveedor JMS del
J2EE soporta ambos dominios, y un cliente JMS puede combinar ambos dominios para extender el
poder y la funcionalidad de los productos de mensajería.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
- El productor y el consumidor de mensajes trabajan de forma independiente, es decir el
receptor del mensaje lo puede leer sin necesidad de que el cliente productor este en
ejecución y viceversa. No hay dependencia temporal entre ambos.
- Un consumidor de mensajes siempre tiene que confirmar la lectura o recepción del mismo a
la cola de mensajes, para que esté pueda ser vaciado, caso contrario una vez que el cliente
consumidor finalice o cierre su conexión con la cola, el mensaje podrá ser leído por otro
consumidor en el caso de existir o por el mismo una vez que establezca nuevamente la
conexión con la cola.
La cola de mensajes
Una cola de mensajes es un lugar común (la heladera) donde algunas aplicaciones publican
mensajes, que son consumidos por otras. Existen así 4 componentes principales en un sistema de
mensajería:
- publicador, es decir, quien publica un Mensaje en una Cola
- consumidor, quien consume Mensajes de una Cola
- mensaje, que tiene algún formato que tanto publicador como consumidor conocen.
- cola, que es el lugar donde publicadores y consumidores se conectan y comunican a través
de mensajes.
Es importante destacar que es un sistema asíncrono: el publicador y el consumidor no necesitan
estar disponibles a la vez, ya que la cola actúa de intermediario, guardando y distribuyendo los
mensajes a medida que el consumidor pueda atenderlos.
Queue
En las Queue, existen uno o varios publicadores, y un consumidor. Los publicadores van dejando sus
mensajes en la cola, y son tomados en orden por el consumidor (y luego se van descartando). Si el
consumidor no está disponible, la cola va guardando ("encolando") los mensajes, de manera que el
consumidor pueda retomar su procesamiento cuando vuelva a estar online.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
a) Dominio publicador/suscriptor
En este modelo el cliente publicador direcciona los mensajes a un destino conocido para este
dominio como tema (topics), los cuales pueden ser recibidos por todos los suscriptores de ese
asunto. Los Topic siguen el modelo "publicador/subscriptor". Uno o varios consumidores se
"suscriben" al Topic, y van recibiendo los mensajes que se publican. Cuando se desconectan dejan
de recibir esos mensajes, y los pierden.
En JMS los clientes se suscriben a un tópico, el que se encarga de recibir los mensajes de los
publicadores y entregarlos a los subscriptores. En esta modalidad hay dependencia temporal
dado que un cliente suscrito puede consumir mensajes enviados solamente después de que se
suscribe. JMS da más flexibilidad y la confianza del método punto a punto vía las suscripciones
duraderas ("durable subscriptions") donde un subcriptor puede recibir mensajes aun estando
inactivo.
Objetos administrativos
Son objetos creados con herramientas de administración del proveedor JMS,y son el punto de enlace
de este con los clientes JMS.
Existen dos tipos de objetos administrativos:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
- ConnectionFactory: Son objetos que los clientes usan para establecer una conexión con el
proveedor JMS.
- Destination: Un destination es el objeto que un cliente usa para especificar el destino de los
mensajes producidos y la fuente de los mensajes a consumir, es decir, ¿a dónde envió y de
dónde recibió?
Estos dos objetos son dependientes y necesarios para establecer una comunicación entre el cliente y
el proveedor JMS, y son almacenados por el administrador en le JNDI (Java Naming and Directory
Interface) para que puedan ser accedidos de manera local o remota desde los clientes.
Conexiones
Las conexiones son objetos del API JMS cuya función es la de encapsular conexiones virtuales con el
proveedor JMS, y puede representar un socket abierto TCP/IP entre el proveedor y el cliente.
Este objeto es llamado dentro del API JMS como Connection y es usado por los clientes para crear
una o más sesiones.
Existen dos interfaces de conexión según el dominio que se vaya a implementar y son:
“QueueConnection” para un dominio punto a punto.
“TopicConnection” para un dominio publicador/suscriptor.
Sesiones
Las sesiones son entidades JMS que soportan la transaccionalidad y el consumo de mensajes
asincrónico, permitiendo crear productores de mensajes, consumidores de mensajes y mensajes.
Su implementación se la puede hacer por las interfaces TopicSession o QueueSession según el
dominio de mensajes utilizado por parte del cliente.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
- Asincrónica, por medio de la implementación de un escuchador de mensajes o más conocido
como MessageListener, el cual llama automáticamente a la ejecución del método onMessage
cada vez que un mensaje llega a un determinado destino, pudiendo ejecutarse de manera
paralela varias llamadas a este método y las acciones que este ejecuta.
Mensajes
El principal propósito de un sistema de mensajería es el producir y consumir mensajes que puedan
ser usados por otras aplicaciones de software, siendo entonces los mensajes el corazón del sistema.
La estructura de un mensaje en JMS es:
- Una cabecera,
- Propiedades o atributos (opcionales) y,
- Cuerpo del mensaje (opcional).
Cabecera
Parte esencial de un mensaje java que contiene una serie de campos predefinidos que permite a los
proveedores y clientes identificar y encaminar los mensajes. La siguiente tabla lista los campos que
forma parte de la cabecera:
Campo Tipo de Dato Descripción
JMSMessageID String Número que identifica unívocamente al mensaje.
Solo se puede consultar una vez que esta enviado el
mensaje.
JMSDestination Destination El destino a donde se envía el mensaje.
JMSDeliveryMode int Puede ser de tipo PERSISTENT, entonces se envía una
única vez, o de tipo NON_PERSISTEN, de manera que
se envía como mucho una vez, lo cual incluye también
que no sea enviado nunca.
PERSISTENT y NON_PERSISTENT están definidas como
constantes.
JMSTimestamp long Hora a la que se envió el mensaje.
JMSExpiration long Hora hasta la cual el mensaje es válido, si es 0 quiere
decir que no caduca nunca.
JMSPriority int Prioridad del mensaje de 0 a 9, siendo 0 la más baja.
JMSCorrelationID String Este campo se usa para relacionar una respuesta a un
mensaje, se copia aquí el id de mensaje del mensaje al
que se está respondiendo.
JMSReplyTo Destination Especifica el lugar a donde se deben enviar las
respuestas al mensaje actual.
JMSType String Este campo lo puede usar el programa de mensajería
para almacenar el tipo del mensaje.
JMSRedelivered boolean Indica que el mensaje ha sido enviado con anterioridad
pero el destino no lo ha procesado, por lo que se renvía.
Propiedades: son campos que permiten fijar características adicionales a las de la cabecera, son
propiedades personalizadas para un mensaje en particular.
Cuerpo del mensaje: es el mensaje en sí, los tipos de mensajes soportados por JMS son:
- StreamMessage: Contiene un stream de datos que se escriben y leen de manera
secuencial.
- MapMessage: Contiene pares nombre-valor.
- TextMessage: Contiene un String.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
- ObjectMessage: Contiene un objeto que implementa la interfaz Serializable, permitiendo
almacenar cualquier objeto de java.
- BytesMessage: Contiene un stream de bytes.
Por su naturaleza, los mensajes son asíncronos pues no hay dependencia temporal entre la
producción y el consumo de un mensaje. JMS ahonda esto y dice que los mensajes pueden ser
consumidos de forma:
- sincrónica: el cliente extrae explícitamente el mensaje vía el método "receive", que usa
timeout.
- asincrónica: el cliente registra un "message listener". Similar a un "event listener", el
mensaje es obtenido vía el método "onMessage".
Clientes JMS
Son las aplicaciones que consumen y producen los mensajes, para dichas acciones deben cumplir
una serie de requisitos en común antes de poder ejecutarlas, los mismos que son:
1. Obtener el objeto ConnectionFactory del JNDI.
2. Obtener un destino o fuente, mediante el objeto Destination a través de JNDI.
3. Establecer una conexión con el proveedor JMS a través del ConnectionFactory.
4. Usar Destination para establecer la sesión con el proveedor, para poder crear, enviar o recibir
los mensajes tras crear ya sea un productor o un consumidor de mensajes.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Unidad 9. Desarrollo de Beans Controlados
por Mensajes
Objetivo
Conocer y manejar los beans controlados o conducidos a través de mensajes.
En resumidas cuentas, los beans dirigidos por mensajes pueden escuchar mensajes de un
servicio de mensajes JMS. Los clientes de estos beans nunca los llaman directamente, sino que es
necesario enviar un mensaje JMS para comunicarse con ellos.
Asimismo, cuando se produce la invocación de un método de un MDB desde un cliente, la llamada
no bloquea el código del cliente y el mismo puede seguir con su ejecución, sin tener que esperar
indefinidamente por la respuesta del servidor. De este modo, el hilo de ejecución de un cliente no se
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
bloquea cuando está esperando que se complete algún método de negocio de otro Enterprise
JavaBean.
Tampoco los beans dirigidos por mensajes necesitan objetos EJBObject porque los clientes no se
comunican nunca con ellos directamente. Un ejemplo de bean dirigido por mensajes podría ser un
bean ListenerNuevoCliente que se activara cada vez que se envía un mensaje comunicando que se
ha dado de alta a un nuevo cliente.
No representan directamente datos compartidos de la base de datos, pero pueden acceder y
actualizar estos datos.
Podemos hacer la siguiente analogía: los MDBs son a JMS lo que JDBC es al lenguaje de consultas
SQL.
En EJB 3.0, la clase de un MDB puede ser un POJO, o bien, no necesita implementar javax.ejb.
MessageDrivenBean, usando la anotación @MessageDriven. Además, las anotaciones son
usadas para varias características, incluidas el destino y las factories (topic o queue). Se puede usar
inyección para adquirir un MessageDrivenEntityContext.
Para implementar un Message Driven Bean se debe:
1. Configurar el proveedor del servicio de mensajería.
2. Crear la clase MDB.
3. Configurar la información del proveedor (vía @ActivationConfigProperty).
4. Añadir un miembro de datos para el MessageDrivenContext. Se puede usar "resource
injection" para no usar métodos get/set.
5. Implementar la interfaz apropiada de message listener. Para un MDB JMS, hay que
implementar la interfaz javax.jms.MessageListener y así tendremos disponible el método
onMessage.
Opcionalmente, se puede:
- Implementar javax.ejb.TimedObject. Se puede usar MessageDrivenContext para configurar
un javax.ejb.
- TimerService si este paso se implementa.
- Definir métodos de callback de ciclo de vida con anotaciones apropiadas (@PostConstruct y
@PreDestroy).
- Completar la configuración del MDB.
El modelo básico de comunicación efectiva consiste de 5 elementos:
1. Emisor: MessageProducer
2. Receptor: MessageConsumer con MessageListener
3. Mensaje: Message
4. Canal: Connection, Session, Topic/Queue, etc.
5. Retorno: de acuerdo a lo programado en onMessage
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
@MessageDriven( mappedName = "jms/Queue" )
public class AsynchronousService implements MessageListener {
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Un primer elemento a considerar es la interface de negocio que ha de ser una interfaz message-
listener determinada por el tipo de mensajería en uso para el bean.
Por su parte la clase bean MDB será señalada con la anotación @MessageDriven que especifica la
cola de mensajes que el MDB monitorea.
Esta clase necesita implementar la interfaz MessageListener, que define sólo al método
onMessage().
En resumen, cada MDB debe implementar una interfaz message-listener apropiada para el tipo de
mensajería que éste soporta, o podría designar su interfaz message-listener usando la anotación
@MessageDriven. En consecuencia, para declarar un MDB solo hace falta establecer la anotación
@MessageDriven a una clase que implemente la interfaz MessageListener, e indicar el nombre de la
cola del contenedor que va a controlar el MDB.
Cuando un mensaje llega a la cola de mensajes del MDB el contenedor llama al método onMessage()
de esta clase bean y le pasa el mensaje recibido mediante un parámetro.
Por otro lado, para enviar un mensaje, hay que referenciar al MDB, el cliente usa el API estándar
JMS para obtener la cola de mensajes mediante su nombre JNDI (cola/mdb) y entonces él envía el
mensaje a la cola.
Los siguientes eventos de ciclo de vida son soportados por el MDB: PostConstruct y PreDestroy
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
estas instancias para permitir que los streams de mensajes sean procesados de forma
concurrente.
Un único bean dirigido por mensajes puede procesar mensajes de múltiples clientes.
Las variables de instancia de estos beans pueden contener algún estado referido al manejo de los
mensajes de los clientes. Por ejemplo, pueden contener una conexión JMS, una conexión de base de
datos o una referencia a un objeto enterprise bean.
Cuando llega un mensaje, el contenedor llama al método onMessage del bean. El método onMessage
suele realizar un casting del mensaje a uno de los cinco tipos de mensajes de JMS y manejarlo de
forma acorde con la lógica de negocio de la aplicación. El método onMessage puede llamar a
métodos auxiliares, o puede invocar a un bean de sesión o de entidad para procesar la información
del mensaje o para almacenarlo en una base de datos.
Un mensaje puede enviarse a un bean dirigido por mensajes dentro de un contexto de transacción,
por lo que todas las operaciones dentro del método onMessage son parten de un única transacción.
Los message‐driven beans en cierto modo se parecen a los stateles ssession beans:
- No retienen datos acerca de un cliente específico.
- Todas las instancias de un message‐driven bean son equivalentes, por lo que el contenedor
puede realizar un pool con estas instancias para permitir transmisiones de mensajes
concurrentes.
- Un único message‐driven bean puede procesar mensajes de diferentes clientes.
Un ejemplo completo
Ejemplo avanzado donde se puede ver cómo un Message Driven Bean recibe un mensaje que algún
cliente ha enviado a un Topic y a su vez, responde renviando una respuesta al Topic al que se le
indica que responda en el propio mensaje recibido.
Se puede ver también el uso de los métodos con anotaciones:
@PostConstruct - para crear la conexión, ejecutado después de la llamada al constructor
@PreDestroy - que indica que dicho método se ejecuta antes de retirar el Enterprise Bean del
contenedor
El ReplyBean queda de la siguiente forma:
import javax.ejb.MessageDriven;
import javax.ejb.MessageDrivenContext;
import javax.ejb.ActivationConfigProperty;
import javax.jms.ConnectionFactory;
import javax.jms.Topic;
import javax.jms.Connection;
import javax.jms.Session;
import javax.jms.MessageProducer;
import javax.jms.MessageListener;
import javax.jms.Message;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.JMSException;
import javax.annotation.Resource;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.logging.Logger;
Esta clase ReplyMsgBean es un bean dirigido por mensaje que implementa la interfaz
javax.jms.MessageListener. La clase a su vez está definida como pública, nunca como final o
abstracta.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
@MessageDriven(mappedName = "jms/Topic")
public class ReplyMsgBean implements MessageListener {
static final Logger logger = Logger.getLogger("ReplyMsgBean");
@Resource(mappedName = "jms/ConnectionFactory")
public ConnectionFactory connectionFactory;
@Resource
public MessageDrivenContext mdc;
Connection con = null;
public ReplyMsgBean() {
}
@PostConstruct
public void makeConnection() {
try {
con = connectionFactory.createConnection();
} catch (Throwable t) {
// JMSException could be thrown
logger.severe(
"ReplyMsgBean.makeConnection:" + "Exception: "
+ t.toString());
}
}
El método onMessage se declara público, no final o static, marcado con un tipo void, es decir, sin
valor de retorno y con un argumento de tipo javax.jms.Message. Así, se encargará de ofrecer los
contenidos del mensaje y crear una conexión, sesión y producir la réplica, usando como destino el
campo JMSReplyTo para el envío del mensaje. De este modo, el método crea y envía un mensaje,
asignando el encabezado JMSCorrelationID del campo al ID del mensaje enviado y la propiedad ID.
Después se cerrará la conexión.
public void onMessage(Message inMessage) {
TextMessage msg = null;
Session ses = null;
MessageProducer producer = null;
TextMessage replyMsg = null;
try {
if (inMessage instanceof TextMessage) {
msg = (TextMessage) inMessage;
logger.info("ReplyMsgBean: Received message: " + msg.getText());
con = connectionFactory.createConnection();
ses = con.createSession(true, 0);
if (con != null) {
try {
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Enunciado
Vamos a realizar una aplicación que enviará mensajes al servidor utilizando la tecnología JMS.
Lo primero de todo será irnos a la pestaña Services dentro del entorno gráfico de NetBeans.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Una vez iniciado el servidor, debemos irnos a la consola de administración del contenedor para
poder visualizar las características propias de JMS. Para ello, seleccionamos la opción View Admin
Console.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Pulsamos sobre Nuevo para poder crearnos un nuevo conector sobre JMS.
Lo llamaremos jms/QueueFactory y debe estar activado.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Llamaremos al JNDI jms/Queue y el nombre de nuestro destino será mi destino.
El tipo de recurso será javax.jms.Queue y debe estar activado.
Ahora vamos a probarlo, para ello, nos creamos un nuevo proyecto Enterprise Application.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Llamaremos a la aplicación MiAplicacionMDB y crearemos los proyectos WAR Y EJB.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Sobre el proyecto WAR nos agregamos un nuevo Servlet.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Ahora escribimos la llamada a los recursos del servidor en el servlet escribiendo el siguiente código
dentro de la clase:
@Resource(mappedName="jms/QueueFactory")
javax.jms.QueueConnectionFactory queueConnection;
@Resource(mappedName="jms/Queue")
javax.jms.Queue queue;
Añadimos los import necesarios mediante el entorno NetBeans.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
message.setString("apellido", apellido);
message.setString("texto", texto);
producer.send(message);
producer.close();
session.close();
connection.close();
out.println("<h1>Servlet Generador Mensajes</h1>");
out.println("El mensaje se ha enviado correctamente");
}catch (Exception ex)
{
out.println("<h1>Excepcion al enviar el mensaje</h1>");
out.println(ex.toString());
} finally {
out.println("</body>");
out.println("</html>");
out.close();
}
}
Una vez que hemos configurado el servlet para enviar mensajes, es el momento de crearnos un
objeto de la clase Message Driven Bean. Sobre el proyecto EJB agregamos un nuevo objeto
Message-Driven Bean.
Le indicaremos la opción Server Destinations nuestro recurso jms/Queue creado al principio del
laboratorio. Lo llamaremos BeanMDB.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
package paquetemdb;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageListener;
@MessageDriven(mappedName = "jms/Queue", activationConfig = {
@ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-
acknowledge"),
@ActivationConfigProperty(propertyName = "destinationType", propertyValue =
"javax.jms.Queue")
})
public class BeanMDB implements MessageListener {
public BeanMDB() {
}
@Override
public void onMessage(Message message) {
System.out.println("Mensajes recibidos en el MDB...");
try {
MapMessage msg = (MapMessage) message;
System.out.println("Nombre:" + msg.getString("nombre"));
System.out.println("Apellido:" + msg.getString("apellido"));
System.out.println("Texto:" + msg.getString("texto"));
} catch (Exception ex) {
System.out.println(ex.toString());
}
}
}
Ya tenemos todo configurado para enviar y consumir mensajes, nos quedaría una parte gráfica para
enviar los mensajes. Para ello, utilizaremos la página index.jsp del proyecto WAR.
Implementamos su código:
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
</head>
<body>
<h1>Enviar mensajes a la cola Queue JMS</h1>
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
<form name="form1" action="GeneradorMensajes">
<table border="1">
<tr>
<th>Nombre</th>
<td>
<input type="text" name="txtnombre">
</td>
</tr>
<tr>
<th>Apellidos</th>
<td>
<input type="text" name="txtapellidos">
</td>
</tr>
<tr>
<th>Texto del mensaje</th>
<td>
<textarea name="txttexto" rows="4" cols="20">
</textarea>
</td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="Enviar mensaje a la cola">
</td>
</tr>
</table>
</form>
</body>
</html>
Solo nos quedaría probar el proyecto.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Actividades
“Recuerde que para un seguimiento óptimo de la unidad es imprescindible realizar las actividades
que encontrará en la unidad correspondiente de la plataforma eLearning”.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Unidad 10. Interceptores
Objetivo
Conocer y manejar los interceptores en EJB.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Tanto en el caso del Entity Listener como del Interceptor, la clase a vigilar es la que debe determinar
quien se encargará de controlarla.
Por ejemplo, mediante el Deployment Descriptor podemos definir un procedimiento común para
todos de tal manera que todos los session bean y message-driven beans sean auditados por un
interceptor determinado, mientras que los entity beans lo sean a su vez por un único entity listener.
No obstante, esta idea de reducir el procedimiento a un único y común controlador no resulta la
manera más óptima de proceder. Hay que tener en cuenta que no hay manera de activar o
desactivar sobre la marcha los controladores. Es decir, una vez estén definidos en el Deployment
Descriptor, dichos interceptores y entities listener serán llamados siempre, lo que supondrá una
sobrecarga del sistema que en muchos casos puede ser inasumible por innecesario.
En resumen:
- Los métodos interceptores pueden estar en la misma clase a interceptar o en otra aparte.
- Un interceptor se puede aplicar a un sólo método o a todos los de una clase
- Se pueden aplicar varios interceptores a un mismo bean (o método), la llamada pasa a
través de todos ellos.
- Actúan como filtros
}
Un interceptor de devolución de llamadas de ciclo de vida se aplica a invocaciones de devolución de
llamadas de ciclo de vida por el contenedor:
}
Una clase de interceptor puede interceptar métodos de devolución de llamadas de ciclo de vida y
métodos de negocios.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Los consejos a seguir para el uso apropiado de los mismos son:
La clase que implementa el Interceptor debe ser lo más liviana posible, debido a que los
interceptores pueden ser Pasivados por el contenedor, esta operación es considerada muy
costosa, por lo tanto se tiene que tratar de disminuir al mínimo la serialización a disco.
Utilizar interceptores a nivel de método (method-level Interceptors) y no a nivel de bean
(bean-level Interceptors). Esto permite que los interceptores se llamen únicamente cuando
sea necesario.
Utilizar la menor cantidad de interceptores.
Interceptor
Veamos un fragmento de código completo para hacernos una primera idea de las características de
implementación de un interceptor:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
El modo más sencillo de definir un interceptor es dotar al método correspondiente con la anotación
@AroundInvoke.
El método puede nombrarse de cualquier manera y dar como resultado una instancia de tipo object,
a la vez que esperar como único parámetro una instancia de la interface InvocationContext:
public interface InvocationContext
{
public Object getBean();
public Method getMethod();
public Object[] getParameters();
public void setParameters(Object[] params);
public java.util.Map<String, Object>
getContextData();
public Object proceed() throws Exception;
}
A través de estos parámetros el método de escucha se consigue acceder al objeto:
De ese objeto podemos acceder con los método ilustrados en el cuadro anterior a los métodos
objetivo en concreto -getMethode()- , a sus parámetros - getParameters()-, a modificar dichos
parámetros –setParameters()- incluso antes de que se produzca la llamada al método en sí –
proceed().
Es importante subrayar que un interceptor se activa en el momento en el que se produce la llamada
a un método. Así, proceed() causa la invocación del próximo método. Retorna el resultado de esa
invocación, pero si el método de negocio retorna void entonces proceed retorna null.
Por su parte, con el método getContextData() se pude otorgar al método interceptor una instancia
de la clase Map. A través de esta clase diferentes clases interceptoras pueden intercambiar
información, siempre que todas ellas escuchen o vigilen las mismas llamadas a método. Veamos un
ejemplo:
import javax.interceptor.*;
Deployment Descriptor
Además del establecer el interceptor por el mecanismo ya visto de las anotaciones, también hemos
comentado que existe la posibilidad de definirlo dentro del Deployment Descriptor de nuestra
aplicación. En este caso estaríamos creando una clase interceptor applicable a todos los beans, esto
conviene tenerlo bien presente si es que optamos por esta opción.
En primer lugar, deberemos describir la clase interceptor, para ello disponemos de la etiqueta
dentro del archive ejb-jar.xml. Dicha etiqueta consta a su vez de otras etiquetas
encargadas de reflejar el nombre de la clase interceptora, así como todos los métodos
de la misma. Veamos un fragment de código de ejemplo:
<interceptors>
<interceptor>
<interceptor-class>server.CalcularRespuesta</interceptor-class>
<around-invoke>
<method-name>ObtenerTiempo</method-name>
</around-invoke>
</interceptor>
</interceptors>
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Como vemos hay una clase denominada CalcularRespuesta que cuenta con un método
ObtenerTiempo.
A continuación lo que corresponde hacer es conectar el bean con la clase del interceptor. En este
caso podremos mediante la etiqueta define el método que deberá activarse ante
el interceptor.
<interceptor-binding>
<ejb-name>AdministracionRespuestaEmpleadoBean</ejb-name>
<interceptor-class>server.CalcularTiempo<interceptor-class>
<method>
<method-name>InsertarEmpleado</method-name>
<method-params>
<method-param>java.util.vector</method-param>
</method-params>
</method>
</interceptor-binding>
En el fragmento anterior estamos configurando dos cuestiones:
- La supervisión de un método (InsertarEmpleado) mediante un interceptor (CalcularTiempo)
- El establecimiento de todos los métodos de la clase como supervisables.
No obstante, podemos definir un interceptor genérico para todos nuestros beans incluyendo un
asterisco dentro de la etiqueta
<interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>server.CalcularTiempo<interceptor-class>
</interceptor-binding>
Mediante la anotación @ExcludeDefaultInterceptors o la etiqueta
podemos forzar la supervisión por un interceptor de una clase
determinada. Por ejemplo en el nivel de método podría hacerse así:
@Stateful
@ExcludeDefaultInterceptors
@Interceptos(CalcularTiempo.class)
Public class TiempoAdministracionEmpleadoBean
Implements TiempoAdministracionEmpleadoRemoto {
//código
}
Mientras que para activar un interceptor común a todos los métodos de la clase a través del
Deployment Descriptor sería así:
<interceptor-binding>
<ejb-name>AdministracionEmpleadoBean</ejb-name>
<interceptor-class>server.CalcularTiempo<interceptor-class>
<exclude-default-interceptors>true<exclude-default-interceptors>
</interceptor-binding>
Si lo que queremos es que un interceptor definido en una clase no afecte a un método determinado
de la misma, entonces tenemos la anotación @ExcludeClassInterceptors. Por ejemplo:
@Stateful
@Interceptos(CalcularTiempo.class)
public class TiempoAdministracionEmpleadoBean
Implements TiempoAdministracionEmpleadoRemoto {
//código
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
@ExcludeClassInterceptors
Public vector<Grupodatos> getEstructura(){
Return getEstructura(null,null,null,true);
}
}
En el código anterior el método getEstructura queda excluido de la supervisión realizada por el
interceptor CalcularTiempo.
Cabe la posibilidad también de combinar las dos anotaciones anteriores -@ExcludeClassInterceptors
y @ExcludeDefaultInterceptors -, de este modo, haremos que un método no pueda ser supervisado
por ninguna otra clase.
Entity Listener
Como hemos dicho anteriormente, un interceptor no puede utilizarse en un entity bean. En este
caso, la supervisión ha de realizarse sobre los métodos del ciclo de vida propios del bean de entidad.
Serían los siguientes:
En este sentido un entity listener sería una clase de java normal, sólo que portadora de uno o varios
métodos definidos con las anotaciones propias del ciclo de vida que acabamos de mostrar en la
imagen anterior. Por ejemplo:
En la imagen anterior vemos dos métodos marcados con las anotaciones @PrePersist y @PostLoad.
Para que un listener de esta clase esté activo deberán cumplirse algunas condiciones:
- Un entity bean que acepte dicha clase como listener
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
- bien, una definición del entity listener en el Deployment Descriptor que será aplicado, eso sí,
a todos los entity bean de la aplicación.
Del primer caso tendríamos una implementación del siguiente estilo:
Ahí tendríamos la clase implementada como EntityListener y nos queda su conexión en la clase con
las anotaciones del ciclo de vida:
La variable acct apunta a la clase Account, ligado todo a su vez al evento @PostPersist en el método
newAccountAlert encargado de enviar un mensaje con algunos datos informativos del proceso.
En el caso de que el entity bean y el listener tuvieran los mismos métodos del ciclo de vida, primero
sería llamado el método del listener y después el del entity.
Por su parte, en el Deployment Descriptor podremos definir el entity listener:
- bien en la etiqueta para un entity bean determinado.
- bien en la asignado por defecto a todos los entity bean.
<entity-listeners>
<entity-listener class=”server.RegistrarActividad”>
<post-persist method-name=”postPersist”/>
<post-remove method-name=”postRemove”/>
<post-update method-name=”postUpdate”/>
<post-load method-name=”postLoad”/>
<entity-listener/>
<entity-listeners/>
</entity>
En la etiquetas post figuran los diferentes eventos del ciclo de vida y sus correspondientes métodos
del listener. Si lo que queremos es el mismo entity listener para todos los entity beans entonces
debemos eliminar la referencia a una clase concreta, en nuestro caso RegistrarActividad, y colocar
la etiqueta
<persistence-unit-metadata>
<persistence-unit-defaults>
<entity-listeners>
<entity-listener class=”server.RegistrarActividad”>
<post-persist method-name=”postPersist”/>
<post-remove method-name=”postRemove”/>
<post-update method-name=”postUpdate”/>
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
<post-load method-name=”postLoad”/>
<entity-listener/>
<entity-listeners/>
<persistence-unit-metadata/>
<persistence-unit-defaults/>
Actividades
“Recuerde que para un seguimiento óptimo de la unidad es imprescindible realizar las actividades
que encontrará en la unidad correspondiente de la plataforma eLearning”.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Unidad 11. Transacciones
Objetivo
Conocer y manejar las transacciones en EJB.
Introducción
En la actualidad las aplicaciones emplean en alguna ocasión transacciones. Este empleo tan
generalizado resulta lógico, puesto que con él aseguramos que una función compleja del sistema se
ejecuta o no con éxito.
A menudo debemos realizar varias operaciones de modificación de base de datos de una manera
conjunta. Por ejemplo pensemos en la transferencia de dinero entre dos cuentas corrientes. Esta
acción tan simple supone realizar dos UPDATE, cada una en una cuenta corriente de un usuario.
Pues bien, si una de la las dos falla o lo que es lo mismo, sólo una se produce correctamente
mientras que la otra no se termina de llevar a cabo, entonces habrá un problema. O bien, una
cuenta recibe un dinero que no figura como extraído de ninguna otra cuenta, o bien, se realiza un
desembolso desde una cuenta, pero dicha cantidad no se transfiera a ninguna parte. Por lo tanto,
necesitamos un sistema que garantice que todas las operaciones involucradas tienen éxito, o en el
caso de que una de las dos falle, la otra no llegue a ejecutarse o en el caso de que ya lo haya hecho,
se deshaga.
Así, los datos en la base de datos solo se modificarían cuando se pudiera realizar dicha tarea por
completo y sin errores. Si mientras tanto se produjera un error, la transacción se ocuparía de que
todas las modificaciones realizadas hasta el momento en la base de datos se cancelaran.
Imaginemos que no hubiera todavía ninguna protección de transacción, entonces tendríamos que
volver a establecer los datos modificados en caso de una cancelación, mediante la última copia de
seguridad y este escenario resultaría inasumible en una aplicación online.
Conviene subrayar que cuando hablamos de una protección de transacción estamos aludiendo
siempre al contenido de la base de datos, no al estado de las clases Java en la memoria principal.
Una vez se ha llamado un método en un session bean, se inicia esta transacción. Memoriza
prácticamente todo lo que el EntityManager modifica en la base de datos durante el proceso.
Siempre que se coloque un UPDATE O INSERT se ejecutará esto en la base de datos.
Durante la modificación transaccional de los datos, los cambios en las filas no serán advertidos por
los usuarios y permanecerán invisibles. Esto es así, porque la transacción bloquea los registros
mientras se ejecuta y pone a la cola en espera al resto de operaciones transaccionales que se
quieran realizar sobre los mismos hasta que no se termine. No obstante, también se puede
configurar una base de datos de manera que las transacciones lectoras reciban los datos aún no
liberados de otra transacción. Pero, entonces no se puede asegurar que los datos al final se
introduzcan exactamente así en las tablas.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Una vez ha finalizado el método invocado, finaliza normalmente también la transacción. Si no se
produce ningún error, se llega a lo que suele denominarse COMMIT, es decir, una confirmación de
todos los cambios producidos y una liberación de los bloqueos sobre las filas. En caso de error se
produce entonces el denominado ROLLBACK, lo que supone que la base de datos anulará todas las
modificaciones realizadas hasta el momento del fallo. Obviamente, según el número de cambios
realizados durante la transacción, estas operaciones necesitarán más o menos tiempo.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Las transacciones iniciadas por el cliente tienen como ventaja que un cliente es consciente de si la
transacción ha sido deshecha o comprometida, pero tienen el inconveniente de que implican
importantes problemas de rendimiento.
@TransactionAttribute
Cada método público de un session bean o message-driven bean puede dotarse de la anotación o
TransactionAttribute. De este modo el desarrollador configura cómo desea que el servidor de
aplicaciones controle el proceso de la transacción.
En este tipo enumerado tenemos los diferents valores que puede soportar dicho atributo de
transacción:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
a nivel de clase y esta queda extendida a la vez que todos los métodos que la integren funcionarán
de manera transaccional.
@stateful
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class CounterTestBean implements CounterTestRemote {
Sin embargo, lo más habitual será aplicar la anotación transaccional en el nivel de los métodos.
Conviene remarcar que estos atributos transaccionales solo son efectivos cuando el método
correspondiente es llamado a través del servidor de aplicaciones. Es decir, cuando un cliente, desde
dónde se encuentre, haya realizado un lookup en la interfaz local o remota de la clase bean y se
invoque a continuación un método. Si por el contrario nos encontramos ya en un session bean que
llama a su propio método, el servidor de aplicaciones no tendrá ninguna noticia al respecto. Se
tratará en este caso de una llamada de método normal, no transaccional. El atributo de la
transacción del método así será ignorada. Veamos un ejemplo de este escenario.
@Stateful
public class PRUEBABean implements PRUEBABEANRemoto {
@TransactionAttribute(TransactionAttributeType.REQUiRES_NEW)_
public void primerMetodo {
//Si un cliente llama este método, se inicia una nueva transacción segundoMetodo();
}
@TransactionAttribute(TransactionAttributeType.REQUiRES_NEW) public void segundoMetodoO {
// Si este método es llamado por primerMetodo
// no se crea ninguna transacción nueva, se utilizará
// la transacción del primer método.
// si es el Cliente el que llama este método, se iniciará
// una nueva transacción
}
Aunque en ambos métodos parecen necesitar una nueva transacción, sólo al llamar el primer
método se inicia la transacción, aunque durante su ejecución se utilice también el segundo método.
Los atributos de la transacción actúan por lo tanto siempre en una llamada a través del cliente, no
cuando un método sea llamado internamente por otro método. Será siempre el cliente el que active
la transacción personalizada. Como vemos en la siguiente ilustración que el “método b” actúe de
manera transaccional o no, dependerá de cómo se haya procedido a realizar su llamada.
Siempre nos queda la opción de declarar los atributos de la transacción dentro del Deployment
Descriptor. La etiqueta assembly-descriptor del archivo ejb-jar.xml consta de varias entradas de
container-transaction , que asignan un atributo de transacción a uno o más métodos. No hay problema
en que la etiqueta method aparezca tantas veces en una container-transaction como fuera necesaria. Por
su parte, deberá constar como mínimo de las entradas ejb-name y method-name , con lo cual
determinaremos, para qué método y desde qué bean se tiene que realizar aquí una entrada. Como
puede darse el caso de que en un mismo bean existan varios métodos con el mismo nombre y que
éstos solo diferencien entre sí por sus parámetros, la etiqueta method puede complementarse
también con la entrada method-param . Aquí se definirán cada uno de los parámetros
correspondientes al método en cuestión . Después de establecer uno o más métodos asignaremos
finalmente el auténtico atributo de transacción mediante la etiqueta trans-attribute .
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
NOT_SUPPORTED
Cuando un cliente invoca a un método con este atributo, se suspende la transacción que estuviera
puesta en marcha. Es decir, a partir de ese momento todas las operaciones de actualización no
serán realizadas de manera transaccional. Pero una vez finalice la ejecución del método, entonces
volverá a activarse cualquier posible transacción suspendida previamente.
Un escenario en el que resulta conveniente emplear este atributo sería en aquellos casos en los
cuales nos planteamos hacer un acceso a datos de sólo lectura. En estas circunstancias
optimizaríamos el rendimiento de nuestra aplicación, dado que una transacción siempre consume
más recursos que la ausencia de la misma.
Si queremos incluir este atributo de la transacción en el Deployment Descriptor le pondremos el
nombre de NotSupported.
SUPPORTS
Este atributo traslada al método que lo tenga asignado una transacción que ya estuviera en marcha
al ser llamado dicho método desde otro que la incluyera. Si, por el contrario, el método que invoca
al método con SUPPORTS no fuera transaccional, entonces, el método invocado no habilitaría la
transacción.
Por ejemplo, imaginemos que tenemos un cliente que llama a una página JSP a través de un método
de negocio con un bean de sesión que incorpora un atributo Required. Esto pondría en marcha una
transacción. Si este método a su vez invoca a otro bean de sesión y llama también a un método que
tenga el atributo supports, entonces este método operará también junto al otro de una manera
transaccional. Por consiguiente, como existe protección para la transacción y esta figura soportada
por los dos métodos involucrados, todos los cambios de la base de datos serán realizados bajo dicha
transacción. Si por el contrario la página JSP llama directamente a un método caracterizado con su-
pports y no hay ningún otro método involucrado en dicha llamada que opere de manera
transaccional, entonces, no existirá allí ninguna protección de transacción sobre las acciones que se
lleven a cabo.
Dentro del Deployment Descriptor, el atributo de transacción lleva el nombre supports.
REQUIRED
Sin duda, este es el atributo de transacción utilizado con mayor frecuencia. Establece en el método
una protección de transacción. Hasta tal punto es así que si durante la llamada aún no existe
ninguna transacción en marcha, se iniciará una. En cambio, Si ya existe alguna en ejecución,
entonces, se seguirá empleando la existente.
El atributo de la transacción lleva el nombre Requi red en el Deployment Descriptor.
REQUIRES NEW
Si lo que buscamos es realizar unas operaciones dentro de una transacción autónoma entonces
deberemos asignar a nuestro método el atributo.
RequiresNew. Con ello se podrán dar varias situaciones:
- Si no hay ninguna transacción en marcha en el momento en que llamemos al método,
entonces se iniciará una nueva.
- Si ya hay una transacción en marcha en el momento en el cual llamamos al método,
entonces se suspenderá dicha transacción y se creará una nueva. Una vez el método provisto
con RequiresNew finalice su labor, se detendrá también la transacción iniciada de nuevo y se
recuperará la ejecución de la transacción suspendida anteriormente. En este punto, carece de
importancia para la transacción externa cómo finaliza la interna. Incluso si una transacción
finaliza con un ROLLBACK, puede llegar la otra a un COMMIT y viceversa.
El atributo de transacción se llama RequiresNew en el Deployment Descriptor.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
MANDATORY
Con Mandatory determinamos que el método llamado sea siempre parte de una transacción
existente. Por lo tanto, resultará obligatoria la existencia de una transacción en ejecución, siempre
que uno de estos métodos sea invocado por un cliente. Si por el contrario no existiera ninguna
protección de transacción, se interrumpiría la llamada al método y se generaría un error del tipo
javax.ejb.E3B- TransactionRequiredException.
El atributo de transacción lleva el nombre Mandatory en el Deployment Descriptor.
NEVER
Never es un atributo de transacción bastante peculiar. Si lo llamamos desde un método con protección
de transacción, se producirá una EUBException. Solo si no existe ninguna protección se puede trabajar
el método como es debido. Tampoco se inicia ninguna nueva transacción para el método. ¿Cuál es
entonces su labor? Pues que todos lo cambios en la base de datos, que se ejecuten en un método de
este tipo se escriban de inmediato en las tablas de modo visible y permanezcan allí
independientemente de lo que ocurra en el método.
Tomando en cuenta lo anterior, podemos encontrar en nuestras aplicaciones un escenario como el
reflejada en el siguiente diagrama:
Ante estos dos casos en los que se llama a métodos, el comportamiento de los atributos
especificados en el CMT sería el siguiente para cada una de las situaciones reflejadas en el diagrama
anterior:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Bean Managed Transaction
Como dijimos anteriormente, también cabe la opción de controlar personalizadamente el
comportamiento de la transacción. Para ello podemos asignar un session bean con la anotación
@TransactionManagement y así establecer que será el bean quien se haga cargo del control. Un
ejemplo de esta implementación lo tenemos en el siguiente fragmento de código:
public enum Transact!onManagerType {
BEAN,
CONTAINER
}
@Target({type}) @Retention(runtime)
public @interface TransactionManagement {
TransactioriManagerType value() default TransactionManagerType.container;
}
Si en ese momento llamamos a un método de este vean, el Container ya no se preocupará de la
protección de la transacción. Aquella que estuviera en marcha porque el cliente la hubiera iniciado
ya, también seguirá utilizándose. Por el contrario, si lo que se pretende es que el método llamado
inicie su propia transacción, entonces deberá poseer una instancia de la clase userTransaction. Para ello
lo más cómo es hacerlo mediante el método getuserTransaction() de la clase EJBContext. Veamos unas líneas de
código que lo implementan:
@Stateless
@TransactionManagement(TransactionManagetnentType.BEAN)
public class CounterPRUEBABEAN implements CounterPRUEBABEANRemote {
@Resource
SessionCóntext ejbcontext;
public void Metodo() {
boolean error Aparecido = false;
userTransaction ut = ejbcontext,getuserTransaction();
try {
ut.begin();
if(errorAparecido)
ut.rollback(); else
ut.commit();
} catch (IllegalStateException e) {
} catch (SecurityException e) {
} catch (NotsupportedException e) {
} catch (SystemException e) {
} catch (RollbackException e) {
} catch (HeuristicMixedException e) {
} catch (HeuristicRollbackException e) { }
}
}
Estas líneas de código permiten ver cómo un stateless session bean se gestiona él mismo la
protección de su transacción. En este caso la transacción debe finalizar en el mismo método en el
que se ha creado. Esta restricción no es válida para stateful session beans.
No obstante, En vez de emplear el EJBContext, se puede inyectar una user-Transaction directamente
en un bean.
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class CounterTestBean implements CounterTestRemote { @Resource UserTransaction ut;
}
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Finalmente, otra opción para llegar a un UserTransaction consiste en colocar en establecer
"java:comp/env/userTransaction". De este modo un cliente también puede participar en
administración de la transacción desde fuera de un servidor de aplicaciones. Con ello conseguiremos
iniciar una transacción en el cliente, llamar uno o más métodos de los session beans y después
finalizar la transacción en el mismo cliente.
UserTransaction ut;
try {
initialContext ctx = new initialContextO;
ut = (UserTransaction)
ctx.lookup("java:comp/env/UserTransaction");
} catch (NamingException e) { }
Los métodos más importantes de la clase UserTransaction son begin() para iniciar una transacción,
commit() para finalizarla limpiamente y rollback() para anular todos los cambios. Además también
existe el método setRollbackonly(), que no finaliza aún la transacción pero ya indica que debe terminar
en todo caso con un rollback.
Con setTransactionTimeout() podemos establecer el número de segundos tras los cuales concluirá la
transacción. Si no se utiliza este método prevalecerán los valores estándar del gestor de la
transacción, lo cual pueden configurarse mediante el servidor de aplicaciones.
Por último, el método getstatus() nos da información acerca del estado actual de la transacción.
Este método devuelve constantes desde la interfaz javax.transaction.status cuyos valores son más
interesantes son STATUS_ACTIVE,STATUS_COMMITED o STATUS_MARKED_ROLLBACK.
También un message-driven bean puede asumir él mismo el control de la transacción. Todo lo que
se ha mostrado aquí se puede aplicar sin cambios al método onMessage() de este tipo de bean.
Como en los stateless session beans la transacción debe terminar en el mismo método en el que se
ha iniciado. No es posible recoger varias llamadas del método onMessage() en una transacción.
No es necesario que para cada método exista protección de transacción. Un stateless session bean
para la validación de un número de tarjeta de crédito o el número de un billete no necesita ninguna
transacción. Si un bean consta tan solo de estos métodos, se puede describir toda la clase con
NOT_SUPPORTED. Esto no significa obligatoriamente que los métodos no realicen ningún tipo de acceso a
la base de datos. Siempre y cuando estos sean tan solo de lectura por ejemplo para comprobar la
existencia de un número de cuenta, también funcionará correctamente sin protección de la tran-
sacción. En todo caso es mucho más grave olvidar la protección de transacción en un método que la
necesita que dársela a un método que no va a sacarle ninguna utilidad. Caracterizar todos los
métodos con NOT_SUPPORTED para darles después por separado un REQUIRED es peor que preocuparse
primero de que todos los métodos tengan protección de transacción y después cambiar algunos de
ellos.
En el siguiente fragmento de código destinado a actualizar el stock de un almacén, podemos ver un
ejemplo del empleo de este tipo de transacción personalizada con la invocación a los métodos:
.begin();
.commit();
.rollback();
Lo vemos dentro de un bloque try/catch en prevención de posibles excepciones y la transacción se
declara mediante @Resource en javax.Transaction.UserTransaction ut:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Ver Video: Creación de una transacción en CMT Contenedor de
Transacciones, en la Unidad 11, en el Módulo 6,
en la plataforma elearning
Actividades
“Recuerde que para un seguimiento óptimo de la unidad es imprescindible realizar las actividades
que encontrará en la unidad correspondiente de la plataforma eLearning”.
Objetivo
Conocer y manejar las excepciones en EJB.
Introducción
Suele decirse que en Java hay dos tipos de excepciones. Aquellas motivadas por la ejecución del
Runtime y aquellas otras producidas al realizar un chequeo de información, mensajes o datos. Las
chequeadas heredan de Exception e indican errores lógicos en la aplicación. Las no chequeadas, por
su parte heredan de RuntimeException e indican errores de programación o no recuperables
Pues bien para los EJBs se suele hacer otra distinción:
a) Excepciones de aplicación: Relacionadas con la lógica de negocio. Así, estas son excepciones
para reportar problemas en la ejecución de la lógica de negocio. Por ejemplo:
public void withdraw(float amount) throws OverdraftException
En este tipo de excepciones resulta una tarea del cliente recuperar o reportar estas excepciones.
Teniendo en cuenta que “El cliente" aquí no significa la aplicación cliente sino cualquiera que haya
realizado la invocación del método del EJB que lanza la excepción.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Por otro lado, las excepciones de aplicación tienen que ser subclases de Exception, pero no
subclases de RuntimeException.
Si se lanza una excepción de aplicación, y no se captura en el servidor, se manda al cliente. Es algo
similar a lo que ocurre con RMI “regular”, el cliente sencillamente invoca el método y obtiene una
excepción como resultado (de forma transparente).
Ejemplos de excepciones de aplicación son:
- CreateException: se produce cuando no puede crear una instancia
- FinderException: cuando dada la clave de una instancia no la puede localizar.
- y RemoveException.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
En la siguiente ilustración podemos ver esquemáticamente diferentes tipos de errores que pueden
acontecer en un EJB:
Ahora bien, ¿Cuál es la ruta de retorno de las excepciones una vez ha saltado el error? Depende. En
el siguiente diagrama de secuencia podemos hacernos una idea de su recorrido:
De este modo está claro que el manejo de las excepciones se puede hacer a diferentes niveles
dentro del EJB.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Si estamos ante una excepción de aplicación gestionada por el contenedor entonces caben dos
posibilidades.
- Que hayan sido generadas por un método de negocio
- Que haya sido generadas en el contenedor.
En este último caso la excepción pasaría al cliente local o remoto. Lo que implica que la excepción
no se anularía automáticamente salvo que empleemos el método setRollbackOnly.
Si estamos ante una excepción del sistema y queremos manejarlas mediante el contenedor entonces
pueden acontecer cualquiera de las exceciones enumeradas en la siguiente lista de excepciones:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Manejo de excepciones por el cliente
Si lo que queremos es manejar la excepción en el cliente. El proceso de error acaece de esta
manera:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Excepciones en transacción
Las excepciones de anulación de una transacción trascurren del siguiente modo:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Cuando se trata de una excepción de una transacción requerida:
Actividades
“Recuerde que para un seguimiento óptimo de la unidad es imprescindible realizar las actividades
que encontrará en la unidad correspondiente de la plataforma eLearning”.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Unidad 13. Temporizador
Objetivo
Conocer y manejar los temporizadores en EJB.
Introducción
El temporizador se suele utilizar cuando deseamos realizar un proceso controlado temporalmente,
ya sea en una fecha determinada o cada cierto intervalo de tiempo. Básicamente, consiste en tener
la posibilidad de llamar periódicamente a un método, bien haciéndolo en una única ocasión o bien
cada cierto tiempo. Esto puede hacerse sin tener ningún tipo de relación con el cliente, puesto que el
primer impulse para activar el temporizador será realizado a través de una llamada explícita al
método en la capa de negocio. Por lo tanto, será el propio servidor de aplicaciones el que traslada
en ese momento el control de la aplicación guiado por el temporizador y el beans.
Para implementar el temporizador se suele utilizar un Stateless Session Bean o un Message-driven
Bean. Conviene subrayar que en la versión EJB 3.0 los Entities no están disponibles para el Timer
Service. Tampoco los beans de sesión de tipo Stateful pueden ser adaptados al uso de
temporizadores. De hacerlo se produciría un error de la clase IllegalStateException.
Los usos de los temporizadores son abundantes, puesto que permiten auditar la actividad del
recurso en funcionamiento de una manera periódica y registrar esa información en archivos log,
hacer mantenimientos de recursos cada cierta fecha u hora o enviar mensajes de aviso a quien
corresponda si no se han obtenido los resultados esperados.
Conviene, no obstante tener presente que un temporizador puede no comenzar a hacer su tarea con
toda la puntualidad que deseáramos. Una actividad desmesurada del servidor podría retrasar o
ralentizar el arranque del temporizador o que después fuera este llamado en varias ocasiones
sucesivas.
También cabe la posibilidad de que un mismo bean pueda iniciar varios Timers a la vez,
ejecutándose de manera autónoma pero simultanea. Todos llamarían al mismo método pero sería
posible saber qué temporizador llamó en cada ocasión, identificando así por separado su servicio. Es
más aún, los temporizadores son objetos persistentes que deberían superar incluso una caída del
servidor de tal manera que una vez se reincide el servidor, este deberá atender a todos los timer
ejecutados.
El método de procesamiento de eventos en el Timer es el siguiente:
El timer llama al método indicado del bean que lo creó y tiene dos formas de indicar a qué método
se debe invocar:
- Implementando la interfaz TimedObject
- Usando la anotación @Timeout
TimedObject y @Timeout
Para utilizar un temporizador en un stateless sesión bean o un message driven bean debemos
implementar la interfaz javax.ejb.TimedObject, dicha interfaz cuenta con el método ejbTimeout(), al
que se transmite una instancia del Timer.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
También podemos emplear la opción de la anotación @Timeout que cuenta con el método
@javax.ejb.Timeout.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Interfaz TimerService
La ventaja de utilizar como temporizador un stateless session bean reside en que se pueden ofrecer
métodos al cliente a través de la interfaz para iniciar y detener el temporizador.
Para ello el bean tiene que convertirse en temporizador cuando llama a uno de los cuatro métodos
existentes y propios de una instancia de la clase javax.ejb.TimerService. Para ello, el bean hará uso
del método getTimerService() perteneciente a la clase EJBContext o usará la anotación @Resource.
Estas situaciones las podemos ver en los fragmento de código del apartado anterior.
Con el método getTimers() un bean obtiene acceso a todos los temporizadores que estén activos y
conectados a sus respectivas clases bean. De este modo, podremos parar cada temporizador o
conocer el momento en el que cada servicio vaya a ser ejecutado de nuevo.
El resto de métodos ofrecen la posibilidad de configurar el temporizador a través de sus
correspondientes parámetros, tal y como se esquematiza cada uno de ellos en las siguientes
ilustraciones:
Temporizador de una sola acción con comienzo en una fecha determinada e intervalo de repetición
establecido en milisegundos en su segundo parámetro.
Temporizador de una sola acción con inicio dentro de un cierto número de milisegundos.
Temporizador con inicio en unos milisegundos y repetición cada cierto número de milisegundos.
El temporizador cuenta con tres excepciones:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
- IllegalArgumentException
- IllegalStateException
- EJBException
Interfaz Timer
El método Timeout que vimos anteriormente devuelve una instancia de la clase Timer mediante la
cual:
- es posible detener el temporizador
- conocer cuando volverá a ejecutarse de nuevo.
Pues bien, esta interfaz cuenta con varios métodos:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
a) cancel(): Para detener el Timer.
b) getNextTimeout(): Para conocer la fecha y hora en que se iniciará de nuevo el temporizador.
c) getHandle(): Para obtener un manejador del temporizador que puede ser guardado en un
archivo o una base de datos y así poder acceder a él más tarde.
d) getInfo(): Para recoger el objeto al que se ha asociado el temporizador cuando vue creado.
e) getTimeRemaining(): Número de milisegundos que quedan hasta la siguiente ejecución del
temporizador.
El código anterior muestra un método que genera un temporizador que se ejecutará en un segundo
y que sólo será llamado en una ocasión, de ahí que tenga el valor null en el segundo parámetro.
También podemos configurar el temporizador a través del contexto de la sesión del siguiente modo:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Veamos un ejemplo simulado completo. En este caso tenemos un método de un a través del cual
podríamos detener diferentes temporizadores liberando la memoria del proceso. Para ello el
temporizador se iniciará un segundo después de la llamada y se ejecutará periódicamente cada
segundo después. Así, la clase bean implementa una interfaz remota y crea dentro de su método
startMonitor() el temporizador deseado con el nombre HSP. A su vez el método stopMonitor()
comprueba todos los temporizadores que han sido generados. Ahora bien, cada vez que uno de esos
temporizadores lleve la denominación HSP, dicho objeto será cancelado mediante la llamada a
cancel(). Cada segundo, el método timeout() devuelve la cantidad de memoria libre que hay
disponible.
public class AlarmSchedulerBean implements AlarmSchedulerRemote {
@Resource TimerService timerService;
public static final String COMANDO = "HSP";
public void startMonitor() {
timerService.createTimer(1 * 1000, 1 * 1000, COMANDO);
}
@Timeout
public void timeout(Timer timer) {
String info = (String)timer.getInfo();
if (info.equals(COMANDO)) {
System.out.println("Liberando memoria: "+ Runtime.getRuntime().freeMemory());
}
}
}
Recomendaciones finales
Conviene tener presentes algunas pautas importantes a la hora de utilizar temporizadores:
- Si un bean crea varios temporizadores debe usar atributo info para distinguir quien llama al
método de timeout (todos llaman al mismo)
- Si se cae el servidor, los temporizadores creados sobreviven a la caída. En este caso, los
eventos perdidos se disparan todos al arrancar de nuevo.
- El método Timeout puede ser transaccional
- El contexto de seguridad en el que se ejecuta el método Timeout debe ser especificado en
configuración
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Ver Video: Temporizador, en la Unidad 13, en el Módulo 6,
en la plataforma elearning
Actividades
“Recuerde que para un seguimiento óptimo de la unidad es imprescindible realizar las actividades
que encontrará en la unidad correspondiente de la plataforma eLearning”.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Unidad 14. Seguridad
Objetivo
Conocer y establecer unos EJB seguros en nuestras aplicaciones.
Introducción
Obviamente los requisitos de seguridad de nuestra aplicación dependen del sentido que le demos a
la misma. Así, por ejemplo si nos planteamos realizar un sitio web comercial, deberemos dejar paso
a cualquier usuario para que curiosee por nuestras páginas y contenidos sin ponerle ningún
impedimento a su navegación. Si desde el primer momento reclamáramos algunos datos de su parte
como email o darse de alta en nuestro sistema, seguramente, sería razón suficiente para que
abandonara nuestra tienda virtual y buscara en internet otra con contenidos similares y menos
exigente en su primera toma de contacto.
Otra situación bien distinta es que dentro de esa misma tienda, transparente y accesible, llegue un
momento en que el usuario quiera realizar un pedido. En ese momento, resulta obligatorio que el
cliente se identifique para que podamos gestor el envío del producto y la transacción económico con
todas las garantías para ambas partes. Será en este punto de la aplicación donde reclamemos que el
usuario nos aporte información sobre su persona y así poder crear un vínculo de confianza entre
ambos usuario y tienda virtual. Datos como su tarjeta de crédito, cuenta bancaria, nif, email de
contacto, etc. etc. resultarán imprescindibles y no resultará violenta su petición, sino bien justificada
y comprensible por el propio cliente.
Hay una segunda parte. Y es que dentro de nuestra aplicación también sólo determinadas personas
deben tener acceso a determinadas áreas. En este caso, no estamos hablando de los usuarios
particulares que navegan por nuestro sitio web, sino por los propios gestores o administradores de
nuestra aplicación. Sólo personas debidamente autorizadas podrán realizar cambios en nuestros
productos, nuestro diseño de página, alteración de precios, etc. Y en este sentido corresponde
otorgar permisos con diferente grado de maniobra dentro de la aplicación para poder realizar esos
cambios.
A través del breve ejemplo anterior podemos enseguida comprobar que en seguridad web hay que
hablar de diferentes niveles.
a) Seguridad técnica: corresponde a la transferencia segura y confiada de datos entre un
usuario/cliente y un servidor web. Aquí debemos asegurar que el cliente envía los datos al
servidor correspondiente y no a otro. Justo a aquél en el que se registró y que los datos no
va a ser interceptados por terceras personas o aplicación que puedan hacer un uso no
previsto de tales datos. Bien, estos problemas de comunicación sin duda importantes, salen
fuera del ámbito de control de Java y han de resolverse por otras vías que aquí no van a ser
revisadas.
b) Autorización funcional: consiste, básicamente, en establecer a quién se le permite hacer
qué dentro de nuestro sistema. Para ello deberemos fijar qué ámbitos de nuestra aplicación
son accesibles, qué se puede hacer dentro de ellos y sobre todo quién puede hacerlo. Para
ello, si alguien cliente, usuario, administrador, etc. intenta llamar a un método o función de
nuestro sitio web, deberemos estar en condiciones de identificarlo. Si el elemento a
identificar no coincidiera con lo esperado, entonces la aplicación debería estar en disposición
de rechazarlo. En el caso de EJB contamos con una excepción destinada a este propósito:
EJBAccessException.
En resumen, cuando diseñamos la seguridad de nuestra aplicación se deben cumplir las siguientes
características:
- La portabilidad no se debe ver afectada
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
- Transparencia, Aislamiento y Abstracción:
- Los desarrolladores de los beans no tienen por qué conocer nada sobre la seguridad
- Los desarrolladores de aplicaciones pueden manejar la seguridad sin modificar el código de
los beans
- Extensibilidad
- Flexibilidad: Los mecanismos de seguridad no deben imponer políticas de seguridad.
- Independencia de implementación
- Interoperabilidad. Entre servidores, entre vendedores …
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
La autenticación del emisor de la llamada responde al siguiente diagrama:
Autorización declarativa
La estrategia de autorización declarativa se realiza por medio de anotaciones o bien de
descriptores de despliegue en los que hay que:
a) especificar la declaración del rol de seguridad
b) la asignación de permisos de métodos
c) la asignación de identidades de seguridad.
Antes de Java EE 5, podíamos especificar información de autenticación y autorización para
componentes de la capa web así como para componentes con tecnología Enterprise JavaBeans,
también conocidos como enterprise beans, sólo en los deployment descriptors. Sin embargo, Java
EE 5 simplificó las cosas mediante la incorporación de anotaciones de seguridad. Estas anotaciones
se encuentran especificadas en JSR 250: Common Annotations for the Java Platform. Estas
anotaciones simplifican la autorización para enterprise beans y para componentes web. Esta
simplificación es especialmente importante para enterprise beans.
Trasladado al código resultaría así en el caso de las anotaciones:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Por su parte, la asignación de permisos a métodos resultaría del siguiente modo:
Partimos de una serie de usuarios o principales a los cuales hay que asociarlos con un rol, después
esos roles deberán tener acceso a una serie de beans y dentro de esos beans a unos métodos
concretos de cada uno de ellos.
Para estos caso usamos la anotación @RolesAllowed que puede fijar tanto en el nivel de clases como
en el de método qué roles debe tener un usuario para poder hacer uso de los métodos así marcados.
En los fragmentos de código siguiente vemos que para llamar a los métodos de la clase bBean se
debe tener el roll “HR”.
En este caso siguiente el rol reclamado sería “admin” para la llamada a los métodos de la clase
aBean:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Otro ejemplo de declaración de permisos mediante descriptor de despliegue sería el siguiente
combinando diferentes métodos y roles:
Aquí vemos unos métodos getAllAuctionsOfSeller y getSeller que no reclamarían una comprobación
de roles. Para ello está la etiqueta De tal forma que cualquier rol puede hacer uso de
ellos, mientras que para el método closeAuction, únicamente el rol auction-admin resultaría
autorizado para operar con él
El equivalente a la etiqueta en anotación es @PermitAll también empelable tanto a
nivel de clase como de método. Suele usarse sobre todo a nivel de método en aquellos casos donde
para la clase se haya usado un @RolesAllowed concreto. De esta forma, se puede aislar un método
concreto de la misma para que pueda ser llamado por cualquier rol, mientras que para el resto
regiría el rol principal declarado sobre la clase en su @RolesAllowed:
@Stateful
@RolesAllowed(“ADMIN”)
public class GrabarPedido implements GrabarPedidoRemoto {
@PermitAll
public void ObtenerNumPedidosHoy(){
//código
}
}
Nos queda ahora la declaración de la identidad de seguridad. La identidad con la cual un bean llama
a otro bean es la identidad del que le llama a él por defecto y, por defecto, corresponde al use-
caller-identity. Esto quiere decir que todos los métodos del bean deben transcurrir bajo el rol del
cliente que ha realizado la llamada. Este forma de operar no es válida en los message driven bean
porque éstos siempre carecen de un usuario asignado.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
No obstante, es posible cambiarla, por ejemplo, para que un bean llame a otro con una identidad
configurada mediante @Runas. Esta anotación sólo se puede emplear en el nivel de clase y se aplica
a todo los métodos de la dicha clase. Su cometido es que podamos fijar para qué roles funcionan
esos métodos cuando son llamados desde otro bean.
Esta identidad RunAs responde al siguiente diagrama:
Autorización programática
La otra estrategia es la autorización programática que supone el uso explícito de los APIs de
seguridad por parte del código de los componentes. Proporciona flexibilidad ya que, por ejemplo, un
mismo método puede comportarse de forma distinta en función del usuario o servidor que lo
reclame.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Por programa se puede averiguar el usuario/servidor y el rol con el que actúa gracias a los métodos
definidos en su EJBContext:
- isCallerInRole
- getCallerPrincipal
isCallerInRole(String roleName) permite al proveedor del bean codificar comportamientos distintos
para un mismo método en función del role de seguridad del usuario/servidor que reclame el uso de
dicho método. Es mejor proporcionar diferentes métodos y usar las declaraciones en el descriptor
de despliegue para especificar si un role puede o no acceder al método. Veamos un ejemplo:
En el diagrama anterior, el valor devuelto es false, por lo tanto el usuario no es autenticado y habría
que codificar la respuesta oportuna.
Podemos mapear los roles de bean a los de aplicación a través del descriptor de despliegue de la
siguiente forma:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
seguridad del cliente. Este método se utiliza para implementar la seguridad programática. Aquí
vemos un fragmento de código en el que comprobamos la identidad del elemento que provoca está
utilizando un objeto:
Como vemos primero intentamos identificar al usuario y ver si se corresponde con el rol “auction-
admin”. El resultado es false puesto que se trata de un “auction-user” por lo tanto echamos mano
del método getCallerPrincipal para a través de su método getName obtener una cadena con el rol
del usuario que demandó el método.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Responsabilidades del administrador
Cuando planteamos el diseño de una política de seguridad pueden surgir muchas dudas y tareas de
cómo llevarla a cabo:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
A cada una de ellas correspondería una función concreta tal y como vemos en esta otra ilustración:
Los proveedores EJB se encargarán de establecer las referencias a los roles de seguridad. Por su
parte los ensambladores de la aplicación definirán los roles de seguridad, y vincularán esas
referencias a roles con los verdaderos roles. El encargado de desplegar la aplicación, asignará los
roles a grupos de usuarios y, finalmente, el administrador del sistema creará los grupos de usuarios,
gestionará la política de seguridad y administrará los servicios de seguridad.
Como vemos unos son meros asignadores de elementos y relaciones, mientras que otros tienen
capacidad para crear, definir y modificar.
Actividades
“Recuerde que para un seguimiento óptimo de la unidad es imprescindible realizar las actividades
que encontrará en la unidad correspondiente de la plataforma eLearning”.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Unidad 15. Uso de las Mejores Prácticas de la
Tecnología EJB
Objetivo
Conocer las maneras más recomendables de implementar EJBs para obtener una funcionalidad
óptima.
Introducción
Ya sabemos que el bean de sesión se mantiene a lo largo de la sesión del cliente con la finalidad de
dar representar flujo de trabajo, estado de aplicación, lógica y utilidades de empresa. Un bean de
sesión con estado puede mantener datos entre las diferentes peticiones de un cliente; pero no
ocurre así con un bean de sesión sin estado.
Supongamos que utilizamos beans de sesión sin estado. En este contexto el servidor tiene mayores
oportunidades de aplicar mecanismos de optimización. El servidor mantiene un pool de
objetos disponibles y es capaz de reutilizar objetos para múltiples clientes. Pero siempre de forma
sucesiva o sincronizada, el servidor asigna un hilo a cada petición de un cliente, de tal forma que
no libera el hilo para otra petición a menos que la primera haya terminado:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Veámoslo desde el punto de vista del código. En la implementación del bean tenemos el método
ejbCreate():
public void ejbCreate() {
System.out.println("--->Llamada a ejbCreate() de " + getClass().getName());
}
Además en el cliente creamos dos EJBs:
try {
/**************************************************************************
* Liberamos el objeto: la instancia se devuelve al pool de bean de sesión.
* IMPORTANTE: la persistencia del objeto en memoría viene determinada por
* el servidor EJB.
**************************************************************************/
cal.remove();
/**************************************************************************
* Creamos una segunda referencia
**************************************************************************/
Calculador cal2 = home.create();
cal2.remove();
ctx.close();
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
En un bean de sesión sin estado, la respuesta del servidor es diferente:
13:58:23,046 INFO [STDOUT] --->Llamada a setSessionContext() de calculador.CalculadorBean
13:58:23,046 INFO [STDOUT] --->Llamada a ejbCreate() de calculador.CalculadorBean
Se puede observar que aunque se han creado sucesivamente dos objetos con create(), el servidor
EJB sólo ha invocado una vez a ejbCreate(). La razón es sencilla, el servidor creará un bean sólo
cuando no haya beans remotos en la reserva. En este caso, aunque hay dos create(), el servidor
invoca un ejbCreate() por una sencilla razón: el primer EJB está en el pool y no es necesario crear
un segundo bean. Otro aspecto importante es que, aunque se ha realizado remove() en el cliente,
no se ha invocado ejbRemove() por parte del servidor, es decir, el servidor mantiene objetos en
reserva (en el pool) y no los borra a menos que sea necesario.
Dicho de forma abstracta, estos tipos de beans tienen diferentes tipos de ciclos de vida.
Elegir
Llegado a este punto uno puede seguir preguntándose cuando utilizar beans de sesión con estado o
sin estado, más aún, cuando usar beans de entidad o beans de sesión. No hay normas
inquebrantables, aunque si hay orientaciones generales:
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.
Usaremos beans de sesión con estado para aplicaciones de carrito de la compra, workflow, etc., es
decir, en aquellas aplicaciones en las que el mantenimiento de datos de sesión sea crucial.
Usaremos beans de sesión sin estado para cálculos, ordenamientos de estructuras de datos, reglas
de negocio, etc., es decir, para aquellas aplicaciones en las que el servicio se de en el contexto de
petición y no en el de sesión.
Los beans de entidad se han pensado para representar entidades de negocio que requieren acceso a
bases de datos. Aunque en circunstancias especiales, empujados por imperativos de rendimiento se
puede usar un bean de sesión para accesos a datos. Pero en estas circunstancias se debe tener en
cuenta que puede que no aprovechemos los servicios de acceso a datos que nos puede dar el
servidor EJB, como por ejemplo, la gestión de un pool de conexiones.
Actividades
“Recuerde que para un seguimiento óptimo de la unidad es imprescindible realizar las actividades
que encontrará en la unidad correspondiente de la plataforma eLearning”.
www.learning.es
Para uso exclusivo de los alumnos de LEARNING & TRAINING CLOUD, S.L.