You are on page 1of 7

davidmarco.

es

http://www.davidmarco.es/blog/entrada.php?id=243

davidmarco.es
Inicio Blog Sobre m Tutoriales Contacto

Introduccin a EJB 3.1 (IV)


Publicado el 07 de Abril de 2011
En los artculos anteriores del tutorial de introduccin a EJB 3.1, hemos visto como declarar y trabajar con componentes de lado del servidor (Session Beans y Message-Driven Beans). El ltimo componente que nos queda por ver es Entity Beans (EB - Beans de Entidad; a partir de ahora nos referiremos a ellos como entidades).

4.1 ENTIDADES: CONCEPTOS BSICOS Las entidades, a diferencia del resto de componentes EJB, son objetos Java reales que son manejados entre componentes (o entre un cliente y un componente) en su forma original, nunca a travs de proxys/vistas. Podemos crearlos con sentencias new, pasarlos como parmetros a un Session Bean, etc. Pero el verdadero valor de las entidades reside en que su estado puede ser almacenado en una base de datos, y ms tarde recuperado en un nuevo objeto del tipo correspondiente. De manera adicional, los cambios que realicemos en el estado de una entidad sern sincronizados con la informacin que tenemos almacenada en la base de datos. Aunque las entidades se consideran componentes EJB, hasta la version JavaEE 1.4 pertenecan a una especificacin independiente llamada Java Persistence API (JPA - API de Persistencia en Java). Fue a partir de la versin 5 de JavaEE que la especificacin EJB absorvi a la especificacin JPA. Sin embargo, podemos trabajar con entidades en una aplicacin no-EJB, aunque, tras bambalinas, se seguirn ejecutando dentro de un contenedor EJB. Desde ahora usaremos el trmino aplicacin JPA para referirnos a aplicaciones que realizan persistencia, y el trmino aplicacin EJB cuando exista integracin de ambas tecnologas. En este artculo no vamos a tratar en profundidad la especificacin JPA (ni la declaracin ni el uso de entidades), si no como integrarla con una aplicacin EJB 3.1. Si no conoces JPA, es prcticamente obligatorio que visites el tutorial de JPA publicado en este mismo blog, de manera que puedas comprender todo el material que sigue a continuacin.

4.2 ENTIDADES: EL CICLO DE VIDA Como ya se ha mencionado, las entidades no son objetos del lado del servidor. Sin embargo, cuando son usadas dentro del contexto de un contenedor EJB, se convierten en objetos gestionados (gracias al servicio de persistencia, el cual es controlado mediante la interface EntityManager). Esto nos lleva a los dos nicos estados de una entidad cuando se encuentra en el contexto de un contenedor EJB: - Gestionada (Attached) - No gestionada (Detached) En el primer estado, la entidad se encuentra gestionada por el servicio de persistencia: cualquier cambio que realicemos en su estado se ver reflejado en la base de datos subyacente. En el segundo estado, la entidad es un objeto Java regular, y cualquier cambio que realicemos en su estado no ser sincronizado con la base de datos subyacente. En este ltimo estado, la entidad puede ser, por ejemplo, enviada a traves de una red (mediante serializacin).

4.3 ENTIDADES: UNIDAD DE PERSISTENCIA Una unidad de persistencia (persistence unit) representa un conjunto de entidades que pueden ser mapeadas a una base de datos, as como la informacin necesaria para que la aplicacin JPA pueda acceder a dicha base de datos. Se define mediante un archivo llamado persistence.xml, el cual debe acompaar a la aplicacin donde se realizan las tareas de persistencia (recuerda que puede ser una aplicacin EJB o una aplicacin Java normal):

<?xml version="1.0" encoding="UTF-8"?> <persistence 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 /persistence/persistence_2_0.xsd" version="2.0"> <persistence-unit name="introduccionEJB"> <!-- configuracin de acceso a la base de datos --> <!-- lista de entidades que pueden ser mapeadas --> </persistence-unit> </persistence>

http://java.sun.com/xml/ns

Podemos definir ms de una unidad de persistencia por aplicacin, declarando cada una de ellas mediante el

1 de 7

08/04/2013 10:06

davidmarco.es

http://www.davidmarco.es/blog/entrada.php?id=243

elemento XML <persistence-unit>. Cada unidad de persistencia debe seguir estas dos reglas: - Debe proporcionar un nombre (identidad) a travs del cual pueda ser llamado - Debe conectar a una sola fuente de datos (data source) Dependiendo del tipo de paquete que estemos construyendo (EAR, JAR, etc), el archivo persistence.xml deber encontrarse en una localizacin u otra. En la seccin 4.6 veremos un ejemplo completo de este archivo, as como la informacin necesaria para su correcto despliegue dentro de una aplicacin EJB.

4.4 ENTIDADES: CONTEXTO DE PERSISTENCIA Otro concepto que tenemos que tener claro es el de contexto de persistencia. Un contexto de persistencia representa un conjunto de instancias de entidades que se encuentran gestionadas en un momento dado. Existen dos tipos de contextos de persistencia: - Limitados a una transaccin (Transaction-scoped) - Extendidos (Extended) Cuando trabajamos dentro de un contexto de persistencia limitado a una transanccin, todas las entidades gestionadas pasarn a estar no gestionadas cuando dicha transaccin finalice. Dicho con otras palabras, los cambios realizados tras finalizar la transaccin no sern sincronizados con la base de datos. Cuando trabajamos dentro de un contexto de persistencia extendido, las cosas funcionan de manera diferente: el contexto de persistencia sobrevivir a la transaccin donde se ejecuta, de manera que los cambios que realicemos en el estado de las entidades gestionadas por el contexto de persistencia se sincronizarn con la base de datos en el momento en que entremos en una nueva transaccin. Este comportamiento es til cuando trabajamos con SFSB, pues permite mantener un estado conversacional y mantener nuestras entidades sincronizadas.

4.5 ENTIDADES: ENTITY MANAGER Mediante la interface EntityManager (Gestor de entidades) tenemos acceso al servicio de persistencia de nuestro contenedor. Podemos obtener una instancia de EntityManager en nuestros componentes EJB mediante inyeccin de dependencias:

import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @Stateless public class MiSlsb { @PersistenceContext(unitName="introduccionEJB") private EntityManager em; // operaciones del SLSB }

El ejemplo anterior inyecta una instancia de EntityManager en el SLSB mediante la anotacin @PersistenceContext. A esta anotacin hay que proporcionarle como atributo el nombre de la unidad de persistencia que EntityManager usar para realizar la persistencia (y que hemos definido en el archivo persistence.xml). Con los metadatos proporcionados obtendremos por defecto un contexto de persistencia limitado a una transaccin (ver seccin anterior); si deseamos obtener un contexto de persistencia extendido (solo vlido en SFSB por su naturaleza conversacional), debemos aadir el atributo type con el valor correspondiente para este comportamiento:

import javax.persistence.PersistenceContextType; // ... @PersistenceContext(unitName="introduccionEJB", type=PersistenceContextType.EXTENDED) private EntityManager em;

En lo que se refiere a este tutorial, la integracin de EJB con JPA termina aqu. Por tanto, podemos pasar a ver un ejemplo donde conectaremos todas las piezas para realizar persistencia mediante EJB 3.1.

4.6 ENTIDADES: UN SENCILLO EJEMPLO Veamos un sencillo ejemplo de un componente EJB realizando persistencia sobre una base de datos. Al igual que algunos ejemplos anteriores, este consta de varias partes: - Una fuente de datos (data source) donde realizar la persistencia - Una aplicacin EJB donde se realiza las acciones de persistencia - Un cliente EJB desde el que intercambiar entidades con la aplicacin EJB

2 de 7

08/04/2013 10:06

davidmarco.es

http://www.davidmarco.es/blog/entrada.php?id=243

Nuestro primer paso va a ser definir una fuente de datos que conectar nuestro contenedor EJB con nuestra base de datos. Siguiendo el entorno de desarrollo configurado en el anexo que acompaa este tutorial, escribimos un archivo llamado derby-ds.xml y lo guardamos en el directorio server\default\deploy de nuestra instalacin de JBoss:

<?xml version="1.0" encoding="UTF-8"?> <datasources> <local-tx-datasource> <jndi-name>DerbyDS</jndi-name> <connection-url>jdbc:derby://localhost:1527/introduccionEJB;create=true</connection-url> <driver-class>org.apache.derby.jdbc.ClientDataSource40</driver-class> <user-name></user-name> <password></password> </local-tx-datasource> </datasources>

En el archivo XML anterior definimos una fuente de datos (data source) limitado a transacciones locales, esto es, dentro del propio contenedor (existe otro ambito de ejecucin de una transaccin llamado extendido , capaz de realizar su trabajo a travs de mltiples contenedores). A esta fuente de datos le hemos dado un nombre JNDI desde la que poder invocarla, as como los parmetros de conexin con nuestra base de datos subyacente (url, usuario, y password). Si JBoss 6.0.0 Final est arrancado, nada ms guardar el archivo anterior la fuente de datos ser activada y se producir la conexin con Derby, as que debers tener la base de datos iniciada o se producir un error; nosotros vamos a considerar que en este preciso momento tanto JBoss como Derby estn parados. El siguiente paso es crear un proyecto EJB 3.1 en Eclipse, y continuacin levantar la base de datos Derby desde el propio IDE. Para ello, haz click con el botn derecho sobre el nombre del proyecto EJB en la pestaa Proyect Explorer y selecciona: Apache Derby > Add Apache Derby nature La operacin anterior aade a nuestro proyecto las librerias del servidor embebido Derby, de manera que podamos conectar con l. A continuacin levantamos la base de datos haciendo click con el botn derecho sobre el nombre del proyecto EJB en la pestaa Proyect Explorer y seleccionando: Apache Derby > Start Derby Network Server Nos aparecer una ventana donde se nos informa que la base de datos est siendo levantada, haz click en el botn OK para finalizar este proceso. En un entorno en produccin, todas estas operaciones seran innecesarias, pues tericamente tendramos una base de datos externa funcionando de manera continua. Ahora vamos a crear un componente SLSB remoto, de manera que podamos llamarlo desde un cliente Java normal. Como recordars, un componente remoto requiere de manera obligatoria implementar una interface:

package es.davidmarco.ejb.slsb; import es.davidmarco.ejb.entidad.Cuenta; public interface OperacionesConCuentas { public void crearCuenta(Cuenta cuenta); public Cuenta obtenerCuenta(Long id); public void borrarCuenta(Cuenta cuenta); }

Ahora ya podemos implementar el componente remoto:

package es.davidmarco.ejb.slsb; import import import import import javax.ejb.Remote; javax.ejb.Stateless; javax.persistence.EntityManager; javax.persistence.PersistenceContext; es.davidmarco.ejb.entidad.Cuenta;

@Remote @Stateless public class OperacionesConCuentasRemote implements OperacionesConCuentas { @PersistenceContext(unitName="introduccionEJB") private EntityManager em; @Override public void crearCuenta(Cuenta nuevaCuenta) { em.persist(nuevaCuenta); }

3 de 7

08/04/2013 10:06

davidmarco.es

http://www.davidmarco.es/blog/entrada.php?id=243

@Override public Cuenta obtenerCuenta(Long id) { return em.find(Cuenta.class, id); } @Override public void borrarCuenta(Cuenta cuenta) { em.remove(cuenta); } }

El componente anterior es extremadamente sencillo: en l se inyecta una instancia de EntityManager, la cual es usada en los mtodos del Session Bean para realizar las tareas de persistencia. Estos mtodos usan como parmetros o tipos de retorno instancias de la clase Cuenta (la cual define cuentas bancarias) y que ser nuestra entidad:

package es.davidmarco.ejb.entidad; import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class Cuenta implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue private Long id; private String numeroDeCuenta; private String nombreDelTitular; private Double saldo;

public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getNumeroDeCuenta() { return numeroDeCuenta; } public void setNumeroDeCuenta(String numeroDeCuenta) { this.numeroDeCuenta = numeroDeCuenta; } public String getNombreDelTitular() { return nombreDelTitular; } public void setNombreDelTitular(String nombreDelTitular) { this.nombreDelTitular = nombreDelTitular; } public Double getSaldo() { return saldo; } public void setSaldo(Double saldo) { this.saldo = saldo; } public void aumentarSaldo(Double cantidad) { saldo += cantidad; } public void reducirSaldo(Double cantidad) { saldo -= cantidad; } }

Algunos detalles de la entidad definida en el ejemplo anterior merecen una pequea explicacin: para empezar, nuestra entidad implementa la interface Serializable, necesara cuando nuestra entidad va a viajar a travs de

4 de 7

08/04/2013 10:06

davidmarco.es

http://www.davidmarco.es/blog/entrada.php?id=243

una red (en nuestro caso entre el cliente Java y el contenedor EJB). Dentro de la entidad definimos 4 propiedades: una para la identidad de la entidad, y tres para representar su estado ( numeroDeCuenta, nombreDelTitular, y saldo), ms sus correspondientes mtodos getter/setter. De manera adicional, hemos aadido algunas operaciones de lgica de negocio (aumentarSaldo() y reducirSaldo) dentro de la entidad; es una buena prctica que las operaciones relacionadas con cuentas estn dentro de la clase que representa dichas cuentas. Ahora que tenemos un componente EJB y una entidad, vamos a crear la unidad de persistenca asociada a la fuente de datos y nuestra entidad. Crea un archivo llamado persistence.xml en el directorio META-INF del proyecto EJB:

<?xml version="1.0" encoding="UTF-8"?> <persistence 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 /persistence/persistence_2_0.xsd" version="2.0"> <persistence-unit name="introduccionEJB" transaction-type="JTA"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <jta-data-source>java:/DerbyDS</jta-data-source> <class>es.davidmarco.ejb.entidad.Cuenta</class>

http://java.sun.com/xml/ns

<properties> <property name="hibernate.dialect" value="org.hibernate.dialect.DerbyDialect" /> <property name="hibernate.hbm2ddl.auto" value="update"/> </properties> </persistence-unit> </persistence>

En el archivo XML anterior hemos declarado una unidad de persistencia con nombre introduccionEJB (que fue el que usamos como parmetro de la anotacin @PersistenceContext en el componente SLSB que definimos previamente), as como transacciones de tipo JTA (gestionadas por el contenedor; los tipos de transaccin se explicaron en las secciones 3.2 y 3.3 del tercer artculo del tutorial de JPA). Ya dentro de la declaracin de la unidad de persistencia, hemos declarado el proveedor de persistencia que usaremos (HibernatePersistence), la direccin JNDI de la fuente de datos que declaramos en el archivo derbyds.xml, y las entidades que gestionaremos en esta unidad de persistencia (en nuestro caso solamente Cuenta). Por ltimo, hemos configurado algunos detalles relativos a la fuente de datos dentro del elemento <properties>: el dialecto que usar el contenedor para construir sentencias SQL adecuadas a nuestra base de datos, y la creacin automtica de los esquemas necesarios en base a los metadatos de nuestras entidades (de esta manera nos evitamos crear manualmente tanto las tablas como sus columnas, incluyendo la definicin del tipo de dato de cada columna, restricciones de cada columna, etc). Ahora ya podemos desplegar la aplicacin EJB en el contenedor. El ltimo paso necesario para probar nuestro ejemplo es crear un cliente Java que pasar una instancia ya inicializada de la entidad Cliente al componente EJB. Para ello, y para mantener las cosas sencillas, creamos un proyecto Java en Eclipse y le aadimos las librerias del proyecto EJB haciendo click con el botn derecho del ratn en el nombre del proyecto Java y seleccionando: Build Path > Configure Build Path En la ventana que nos aparece, vamos a la pestaa Proyects, hacemos click en el botn Add, seleccionamos el proyecto EJB donde esta nuestro componente SLSB y nuestra entidad, hacemos click en el botn OK, y de nuevo hacemos click en el botn OK. Ahora ya podemos escribir el cliente:

package es.davidmarco.ejb.cliente; import import import import import import java.util.Properties; javax.naming.Context; javax.naming.InitialContext; javax.naming.NamingException; es.davidmarco.ejb.entidad.Cuenta; es.davidmarco.ejb.slsb.OperacionesConCuentas;

public class Cliente { private static final String JNDI_BEAN = "OperacionesConCuentasRemote/remote"; public static void main(String[] args) throws NamingException { Properties properties = new Properties(); properties.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory"); properties.put("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces"); properties.put("java.naming.provider.url", "jnp://localhost:1099"); Context context = new InitialContext(properties); Cuenta cuenta = new Cuenta(); cuenta.setNumeroDeCuenta("0000-0001"); cuenta.setNombreDelTitular("Nuevo cliente"); cuenta.setSaldo(2500D); */

5 de 7

08/04/2013 10:06

davidmarco.es

http://www.davidmarco.es/blog/entrada.php?id=243

OperacionesConCuentas occ = (OperacionesConCuentas)context.lookup(JNDI_BEAN); occ.crearCuenta(cuenta); } }

En el ejemplo anterior, creamos e inicializamos una cuenta, obtenemos un proxy/vista al componente EJB, e invocamos su mtodo crearCuenta() pasndole como parmetro la cuenta. Esta entidad ser serializada, viajar a traves de la red hasta el contenedor, ser deserializada, y dentro del componente EJB ser persistida en la base de datos. Podemos comprobarlo ejecutando una sentencia SQL contra la base de datos: para ello, haz click con el botn derecho sobre el nombre del proyecto EJB en la pestaa Proyect Explorer y selecciona: Apache Derby > ij (Interactive SQL) La consola de ij se abrir en la pestaa Console de Eclipse, y desde el prompt de ij nos conectamos a la base de datos mediante el comando: connect 'jdbc:derby://localhost:1527/introduccionEJB;' Cuando la conexin se realice, volveremos a ver el prompt de ij. Ahora ya podemos realizar la consulta SQL mediante el comando: select * from cuenta; La consola de ij nos mostrar un registro en la tabla Cuenta: 1 |Nuevo cliente |0000-0001 |2500.0

Esto demuestra que nuestra entidad (un POJO Java) ha sido almacenado en una base de datos relacional (tablas y columnas) de manera transparente para nosotros. Misin cumplida. Si deseramos realizar el proceso inverso (obtener un objeto Java desde la informacin almacenada en la base de datos):

// ... public class Cliente { private static final String JNDI_BEAN = "OperacionesConCuentasRemote/remote"; public static void main(String[] args) throws NamingException { Properties properties = new Properties(); properties.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory"); properties.put("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces"); properties.put("java.naming.provider.url", "jnp://localhost:1099"); Context context = new InitialContext(properties); // // // // Cuenta cuenta = new Cuenta(); cuenta.setNumeroDeCuenta("0000-0001"); cuenta.setNombreDelTitular("Nuevo cliente"); cuenta.setSaldo(2500D); */

OperacionesConCuentas occ = (OperacionesConCuentas)context.lookup(JNDI_BEAN); // occ.crearCuenta(cuenta); Cuenta cuenta = occ.obtenerCuenta(1L); System.out.println("Titular de la cuenta " + cuenta.getNumeroDeCuenta() + " con saldo " + cuenta.getSaldo() + ": " + cuenta.getNombreDelTitular()); } }

En el ejemplo anterior, hemos comentado las lineas que crean y persisten un cliente (de otra manera se insertar un nuevo registro con los misma informacin en la base de datos pero con ID con valor 2), y en su lugar hemos llamado al mtodo obtenerCuenta() del SLSB, para as obtener una cuenta desde la base de datos en base a su ID. Si una cuenta con ese ID no existe, obtendremos como respuesta un valor null. Te invito a que, a modo de prctica, elimines de la base de datos la cuenta que hemos creado llamando al mtodo borrarCuenta() del SLSB; para verificarlo, una vez hayas ejecutado esta operacin vuelve a realizar la consulta SQL contra la base de datos a travs de la consola de ij: select * from cuenta; Si has realizado correctamente el ejercicio, la tabla Cuenta no deber mostrar ninguna entidad (salvo que hayas insertado entidades adicionales).

4.7 RESUMEN Como hemos visto en este artculo, podemos realizar persistencia de manera extremadamente sencilla en

6 de 7

08/04/2013 10:06

davidmarco.es

http://www.davidmarco.es/blog/entrada.php?id=243

nuestras aplicaciones EJB. Las entidades son simples POJO's, la configuracin con la base de datos se configura mediante archivos XML, y en nuestros componentes EJB solo tenemos que inyectar una unidad de persistencia y ejecutar sus mtodos. En este punto ya hemos visto todos los componentes EJB (Session Beans, Message-Driven Beans, y Entities). Los dos prximos artculos estarn dedicados a los diversos servicios que ofrece el contenedor, servicios que nuestros componentes pueden usar para realizar taras ms complejas (y que son necesarias en aplicaciones reales).

Volver al blog

7 de 7

08/04/2013 10:06

You might also like