Você está na página 1de 80

Plan de Formacin ATICA

EJB 3
Resumen
Persistencia EJBs con JPA en JavaEE

Persistencia con JPA EJB 3

ndice
1. Introduccin al mundo EJB 3 ................................................................................... 3 1.1. Qu es un EJB? ............................................................................................... 3 1.2. Tipos de EJB .................................................................................................... 4 1.2.1. Session Beans ............................................................................................ 4 1.2.2. Stateless session beans............................................................................... 4 1.2.3. Stateful session beans ................................................................................ 5 1.3. Qu servicios proveen los EJBs? ........................................................................ 7 2. Introduccin a la persistencia .................................................................................. 9 3. JPA (Java Persistente API) ..................................................................................... 11 3.1. Implementacin de modelos de dominio ............................................................ 11 3.1.1. Actores en un modelo de dominio ............................................................... 12 3.1.2. Implementacin de los objetos del dominio con JPA ...................................... 13 3.2. Mapeo Objeto-Relacional ................................................................................. 22 3.2.1. Mapeando Entidades ................................................................................. 22 3.2.2. Generacin de claves primarias .................................................................. 28 3.3. Mapeando relaciones entre entidades ................................................................ 31 3.3.1. Mapeando relaciones uno-a-uno ................................................................. 31 3.3.2. Mapeando las relaciones uno-a-muchos y muchos-a-uno ............................... 34 3.3.3. Mapeando relaciones Muchos-a-muchos ...................................................... 36 3.4. Mapeando la Herencia ..................................................................................... 37 3.4.1. Estrategia Single-Table ............................................................................. 38 3.4.2. Estrategia Joined-tables ............................................................................ 39 3.4.3. Estrategia Table-per-class ......................................................................... 40 3.5. Manipulando entidades: el interfaz EntityManager .............................................. 41 3.5.1. El interfaz EntityManager........................................................................... 41 3.5.2. El ciclo de vida de una entidad ................................................................... 43 3.5.3. Contextos de persistencia, alcance, y el EntityManager ................................. 45 3.5.4. Creando instancias del EntityManager ......................................................... 48 3.6. Gestionando las operaciones de persistencia ...................................................... 52 3.6.1. Persistiendo entidades .............................................................................. 52 3.6.2. Persistiendo relaciones entre entidades ....................................................... 52 3.6.3. Operaciones de persistencia en cascada ...................................................... 53 3.6.4. Recuperando entidades por clave primaria ................................................... 54 3.6.5. Actualizando Entidades ............................................................................. 57 3.6.6. Borrando entidades .................................................................................. 59 3.6.7. Refrescando entidades .............................................................................. 59 3.7. Gestin de Transacciones en EJB 3 ................................................................... 60 3.7.1. Transacciones gestionadas por el contendor (CMT) ....................................... 60 3.7.2. Transacciones gestionadas por el Bean (BMT) .............................................. 62 3.8. Descripcin del mdulo de persistencia: persistente.xml...................................... 63 3.9. Usando JPQL para recuperar entidades .............................................................. 64 3.9.1. Anatoma de una consulta ......................................................................... 64 3.9.2. Definir named querys................................................................................ 65 3.9.3. Ejecutando las consultas ........................................................................... 66 3.10. El lenguaje JPQL .......................................................................................... 70 3.10.1. Tipos de sentencias ............................................................................... 70 3.10.2. La clusula FROM .................................................................................. 71 3.10.3. Expresiones condicionales y Operadores ................................................... 72 3.10.4. Trabajando con funciones JPQL ............................................................... 74 3.10.5. La clusula SELECT................................................................................ 75 3.10.6. Usando las funciones de agregacin ......................................................... 76 3.10.7. Ordenando los resultados de una consulta ................................................ 77 3.10.8. Usando subquerys ................................................................................. 78 3.10.9. Joins entre entidades ............................................................................. 79

Pgina 2 de 80

Persistencia con JPA EJB 3

1. Introduccin al mundo EJB 3


1.1. Qu es un EJB?
Enterprise Java Beans (EJB) es una plataforma para construir aplicaciones de negocio portables, reusables y escalables usando el lenguaje de programacin Java. Desde el punto de vista del desarrollador, un EJB es una porcin de cdigo que se ejecuta en un contenedor EJB, que no es ms que un ambiente especializado (runtime) que provee determinados componentes de servicio. Los EJBs pueden ser vistos como componentes, desde el punto de vista que encapsulan comportamiento y permite reutilizar porciones de cdigo, pero tambin pueden ser vistos como un framework, ya que, desplegados en un contenedor, proveen servicios para el desarrollo de aplicaciones de empresa, servicios que son invisibles para el programador y no ensucian la lgica de negocio con funcionalidades trasversales al modelo de dominio.

En la especificacin 3.0, los EJB no son ms que POJOs (clases planas comunes y corrientes de Java) con algunos poderes especiales implcitos, que se activan en runtime cuando son ejecutados en un contenedor de EJBs. Una anotacin transforma un simple POJO en un EJB:

Los servicios que debe proveer el contenedor de EJBs deben ser especificados por el programador a travs de metadatos de configuracin que puede escribirse como: Anotaciones de Java5 intercaladas en el cdigo de las clases. Descriptores XML (archivos XML separados).

Pgina 3 de 80

Persistencia con JPA EJB 3

A partir de EJB 3 se puede usar cualquiera de estas dos tcnicas. Las tcnicas no son exclusivas, pueden coexistir anotaciones con descriptores XML y, en el caso de superponerse los metadatos, los XML tendrn prioridad y podrn sobrescribir las anotaciones. Algunos ejemplos de los contenedores ms 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.

1.2. Tipos de EJB


1.2.1. Session Beans
En una aplicacin de empresa tpica, dividida en cuatro grandes capas o layers (presentacin, lgica de negocio (business logic), persistencia y base de datos (DBMS)), los Session Beans viven en la lgica de negocio. Hay dos grandes tipos de Session Beans: Stateless y Stateful.

1.2.2. Stateless session beans


Los Stateless session beans son objetos distribuidos que no tienen asociado un estado conversacional, por tanto se puede acceder al bean de manera concurrente. Como no tienen estado, el valor de sus atributos no se mantiene entre sucesivas invocaciones de sus mtodos. Todas las instancias de este tipo de beans son idnticas. Los beans de sesin sin estado son pooleados por el contender del servidor de aplicaciones. Esto significa que, por cada bean gestionado por el contenedor, ste mantiene un cierto nmero de instancias accesibles en un pool. Por cada solicitud realizada por un cliente, una instancia del pool es rpidamente asignada a dicho cliente. Cuando termina la solicitud del cliente, la instancia es devuelta al pool para poder ser reutilizada. Esto significa que un pequeo nmero de instancias de un bean pueden dar servicio a un gran nmero de clientes:

Ejemplo:

@Stateless (1) public class HelloUserBean implement HelloUser { ____public void sayHello(String name) { ____System.out.println("Hello " + name + " welcome to EJB 3!"); } } Pgina 4 de 80

Persistencia con JPA EJB 3

@Remote (2) public interface HelloUser { void sayHello(String name); }

Para definir un bean de sesin sin estado usamos la anotacin @Stateless (1). Los beans de sesin deben implementar una interfaz (2). Podemos definir tres tipos de interfaces: remotos, locales y Web Service (este ltimo slo para el caso de los beans de sesin sin estado): o Local interface: Los interfaces locales se usan cuando los clientes de los bean de sesin estn ejecutndose en la misma instancia del contenedor (JVM). Para ello, basta con marcar nuestra interfaz con la anotacin @Local. Remote interface: Los interfaces remotos se usan cuando los clientes de los beans de sesin residen en distintas instancias del contenedor (JVM) de EJBs. Si el cliente esta escrito es Java, la comunicacin entre el cliente y el bean se har mediante RMI. Para indicar nuestra interfaz como remota, usamos la anotacin @Remote. Web service interface: este tipo de interfaz es especfico de los beans de sesin sin estado. La habilidad de exponer un bean de sesin sin estado como un Web Service basado en SOAP es una de las ms potentes caractersticas de EJB 3. Todo lo que necesitas para hacer que tu bean sea accesible mediante SOAP es anotar tu interfaz de negocio con @WebService.

1.2.3. Stateful session beans


Los Stateful session beans son objetos distribuidos que tienen asociado un estado conversacional. El estado puede ser persistido, pero el acceso al bean se limita a uno por cliente:

No se mantiene un pool de instancias de este tipo de beans, si no que el contenedor crea una instancia para cada cliente y cada una de estas instancias mantiene la informacin del estado del cliente al cual ha sido asignado. La instancia del bean existe hasta que es eliminado explcitamente por el cliente, o bien hasta que se da un time out. Este tipo de beans estn pensados para implementar una conversacin con un cliente en la que es necesario mantener el estado durante el periodo que dura la conversacin. Ejemplos tpicos seran el carrito de la compra, o la entrada de datos basada en un workflow:

Pgina 5 de 80

Persistencia con JPA EJB 3

@Stateful (1) // Anotamos el POJO como stateful public class PlaceOrderBean implements PlaceOrder { // Estos cuatro atributos definen las variables de instancia que mantienen el estado private Long bidderID; private List items; private ShippingInfo shippingInfo; private BillingInfo billingInfo; public PlaceOrderBean () { items = new ArrayList(); } public void setBidderID(Long bidderId) { this.bidderId = bidderId; } public void addItem(Long itemId) { items.add(itemId); } public void setShippingInfo(ShippingInfo shippingInfo) { this.shippingInfo = shippingInfo; } public void setBillingInfo(BillingInfo billingInfo) { this.billingInfo = billingInfo; } @Remove (2) public Long confirmOrder() { Order order = new Order(); order.setBidderId(bidderId); order.setItems(items); order.setShippingInfo(shippingInfo); order.setBillingInfo(billingInfo); saveOrder(order); billOrder(order); return order.getOrderId(); } Pgina 6 de 80

Persistencia con JPA EJB 3

} @Remote (3) public interface PlaceOrder { void setBidderId(Long bidderId); void addItem(Long itemId); void setShippingInfo(ShippingInfo shippingInfo); void setBillingInfo(BillingInfo billingInfo); Long confirmOrder(); }

Lo primero es marcar nuestro POJO como bean de sesin con estado (1). Como ya indicamos al describir los beans de sesin con estado, debemos implementar una interfaz, en este caso, la declaramos como remota (3). Es muy importante destacar que este tipo de beans deben tener un mtodo anotado con @Remove (2), que indica el final del workflow y permite al contenedor liberar el espacio consumido por el bean.

Message-Driven Beans (MDBs): tambin viven en la lgica de negocio y los servicios que proveen son parecidos a los Session Beans, con la diferencia de que los MDBs son usados para invocar mtodos de forma asincrnica. Cuando se produce la invocacin de un mtodo de un MDB desde un cliente, la llamada no bloquea el cdigo del cliente y el mismo puede seguir con su ejecucin, sin tener que esperar indefinidamente por la respuesta del servidor. Los MDBs encapsulan el popular servicio de mensajera de Java, JMS. Los MDBs son a JMS lo que JDBC es a SQL. Entities: Las entidades viven en la capa de persistencia y son los EJBs que manejan la Java Persistence API (JPA), tambin parte de la especificacin de EJB 3.0. Las entidades son POJOs con ciertos metadatos que permiten a la capa de persistencia mapear los atributos de la clase a las tablas de la base de datos y sus relaciones.

1.3. Qu servicios proveen los EJBs?


Integracin: Proveen una forma de acoplar en tiempo de ejecucin diferentes componentes, mediante simple configuracin de anotaciones o XMLs. El acoplamiento se puede hacer mediante Inyeccin de Dependencia (DI) o usando JNDI, como se haca en EJB 2. La integracin es un servicio que proveen los beans de sesin y los MDBs. DEF. Inyeccin de Dependencia (DI) o Inversin de Control (IoC): La Inyeccin de Dependencia (DI) es un concepto (o patrn) de arquitectura que est fuertemente emparentado con la Inversin de Control (IoC). Bsicamente la DI se especializa en inyectar objetos en una clase, en lugar de ser la propia clase quien cree el objeto. En EJB 3 es el contenedor de EJBs quien inyecta en cada objeto los objetos necesarios segn las relaciones plasmadas en los descriptores de despliegue o en las anotaciones. Pooling: El contenedor de EJBs crea para componentes EJB un pool de instancias que es compartido por los diferentes clientes. Aunque cada cliente ve como si recibiera siempre instancias diferentes de los EJB, el contenedor est constantemente reusando objetos para optimizar memoria. El pooling es un servicio que se aplica a los Stateless Session Beans y a los MDBs.

Pgina 7 de 80

Persistencia con JPA EJB 3

Thread-safety: El programador puede escribir componentes del lado del servidor como si estuviera trabajando en una aplicacin sencilla con un solo thread (hilo). El contenedor se encarga de que los EJBs tengan el soporte adecuado para una aplicacin multi-usuario (como son en general las aplicaciones de empresa) de forma transparente, asegurando el acceso seguro, consistente y de alto rendimiento. Aplica a los beans de sesin y a los MDBs. Administracin de Estados: El contenedor de EJBs almacena y maneja el estado de un Stateful Session Bean de forma transparente, lo que significa que el programador puede mantener el estado de los atributos de una clase como si estuviera desarrollando una aplicacin de escritorio ordinaria. El contenedor maneja los detalles de las sesiones. Mensajera: Mediante los MDBs es posible desacoplar por completo dos componentes para que se comuniquen de forma asincrnica, 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 configuracin. Esto significa que cuando un mtodo de un EJB (Session Bean o MDB) se completa normalmente, el contenedor se encargar de hacer un commit de la transaccin y hacer efectivos los cambios que se realizaron en los datos de forma permanente. Si algo fallara durante la ejecucin del mtodo (una excepcin o cualquier otro problema), la transaccin hara un rollback y es como si el mtodo jams se hubiera invocado. Seguridad: EJB soporta integracin con la Java Authentication and Authorization Service (JAAS) API, haciendo casi transparente el manejo transversal de la seguridad. Aplica a todos los Session Beans. Interceptors: EJB introduce un framework liviano y simple para AOP (programacin orientada a aspectos). Acceso Remoto: Es posible acceder de forma remota a distintos EJBs de forma sencilla, simplemente mediante la Inyeccin de Dependencia. El procedimiento para inyectar un componente local o uno remoto es exactamente el mismo, abstrayndonos de las complicaciones especficas de RMI o similares. Este servicio aplica nicamente a los Session Beans. Web Services: Un Stateless Session Bean puede publicar sus mtodos como web services mediante una sencilla anotacin. Persistencia: EJB 3 provee la especificacin JPA para el mapeo de objetos (Entities) a tablas. Catching and Performance: JPA provee de forma transparente un importante nmero de servicios que permiten usar una cach de entidades en memoria y una lectura y escritura sobre la base de datos con un alto rendimiento.

Pgina 8 de 80

Persistencia con JPA EJB 3

2. Introduccin a la persistencia
Desde una perspectiva abstracta podramos afirmar que una aplicacin informtica consta de dos componentes principales que colaboran para llevar a cabo la funcionalidad que el usuario desea. El primero de estos componentes es la base de datos, que guarda la informacin necesaria para operar con la aplicacin, en forma de datos en disco. El segundo de estos componentes es el programa propiamente dicho, que recupera esos datos de la base de datos, realiza los clculos necesarios y presenta los resultados deseados al usuario. Para que estos dos componentes puedan funcionar juntos deben poder comunicarse intercambiando datos, en otras palabras, deben ser compatibles. Sin embargo, durante los ltimos treinta aos la evolucin de estos dos componentes ha sido divergente, de forma que cada vez se ha hecho ms difcil que colaboren en una misma aplicacin. Por un lado, no es ningn secreto que las Bases de Datos Relacionales son el tipo ms comn de bases de datos en la mayora de las organizaciones actuales si los comparamos con otros formatos (por ejemplo, Orientadas a Objetos, Jerrquicas, de Red, etc.). Nombres como Oracle, Microsoft SQL Server, MySQL, IBM DB2 o Sysbase son trminos comunes usados en el mundo del desarrollo del software. Por otra parte, en el lado de los lenguajes de programacin, el paradigma de orientacin a objetos (OO) se ha convertido en la norma. Lenguajes como Java, C#, C++, etc. son temas comunes de discusin entre los desarrolladores. Y bien, por qu es tan complicada la comunicacin entre estos dos paradigmas? Uno de los principales problemas est en la forma de representacin de los datos en ambos mundos: Las bases de datos relacionales estn estructuradas de una forma tabular y los ejemplares orientados a objetos normalmente estn estructurados en forma de rbol. Otro gran problema es la forma en que se definen las relaciones en uno y otro paradigma: En las bases de datos relacionales, las relaciones son tpicamente definidas en trminos de direccin y cardinalidad (multiplicidad en terminologa OO). Desde una perspectiva OO, las relaciones se definen como asociacin, herencia, agregacin y composicin. Por ltimo, la coexistencia de estos dos paradigmas plantea un escenario donde los desarrolladores estn obligados a utilizar y conocer detalladamente diferentes tcnicas de acceso e interfaces de programacin para cada uno de los sistemas gestores de bases de datos (SGBD) que utilicen. Esto significa conocer como poco: Dos lenguajes distintos para plasmar la lgica del negocio: Java (u otro lenguaje OO) y el lenguaje especializado requerido por el sistema gestor de base de datos (aunque esto se est reduciendo gracias a los estndares, como el SQL92, que ya implementan la mayora de los SGBD actuales, al menos en gran parte). El modo de integrar ambos evitando la resistencia por la falta de correspondencia (impedance mismatch) de uno y otro lenguaje.

Pgina 9 de 80

Persistencia con JPA EJB 3

Como consecuencia de todo esto, la mayora de las aplicaciones que utilizan bases de datos relacionales y lenguajes orientados a objetos acaban con una gran cantidad de cdigo de mapeo entre un modelo y otro. De hecho, se han realizado estudios que demuestran que el 35 % del cdigo de una aplicacin se produce para mapear datos entre la aplicacin y la base de datos. Esto acaba implicando que el cdigo de mapeo sea engorroso (debido al uso de cdigo SQL embebido o llamadas a procedimientos almacenados) o el uso de tecnologas complicadas y difciles de codificar como los EJBs de entidad. Como una forma de reducir este costoso proceso de mapeo de los objetos a tablas de la base de datos y, de alguna manera, automatizar este proceso, surgen los llamados frameworks de persistencia, tambin conocidos como Mapeadores Objeto/Relacional (Object/Relational Mapping u ORM). Los ORMs tienen tantos propsitos que juntos pueden aadir una enorme ganancia de productividad. Especficamente: Los ORMs eliminan el requerimiento de escribir SQL para cargar y persistir el estado de los objetos. Aunque an se tiene que escribir consultas, una buena herramienta ORM debera hacerlo ms sencillo. Los ORMs permiten crear un modelo de dominio de datos apropiado sin muchos de los problemas asociados con el modelo relacional. Permite pensar en trminos de objetos, en vez de en filas y columnas. Los ORMs pueden realizar deteccin automtica de cambios en el estado de los objetos, eliminando una tarea propensa a errores del ciclo de vida de los desarrolladores. Los ORMs pueden mejorar la portabilidad entre bases de datos reduciendo la dependencia del SQL especfico de la base de datos, que se puede abstraer detrs de la herramienta ORM.

Se calcula que un motor de persistencia puede reducir el cdigo de una aplicacin en un 40%, hacindola menos costosa de desarrollar. Adems, el cdigo que se obtiene programando de esta manera es ms limpio y sencillo y, por lo tanto, ms fcil de mantener y ms robusto. Centrndonos en el mundo de Java, la oferta es amplia en cuanto a motores de persistencia. Como primera aproximacin se podra considerar el mecanismo proporcionado por los EJB (Enterprise Java Beans) de entidad, pero stos no permiten implementar algunas caractersticas de la programacin orientada a objetos (por ejemplo, herencia) y adems, obligan a una forma de programar diferente a los objetos normales de Java (o POJOs, por Plain Old Java Objects). Existen motores de persistencia ms completos que no tienen los inconvenientes que se acaban de mencionar. Entre los de cdigo abierto podemos destacar: Hibernate, Castor, Torque, OJB y Cayenne. Entre los comerciales, podemos destacar Oracle TopLink, Cocobase y FastObjects.). La nueva especificacin JSR-220 de Sun (EJB 3.0) introduce Java Persistente API (JPA), que intenta establecer un standar para la persistencia de objetos para la comunidad Java, ya que se construye sobre la combinacin de las mejores ideas de los frameworks ms populares, incluyendo Hibernate, TopLink, JDO y otros. Pero, como la mayora de las cosas en este mundo, los ORMs no estn exentos de crticas. Algunos han sealado que la promocin de las herramientas de mapeo objeto-relacional son un intento de resolver del lado equivocado el problema de la falta de concordancia objetorelacional (Object-Relational impedance mismatch issue). El principio de que la informacin debe almacenarse en bases de datos relacionales implica que la orientacin a objetos es inadecuada para una completa manipulacin de datos y que el "paradigma" como un todo debe ser revisado.

Pgina 10 de 80

Persistencia con JPA EJB 3

3. JPA (Java Persistente API)


Vamos a dividir el estudio del API JPA en cuatro grandes apartados: 1. 2. 3. 4. Implementacin de modelos de dominio Mapeo Objecto-Relacional Manipulacin de Entidades con el interfaz EntityManager JPQL (Java Persistente Query Language)

3.1. Implementacin de modelos de dominio


Uno de los primeros pasos a la hora de desarrollar una aplicacin de empresa es la creacin de lo que se denomina el modelo de dominio. DEF Bsica: Modelo de Dominio. Un modelo de dominio es una imagen conceptual del problema que tu sistema esta intentando resolver. Est compuesto de las entidades que formarn parte de ese dominio y las relaciones existentes entre ellas. Vamos a utilizar el sitio Web de subastas (escenario tpico de aplicacin Java EE) como guin para introducirnos en el universo de JPA. Nos centraremos en la parte principal de la aplicacin, esto es, la funcionalidad que esta directamente relacionada con la compra y venta de artculos en una subasta online:

La figura muestra la funcionalidad bsica que vamos a cubrir, que se puede resumir en los siguientes puntos: 1. 2. 3. 4. Los Vendedores ponen a subastar un artculo. Los artculos a subastar se organizan en categoras. Los Compradores pujan por algn artculo. El comprador con la puja ms alta, en el momento de finalizar la subasta, gana. Pgina 11 de 80

Persistencia con JPA EJB 3

El primer paso para definir nuestro modelo de dominio es averiguar que entidades forman parte de l. En nuestro caso, el dominio de nuestra aplicacin de ejemplo estar compuesto por las siguientes entidades: Vendedor, Comprador, Artculo, Categora, Puja y Pedido.

El segundo paso es definir las relaciones entre las entidades que forman parte de nuestro dominio:

Este diagrama introduce algunos conceptos ms que aparecen a la hora de definir nuestro modelo de dominio. Los detallamos en el siguiente apartado:

3.1.1. Actores en un modelo de dominio


Se identifican cuatro actores a la hora de definir un modelo de dominio: Entidades u Objetos, Relaciones, Multiplicidad o Cardinalidad y Ordinalidad u Opcionalidad de las relaciones. Pgina 12 de 80

Persistencia con JPA EJB 3

Objectos: desde la perspectiva de un desarrollador Java, los objetos del dominio estn ntimamente relacionados con objetos Java. Como los objetos java, los objetos o entidades del dominio, tienen un comportamiento (mtodos en terminologa Java) y un estado (atributos en Java). Por ejemplo, el objeto de dominio Categora tiene un nombre, fecha de creacin, etc. Adems tendr un comportamiento (por ejemplo, modificar su nombre). Relaciones: En terminologa Java, una relacin entre dos objetos ocurre cuando uno de los objetos tiene una referencia al otro. Si nos fijamos en el diagrama, en la relacin entre el Artculo y el Vendedor, vemos que ambos estn relacionados (un vendedor vende 0 o varios artculos). La direccin de la flecha nos indica dnde reside la referencia. En nuestro caso, el objeto que representa al Vendedor contiene ninguna o varias referencias a objetos de tipo Artculo. Como slo aparece una punta de flecha en la relacin, se dice que la relacin es navegable en una nica direccin direcciones o, ms comnmente, unidireccional. Si la punta de flecha aparece en las dos partes de la relacin, dicha relacin se llama bidireccional, esto es, los objetos relacionados contienen referencias el uno del otro. Multiplicidad o cardinalidad: La cardinalidad indica el nmero de referencias que tiene un objeto en el extremo de una relacin con el del otro extremo. Puede ser de tres tipos: o One-to-One: Cada lado de la relacin puede tener como mximo una referencia al otro objeto de la relacin. Por ejemplo, un Comprador puede tener un objeto Cuenta asociado y el objeto Cuenta pertenece a un nico Comprador. o One-to-many: Una instancia de un objeto puede estar relacionado con mltiples instancias de otro. Por ejemplo, un rticulo puede tener ms de una Puja. Desde el punto de vista de una Puja, la relacin recibe el nombre de many-toone. Por ejemplo, muchas Pujas pueden ser realizadas por un Comprador. o Many-to-many. Cuando ambos lados de la relacin pueden tener ms de un referencia del objeto en el otro extremo. Por ejemplo, un Articulo puede pertenecer a varias Categoras y una Categora puede tener varios Articulos. Ordinalidad u Opcionalidad: La ordinalidad de una relacin determina si una entidad asociada existe. Por ejemplo, tenemos una asociacin one-to-one bidireccional entre un Usuario y su Informacin Bancaria. Un Usuario no siempre tiene por qu tener una Informacin Bancaria asociada, la relacin en este lado es opcional. Sin embargo, si existe una instancia de Informacin Bancara, sta debe estar relacionada obligatoriamente con un Usuario, por tanto, la relacin en este extremo NO es opcional.

3.1.2. Implementacin de los objetos del dominio con JPA


Los objetos que forman parte de nuestro modelo de dominio se construyen en Java como un simple POJO. Por ejemplo, nuestro objeto Categora tendra la siguiente definicin como clase Java: package ejb3inaction.actionbazaar.model; import java.sql.Date; public class Category { protected protected protected protected protected protected Long id; String name; Date modificationDate; Set<Item> items; Category parentCategory; Set<Category> subCategories;

public Category() {} Pgina 13 de 80

Persistencia con JPA EJB 3

public Long getId() { return this.id; } public void setId(Long id) { this.id = id; } public String getName() { return this.name; } public void setName(String name) { this.name = name; } public Date getModificationDate() { return this.modificationDate; } public void setModificationDate(Date modificationDate) { this.modificationDate = modificationDate; } public Set<Item> getItems() { return this.items; } public void setItems(Set<Item> items) { this.items = items; } public Set<Category> getSubCategories() { return this.subCategories; } public void setSubCategories(Set<Category> subCategories) { this.subCategories = subCategories; } public Category getParentCategory() { return this.parentCategory; } public void setParentCategory(Category parentCategory) { this.parentCategory = parentCategory; } }

La Anotacin @Entity Esta anotacin convierte un simple POJO en una entidad, convirtiendo al POJO en un objeto persistente. @Entity public class Category { ... public Category() { ... } public Category(String name) { ... } ... }

Todas las clases que sean marcadas como entidad deben declarar un constructor pblico o protegido sin argumentos. La anotacin @Transient Cuando queremos indicar que un determinado atributo de una entidad no debe ser persistido a la BBDD lo marcamos con la anotacin @Transient, o bien declaramos el atributo con el modificador transient. Por ejemplo, podemos incluir un atributo en la clase Categora llamado Pgina 14 de 80

Persistencia con JPA EJB 3

activeUserCount que almacene el nmero de usuarios activos que actualmente estn navegando a travs de productos de la categora que representa. No tiene sentido almacenar este campo en la BBDD, puesto que es un campo calculado, por tanto, le aadimos la anotacin @Transient. El campo generatedName tampoco debe ser persistido, lo declaramos como transient, que es equivalente a usar la anotacin. @Entity public class Category { ... @Transient protected Long activeUserCount; transient public String generatedName ... public Long getActiveUserCount() { return activeUserCount; } public void setActiveUserCount(Long activeUserCount) { this.activeUserCount = activeUserCount; } ... }

Especificando la identidad de una entidad


Toda entidad del modelo de dominio debe ser identificada unvocamente. Esta restriccin se debe a que toda entidad en el modelo de dominio debe ser persistida, en algn momento, en una fila (o en varias) de una tabla de la Base de Datos y ese registro debe ser identificado de manera nica (mediante su Primary Key). Hay varias maneras de indicar al motor de persistencia subyacente dnde debe almacenarse la identidad de una entidad: Usando la anotacin @Id Usando la anotacin @IdClass Usando la anotacin @EmbeddedId

@Id Usar la anotacin @Id es lo modo ms sencillo de decirle al proveedor de persistencia dnde almacenar la identificacin de la entidad. Esta anotacin marca una propiedad de la clase como el ndice de la entidad. Cualquier tipo primitivo (int, float, double, ) y sus respectivas clases de envoltorio (Integer, Flota, Double, ) puede ser marcado como ndice de la entidad. Tambin los tipos Serializables como String, java.util.Date, java.sql.Date pueden ser marcados como identificadores. NOTA: Evitar el uso de float, Float, double y Double como identificadores. public class Category { ... @Id protected Long id; ... public Long getId() { return this.id; Pgina 15 de 80

Persistencia con JPA EJB 3

} public void setId(Long id) { this.id = id; } ... }

La anotacin @IdClass Si queremos modelar una composite key, es decir, cuando son varios los campos los que, en conjunto, conforman la clave primaria de una entidad, usamos la anotacin @IdClass. Para ello, definiremos una clase con tantos atributos como sean necesarios para identificar la entidad. Es muy importante que esta clase identificadora implemente los mtodos equals y hasCode y la interfaz Serializable. Aqu se muestra un ejemplo, en el cual, la clase Categora ser identificada por los campos name y createDate. Para ello, lo primero que hacemos es crear la clase identificadora con esos dos campos, sobrescribimos los mtodos equals y hashCode y usamos la anotacin @IdClass en nuestra clase Categora para indicar que la clase que acabamos de crear ser su clave primaria. Por ltimo, marcamos los atributos name y createDate de la clase categora con la etiqueta @Id:

public class CategoryPK implements Serializable { String name; Date createDate; public CategoryPK() {} public boolean equals(Object other) { if (other instanceof CategoryPK) { final CategoryPK otherCategoryPK = (CategoryPK)other; return (otherCategory.name.equals(name) && otherCategoryPK.createDate.equals(createDate)); } return false; } public int hashCode() { return super.hashCode(); } } @Entity @IdClass(CategoryPK.class) public class Category { public Category() {} @Id protected String name; @Id protected Date createDate; ... }

El principal problema de esta aproximacin es que tenemos que incluir dos veces los atributos que forman parte de la clave primaria, una vez en la clase que actuar como PK y otra en la propia clase que use dicha clase PK. Pgina 16 de 80

Persistencia con JPA EJB 3

La anotacin @EmbeddedId La ltima forma de establecer la clave primaria de una entidad es mediante esta anotacin. Es muy similar a la anterior, slo que esta vez, en vez de repetir los atributos en ambas clases, se embebe la clase que modela la clave primaria dentro de la entidad. Es muy importante, como en el caso anterior, que la clase que modela la clave primaria implemente los mtodos equals y hasCode NO es necesario, en este caso, que la clase sea Serializable. Adems la clase que hace clave primaria debe ser marcada con la anotacin @Embeddable:

@Embeddable public class CategoryPK { String name; Date createDate; public CategoryPK() {} public boolean equals(Object other) { if (other instanceof CategoryPK) { final CategoryPK otherCategoryPK = (CategoryPK)other; return (otherCategory.name.equals(name) && otherCategoryPK.createDate.equals(createDate)); } return false; } public int hashCode() { return super.hashCode(); } } @Entity public class Category { public Category() {} @EmbeddedId protected CategoryPK categoryPK; ... }

La anotacin @Embeddable Esta anotacin se usa para disear objetos persistentes que no necesitan tener una identificacin, porque son identificados por los objetos entidad que los contienen.

@Embeddable public class Address { protected protected protected protected protected protected ... String String String String String String streetLine1; streetLine2; city; state; zipCode; country;

} @Entity public class User { @Id Pgina 17 de 80

Persistencia con JPA EJB 3

protected protected protected protected

Long id; String username; String firstName; String lastName;

@Embedded protected Address address; protected String email; protected String phone; ... }

Relaciones entre Entidades


En este apartado exploraremos, sin profundizar demasiado, en los tipos de relaciones que se pueden establecer entre entidades y cmo se mapean usando anotacin. Aqu un pequeo resumen: Tipo de relacin One-to-one One-to-many Many-to-one Many-to-many Anotacin @OneToOne @OneToMany @ManyToOne @ManyToMany

La anotacin @OneToOne Esta anotacin sirve para indicar una relacin, ya sea unidireccional o bidireccional de tipo Uno a Uno. Unidirectional one-to-one. Ejemplo: Un Usuario tiene una referencia a un objeto que modela la informacin bancaria, pero no al revs:

@Entity public class User { @Id protected String userId; protected String email; @OneToOne protected BillingInfo billingInfo; } @Entity public class BillingInfo { @Id protected Long billingId; Pgina 18 de 80

Persistencia con JPA EJB 3

protected protected protected protected protected protected protected }

String creditCardType; String creditCardNumber; String nameOnCreditCard; Date creditCardExpiration; String bankAccountNumber; String bankName; String routingNumber;

Bidirectional one-to-one. Ejemplo: Un Usuario tiene una referencia a un objeto que modela la informacin bancaria, y viceversa:

@Entity public class User { @Id protected String userId; protected String email; @OneToOne protected BillingInfo billingInfo; } @Entity public class BillingInfo { @Id protected Long billingId; protected String creditCardType; protected String creditCardNumber; protected String nameOnCreditCard; protected Date creditCardExpiration; protected String bankAccountNumber; protected String bankName; protected String routingNumber; @OneToOne(mappedBy=billingInfo, optional=false); (1) protected User user; } En el punto (1) vemos un par de cosas interesantes. Lo primero, el atributo mappedBy indica al contender que el extremo propietario de la relacin se encuentra en el atributo billingInfo de la clase User. Ms tarde, explicaremos en profundidad el concepto de propietario de la relacin. El otro atributo, optional, con valor a false indica que un objeto de tipo BillingInfo NO puede existir sin un User relacionado.

@OneToMany y @ManyToOne En este tipo de relaciones, una entidad tendr una o ms referencias de otra. En Java, esto significa que una entidad tiene un campo de tipo coleccin (como un java.util.Set o una java.util.List), que almacena mltiples instancias de otra entidad. Por otro lado, si la asociacin entre las dos entidades en bidireccional, un extremo de la relacin es uno-a-muchos y el extremo opuesto de la misma es muchos-a-uno. Pgina 19 de 80

Persistencia con JPA EJB 3

@Entity public class Item { @Id protected Long itemId; protected String title; protected String description; protected Date postdate; ... @OneToMany(mappedBy="item") protected Set<Bid> bids; ... } @Entity public class Bid { @Id protected Long bidId; protected Double amount; protected Date timestamp; ... @ManyToOne protected Item item; ... }

Como ya se mencion en el apartado dedicado a las relaciones uno-a-uno, el atributo mappedBy indica que parte es la propietaria de la relacin. La anotacin @ManyToOne en la variable item de la clase Bid le dice al proveedor de persistencia que ms de una entidad Bid puede mantener referencias a la misma instancia de Item. Para relaciones uno-a-muchos, el lado ManyToOne es siempre el propietario de la relacin, por eso el atributo mappedBy no existe en la definicin de la anotacin @ManyToOne. La anotacin @ManyToMany Sin ser tan comunes como las relaciones uno-a-muchos, las relaciones muchos-a-muchos pueden darse con bastante en frecuencia en aplicaciones de empresa. En este tipo de relaciones, ambos lados tienen mltiples referencias a las entidades relacionadas.

@Entity public class Category { @Id Pgina 20 de 80

Persistencia con JPA EJB 3

protected Long categoryId; protected String name; ... @ManyToMany //propietario protected Set<Item> items; ... } @Entity public class Item { @Id protected Long itemId; protected String title; ... @ManyToMany(mappedBy=items) //subordinado protected Set<Category> categories; ... }

Pgina 21 de 80

Persistencia con JPA EJB 3

3.2. Mapeo Objeto-Relacional


En esta seccin veremos todo lo relativo al proceso de mapeo de objetos a tablas, ampliando la informacin que vimos en la seccin anterior.

3.2.1. Mapeando Entidades


En este apartado exploraremos las caractersticas fundamentales del O/R mapping en EJB3, para ello nos centraremos en la implementacin de la clase User, perteneciente a nuestro escenario de ejemplo (el sitio Web de subastas). Esta clase ser la superclase de nuestros objetos de dominio Seller (Vendedor) y Bidder (Comprador).

Para hacer el ejemplo ms simple, de momento ignoraremos la implementacin de la herencia, aadiendo un campo a la clase User que indique el tipo de usuario del que se trata una determinada instancia. Ms tarde, cuando mostremos como se mapea la herencia, haremos una nueva implementacin de la clase. La entidad User contiene campos que son comunes a todos los tipos de usuario en nuestra aplicacin, como por ejemplo, el ID de usuario, el nombre de usuario, primer apellido, segundo apellido, tipo de usuario (vendedor, comprador, administrador, etc.), foto de usuario y fecha de creacin de la cuenta de usuario. A continuacin, vemos como quedara mapeada nuestra entidad User:

@Entity @Table(name="USERS") @SecondaryTable(name="USER_PICTURE", pkJoinColumns=@PrimaryKeyJoinColumn(name="USER_ID")) public class User implements Serializable { @Id @Column(name="USER_ID", nullable=false) protected Long userId; @Column(name="USER_NAME", nullable=false) (1) protected String username; @Column(name="FIRST_NAME", nullable=false, length=1) protected String firstName; @Column(name="LAST_NAME", nullable=false) protected String lastName; Pgina 22 de 80

Persistencia con JPA EJB 3

@Enumerated(EnumType.ORDINAL) (2) @Column(name="USER_TYPE", nullable=false) protected UserType userType; @Column(name="PICTURE", table="USER_PICTURE") @Lob (3) @Basic(fetch=FetchType.LAZY) (4) protected byte[] picture; @Column(name="CREATION_DATE", nullable=false) @Temporal(TemporalType.DATE) (5) protected Date creationDate; @Embedded (6) protected Address address; public User() {} } @Embeddable (7) public class Address implements Serializable { @Column(name="STREET", nullable=false) protected String street; @Column(name="CITY", nullable=false) protected String city; @Column(name="STATE", nullable=false) protected String state; @Column(name="ZIP_CODE", nullable=false) protected String zipCode; @Column(name="COUNTRY", nullable=false) protected String country; }

Vamos a comentar brevemente lo que vemos en el ejemplo, para luego profundizar ms en cada una de las anotaciones usadas. Lo primero que vemos es que la entidad User se mapea a una tabla llamada USERS ms otra tabla llamada USER_PICTURES, unidas mediante la PK USER_ID. Cada uno de los campos de la entidad se mapea a una columna de la tabla en BBDD mediante la anotacin @Column (1). El campo userType se mapea como un tipo enumerado (2). La imagen es mapeada como un BLOB (Binary Large Object) (3) que se recupera de manera perezosa (lazy loading) (4). La fecha de creacin se marca como un tipo temporal (una fecha) (5). El campo addres es un objeto embebido (6). El mapeo a columnas de un objeto de tipo Address se define en el objeto (7) usando la anotacin @Column. En resumen, hemos usado todas estas anotaciones ORM: @Table, @SecondaryTable, @Column, @Enumerated, @Lob, @Basic, @Temporal, @Embedded y @Embeddable. Comenzaremos nuestro anlisis con la anotacin @Table.

Especificando la tabla La anotacin @Table especifica la tabla que contiene las columnas a las que la entidad ser mapeada. En el ejemplo indicamos que las entidades User se mapean a una tabla con nombre USERS, especificndolo a partir del atributo name de la anotacin. Pgina 23 de 80

Persistencia con JPA EJB 3

@Target(TYPE) @Retention(RUNTIME) public @interface Table { String name() default ""; String catalog() default ""; String schema() default ""; UniqueConstraint[] uniqueConstraints() default {}; }

Los atributos catalog y schema especifican el nombre del catlogo y del esquema de bases de datos, respectivamente. Por defecto, se asume que la tabla pertenece al esquema usado en la definicin del data source. Mas adelante, veremos cmo se configura el data source. En la mayora de los casos no es necesario usar estos atributos. Por ltimo, el parmetro uniqueConstraints nos permite declarar restricciones en las columnas de una tabla. Equivale a una sentencia UNIQUE CONSTRAINT en SQL. Por ejemplo: @Table(name="CATEGORIES", uniqueConstraints= {@UniqueConstraint(columnNames={"NAME"})})

En este caso, le decimos al proveedor de persistencia que, cuando cree la tabla CATEGORIES aada una restriccin a la columna NAME, de manera que no permita que dos filas en dicha tabla tengan el mismo valor para esa columna. Es lo que, en el mundo SQL, se conoce clave alternativa. En el parmetro columnNames, podemos especificar cualquier nmero de columnas, separadas con comas, indicando que lo que ha ser nico, es la tupla formada por la suma de dichas columnas. NOTA: la anotacin @Table es opcional, si no se especifica, el proveedor de persistencia mapear la entidad a una tabla cuyo nombre es el mismo que el de la entidad mapeada. Mapeando las columnas La anotacin @Column mapea un campo o propiedad de la entidad a una columna de la tabla. Por ejemplo, el campo userId se mapea a la columna USER_ID:

@Column(name="USER_ID") protected Long userId;

Se asume que la columna USER_ID pertenece a la tabla especificada por la anotacin @Table. En ocasiones necesitamos especificar a que tabla pertenece la columna, por ejemplo cuando mapeamos una entidad a varias tablas (usando la anotacin @SecondaryTable), como hicimos en el caso del campo picture:

@Column(name="PICTURE", table="USER_PICTURE") ... protected byte[] picture;

Estudiemos con ms detenimiento la declaracin de la anotacin @Column:

@Target({METHOD, FIELD}) Pgina 24 de 80

Persistencia con JPA EJB 3

@Retention(RUNTIME) public @interface Column { String name() default ""; boolean unique() default false; // especifica restriccin de unicidad boolean nullable() default true; // especifica si la columna permite nulos boolean insertable() default true; boolean updatable() default true; String columnDefinition() default ""; String table() default ""; int length() default 255; // longitud de la columna int precision() default 0; // precisin decimal de la columna int scale() default 0; // escala decimal }

El parmetro insertable, con un valor igual a false, indica al proveedor de persistencia que el campo no ha ser incluido en la sentencia INSERT cuando se proceda a crear el registro correspondiente a la entidad a persistir. Similarmente, el parmetro updatable, con un valor igual a false excluye el campo en una sentencia UPDATE. Usando @Enumerated Los tipos enumerados son permitidos desde Java 5. En nuestro ejemplo, el campo userType es un tipo enumerado, que en Java se define de la siguiente manera: public enum UserType {SELLER, BIDDER, CSR, ADMIN}; Cada elemento en el tipo enumerado tiene asociado un ndice, que se conoce con el nombre de ordinal. As, el valor UserType.SELLER tiene un valor ordinal igual a 0, el UserType.BIDDER igual a 1, y as sucesivamente. El problema viene a la hora de determinar como almacenar el valor en la columna. En nuestro caso, hemos especificado que el valor del ordinal sea el que se guarde en la tabla: @Enumerated(EnumType.ORDINAL) ... protected UserType userType; Esto quiere decir que si el valor del campo userType, para una determinada instancia de la entidad User, es UserType.SELLER, el valor 0 ser almacenado en la columna correspondiente de la tabla. De manera alternativa, podemos especificar que el valor se almacene como un String: @Enumerated(EnumType.STRING) ... protected UserType userType; As, en lugar de almacenar el valor 0, se almacenar el valor SELLER El comportamiento por defecto, cuando no se especifica el modo en que se almacenarn los tipos enumerados, es guardar el ordinal. Mapeo de CLOBs y BLOBs Una de las ms caractersticas ms potentes de las bases de datos relacionales es la habilidad para almacenar datos de gran tamao, gracias a los tipos BLOB y CLOB. La anotacin @Lob marca una propiedad como un CLOB o BLOB. En nuestro ejemplo tenemos: Pgina 25 de 80

Persistencia con JPA EJB 3

@Lob @Basic(fetch=FetchType.LAZY) protected byte[] picture;

Un campo anotado con @Lob, ser almacenado como CLOB o BLOB de pendiendo del tipo de dato que sea. Si el campo es de tipo char[] o String el proveedor de persistencia almacenar el campo como un CLOB. En cualquier otro caso, la columna se mapear como BLOB. Una anotacin muy til que se usa junto con @Lob es @Basic. Esta anotacin se puede usar con cualquier atributo que tenga un mapeo directo a un campo. En este caso, indicamos que el campo picture debe recuperarse desde la base de datos slo cuando se acceda a l directamente la primera vez. Esto es lo que se llama recuperacin perezosa (lazy loading). Discutiremos este concepto ms adelante. Por el momento, decir que es muy importante indicar al proveedor de persistencia que los campos marcados como @Lob sean recuperados de manera perezosa nicamente cuando sea necesaria su carga, pues es una operacin muy costosa con un consumo de memoria intensivo. Mapeo de tipos temporales (Fechas y Horas) La mayora de bases de datos incluyen soporte para varios tipos de datos temporales con diferentes niveles de granularidad. Los ms comunes son: DATE: almacena el da, mes y ao. TIME: almacena slo la hora. TIMESTAMP: almacena el da, mes, ao y la hora.

Para mapear un tipo de datos temporal en Java (java.util.Date o java.util.Calendar) a uno de los tipos definidos en SQL usamos la anotacin @Temporal, indicando a que tipo SQL corresponde (TemporalType.DATE, TemporalType.TIME, TemporalType.TIMESTAMP): @Temporal(TemporalType.DATE) protected Date creationDate; Si no especificamos ningn tipo, el proveedor de persistencia asume el tipo TIMESTMP. Mapeando una entidad a mltiples tablas En nuestro ejemplo, hemos indicado que nuestra entidad User sea mapeada en dos tablas, una de las cuales albergar la imagen asociada con un usuario. Esto se consigue, gracias a la anotacin @SecondaryTable. No suele ser muy habitual hacer esto, pero en algunos casos, como ste, puede mejorar bastante el rendimiento. Veamos como se traducira nuestro ejemplo en tablas:

Pgina 26 de 80

Persistencia con JPA EJB 3

@Target({TYPE}) @Retention(RUNTIME) public @interface SecondaryTable { String name(); String catalog() default ""; String schema() default ""; PrimaryKeyJoinColumn[] pkJoinColumns() default {}; UniqueConstraint[] uniqueConstraints() default {}; }

Como vemos, la definicin de la anotacin es muy similar a la @Table. El nico parmetro que no tiene esta ltima y que, adems, es muy importante para @SecodanryTable es pkJoinColumns. Recordemos como usamos esta anotacin en nuestra entidad User: @Entity @Table(name="USERS") @SecondaryTable(name="USER_PICTURE", pkJoinColumns=@PrimaryKeyJoinColumn(name="USER_ID")) public class User implements Serializable { Obviamente, la dos tablas, USER y USER_PICTURES estan relacionadas y deben ser unidas para crear nuestra entidad User. Esta clase de relacin se implementa creando una clave extranjera en la tabla secundaria que referencia a la clave principal de la primera tabla. En este caso, adems, la clave extranjera de la tabla secundaria tambin es su clave principal.

Pgina 27 de 80

Persistencia con JPA EJB 3

3.2.2. Generacin de claves primarias


Cuando identificamos una columna o un conjunto de columnas como la clave primaria, lo que hacemos esencialmente, es pedirle a la base de datos que garantize la unicidad. Las claves primarias que estn formadas por datos de la lgica del negocio se denominan natural keys. Un ejemplo clsico, sera el nmero de DNI de un empleado. El ID del empleado, por otro lado, es lo que se conoce como surrogate keys (clave sustituta). Esencialmente, las claves sustitutas son columnas creadas explcitamente para funcionar como claves primarias. Son altamente recomendables, sobre todo cuando debemos trabajar con claves compuestas. Hay varias maneras de generar los valores para las claves primarias, todas soportadas por la anotacin @GeneratedValue: identidad, secuencias y tablas. Columnas de identidad como generadores

@Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="USER_ID") protected Long userId;

Usando la estrategia de generacin IDENTITY, el valor de la clave primaria ser gestionado por la base de datos en el momento que un registro guardado sea comitido. Por tanto, con esta estrategia no debe manipularse directamente el campo que acta como clave primaria. Secuencias como generadores Para usar la estrategia de secuencias en la generacin de claves primarias, primero debemos definir una secuencia en la base de datos. Por ejemplo: CREATE SEQUENCE USER_ SEQUENCE START WITH 1 INCREMENT BY 10; Ahora podemos crear nuestro generador de claves mediante la secuencia de la siguiente manera: @SequenceGenerator(name="USER_SEQUENCE_GENERATOR", sequenceName="USER_SEQUENCE", initialValue=1, allocationSize=10) Esta anotacin crea un generador de secuencias con nombre USER_SEQUENCE_GENERATOR, que referencia a la secuencia que creamos en nuestra base de datos (USER_SEQUENCE). El elmento initialValue indica desde que valor comienza la secuencia, y el elemento allocationSize canto ha de incrementarse la secuencia cada vez que se genere un valor. No es necesario declarar el generador de secuencias en la clase que la vaya a usar, pues esta declaracin es compartida por todas las entidades en el mdulo de persistencia, por tanto, es importante que el nombre con que declaremos cada uno de estos generadores sea nico. Por ltimo, podemos usar nuestro generador de la siguiente manera:

@Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="USER_SEQUENCE_GENERATOR") @Column(name="USER_ID") protected Long userId; Tablas de secuencias como generadores Pgina 28 de 80

Persistencia con JPA EJB 3

Lo primero que debemos hacer es crear la tabla a usar para la generacin de los valores. Por ejemplo: CREATE TABLE sequence_generator_table (sequence_name VARCHAR2(80) NOT NULL, sequence_value NUMBER(15) NOT NULL, PRIMARY KEY (sequence_name)); El campo sequence_name almacenar el nombre de la secuencia y la columna sequence_value almacena el valor actual de la secuencia. El siguiente pase es preparar la tabla insertando los valores iniciales a mano: INSERT INTO sequence_generator_table (sequence_name, sequence_value) VALUES ('USER_SEQUENCE', 1); La ventaja de esta aproximacin frente a la anterior es que podemos almacenar varias secuencias de nuestra aplicacin en la misma tabla. El siguiente paso es crear la tabla generadora usando la anotacin @TableGenerator: @TableGenerator (name="USER_TABLE_GENERATOR", table="SEQUENCE_GENERATOR_TABLE", pkColumnName="SEQUENCE_NAME", valueColumnName="SEQUENCE_VALUE", pkColumnValue="USER_SEQUENCE") Si es necesario, tambin puedes especificar los valores para initialValue y allocationSize. Finalmente, podemos usar la tabla generadora para obtener los valores del USER_ID:

@Id @GeneratedValue(strategy=GenerationType.TABLE, generator=USER_TABLE_GENERATOR) @Column(name="USER_ID") protected Long userId;

Estrategia de generacin de claves primarias por defecto La ltima opcin es permitir al proveedor de persistencia decidir la mejor estrategia de generacin de claves en funcin de la base de datos subyacente, usando AUTO:

@Id @GeneratedValue(strategy=GenerationType.AUTO) @Column(name="USER_ID") protected Long userId;

Pgina 29 de 80

Persistencia con JPA EJB 3

Mapeando clases embebibles Como ya explicamos anteriormente, los objetos imbebibles actan, principalmente, como contenedores de datos para las entidades y no tienen identidad por s mismos. Es ms, comparten la identidad de la entidad a la que pertenecen.

@Table(name="USERS") ... public class User implements Serializable { @Id @Column(name="USER_ID", nullable=false) protected Long userId; ... @Embedded protected Address address; ... } ... @Embeddable public class Address implements Serializable { @Column(name="STREET", nullable=false) protected String street; ... @Column(name="ZIP_CODE", nullable=false) protected String zipCode; ... }

El resultado de la definicin que vemos en el ejemplo es que los datos del objeto Address se almacenarn en la misma tabla que los datos del objeto User, y ambos objetos compartirn la clave primaria USER_ID:

Pgina 30 de 80

Persistencia con JPA EJB 3

Lo ms importante de los objetos embebibles es que pueden ser compartidos entre entidades.

3.3. Mapeando relaciones entre entidades


3.3.1. Mapeando relaciones uno-a-uno
Las relaciones uno-a-uno se mapean usando asociaciones de claves primarias/extranjeras. Las realaciones uno-a-uno tienen, generalmente, un comportamiento de relacin padre-hijo. Por ejemplo, en la relacin entre User y BillingInfo la entidad User podra ser caracterizada como el padre en la relacin. Dependiendo de dnde reside la clave extranjera, la relacin puede ser implementada de dos maneras diferentes: usando la anotacin @JoinColumn, o la anotacin @PrimaryKeyJoinColumn: Usando @JoinColumn Usaremos esta anotacin cuando la clave extranjera resida en la tabla padre:

Pgina 31 de 80

Persistencia con JPA EJB 3

@Entity @Table(name=USERS) public class User { @Id @Column(name=USER_ID) protected String userId; ... @OneToOne @JoinColumn(name=USER_BILLING_ID, referencedColumnName=BILLING_ID, updatable=false) (1) protected BillingInfo billingInfo; } @Entity @Table(name=BILLING_INFO) public class BillingInfo { @Id @Column(name=BILLING_ID) (2) protected Long billingId; ... }

(1) El parmetro name indica el nombre de la clave extranjera en la tabla USER. Si se omite este elemento, se asume uno con la siguiente estructura: <nombre de la propiedad de la relacin>_<nombre de la columna PK referenciada> En nuestro caso, si hubieramos BILLINGINFO_BILLING_ID. omitido el valor, ste hubiera sido el siguiente:

El elemento referencedColumnName especifica el nombre la clave primaria de la entidad referenciada. Si no se especifica, se asume el la columna que contiene la identidad de la entidad referenciada. En nuestro caso, hubiera sido correcto, ya que BILLING_ID en la clave primaria de la tabla BILLING_INFO (2) Pgina 32 de 80

Persistencia con JPA EJB 3

Si tuviramos una relacin uno-a-uno bidireccional, entonces el lado inverso de la relacin deber incluir el parmetro mappedBy. Si User y BillingInfo tuvieran una relacin bidireccional, deberamos modificar la entidad BillingInfo de la siguiente manera:

@Entity public class BillingInfo { @OneToOne(mappedBy=billingInfo) protected User user; .. }

El parmetro mappedBy identifica el nombre de la asociacin en el lado propietario de la relacin. En una relacin bidireccional, el lado propietario de la relacin es la entidad que almacena dicha relacin en su tabla subyacente. En nuestro caso, la tabla USERS almacena la relacin en la columna USER_BILLING_ID y, por tanto, la entidad User es la propietaria de la relacin. Usando @PrimaryKeyJoinColumn

En este caso, ambas tablas (USERS y BILLING_INFO) comparten la misma clave primaria. La clave primaria de la tabla BILLING_INFo es tambin una clave extranjera que referencia a la clave primaria de la tabla USERS. Este tipo de asociacin se mapea usando la anotacin @PrimaryKeyJoinColumn:

@Entity @Table(name=USERS) public class User { Pgina 33 de 80

Persistencia con JPA EJB 3

@Id @Column(name=USER_ID) protected Long userId; ... @OneToOne @PrimaryKeyJoinColumn(name=USER_ID, referencedColumnName=BILLING_USER_ID) protected BillingInfo billingInfo; } @Entity @Table(name=BILLING_INFO) public class BillingInfo { @Id @Column(name=BILLING_USER_ID) protected Long userId; ... }

El elemento name de la anotacin se refiere al nombre de la columna que almacena la clave primaria. El elemento referencedColumnName se refiere a la columna que almacena la clave extranjera en la tabla que mantiene a la entidad referenciada (BillingInfo). En nuestro caso, la clave extranjera es la columna BILLING_USER_ID de la tabla BILLING_INFO, que apunta a la clave primaria de la tabla USERS (USER_ID)

3.3.2. Mapeando las relaciones uno-a-muchos y muchos-a-uno


Como ya mencionamos anteriormente, las relaciones uno-a-muchos y las relaciones muchosa-uno son el tipo ms comn de relaciones que se dan en una aplicacin de empresa y se implementan usando las anotaciones @OneToMany y @ManyToOne, respectiavamente. Por ejemplo, la relacin entre Item y Bid en nuestro escenario de ejemplo es una relacin uno-amuchos, pues Item mantiene referencias a una coleccin de Bid realizadas para dicho Item y Bid mantiene una referencia al Item por el que se ha pujado. Al igual que en las relaciones uno-a-uno, este tipo de relaciones se basan en una asociacin clave primaria/clave extranjera en la base de datos subyacente. Por tano, podemos aplicar las mismas dos anotaciones que vimos al describir las relaciones uno-a-uno. Veamos como implementar la relacin Item-Bid:

@Entity @Table(name="ITEMS") public class Item { @Id @Column(name="ITEM_ID") protected Long itemId; ... @OneToMany(mappedBy="item") (1) protected Set<Bid> bids; ... } @Entity @Table(name="BIDS") public class Bid { @Id @Column(name="BID_ID") Pgina 34 de 80

Persistencia con JPA EJB 3

protected Long bidId; ... @ManyToOne @JoinColumn(name="BID_ITEM_ID", referencedColumnName="ITEM_ID") (2) protected Item item; ... }

Como mltiples registros de la tabla BIDS pueden referirse al mismo registro en la tabla ITEMS, la tabla BIDS mantendr una clave extranjera que referencia a la clave primaria de la tabla ITEMS. En nuestro ejemplo, la clave extranjera es BID_ITEM_ID y referencia a la clave primaria ITEM_ID de la tabla ITEM:

La relacin muchos-a-uno es expresada usando la anotacin @JoinColumn (2). En efecto, el trabajo de esta anotacin es especificar una relacin clave primaria / clave extranjera en el modelo de datos subyacente. El elemento name de la anotacin @ManyToOne especifica el nombre de la clave extranjera, BID_ITEM_ID y referencedColumnName especifica la clave primaria a la que referencia, ITEM_ID. En lugar de repetir la anotacin @JoinColumn para implementar la relacin en el lado del Item, tenemos que usar el elemento mappedBy. Esto lo que hace, esencialmente, es apuntar a la definicin hecha con @JoinColumn en el campo Bid.item. En las relaciones uno-a-muchos bidireccionales, el propietario de la relacin es la entidad que almacena la clave extranjera (el lado muchos de la relacin). Para finalizar, recordemos que una clave extranjera puede apuntar a una clave primaria de la misma tabla en la que reside dicha clave extranjera. Tenemos este tipo de relacin muchos-auno entre las categoras y subcategoras a las que pertenecen los artculos que se ponen en subasta. Podemos usar la anotacin @JoinColumn de la siguiente manera para implementar esta relacin:

@Entity @Table(name="CATEGORIES") public class Category implements Serializable { Pgina 35 de 80

Persistencia con JPA EJB 3

@Id @Column(name=CATEGORY_ID) protected Long categoryId; ... @ManyToOne @JoinColumn(name="PARENT_ID", referencedColumnName=CATEGORY_ID") Category parentCategory; ... }

En este ejemplo, la entidad Category referencia a su padre a travs de la clave extranjera PARENT_ID, apuntando a la clave primaria de otro registro en la tabla CATEGORY.

3.3.3. Mapeando relaciones Muchos-a-muchos


En el mundo de las bases de datos relacionales, las relaciones muchos-a-muchos se implementan dividiendo stas en dos relaciones uno-a-muchos almacenadas en una tabla de asociacin o tabla join. En otras palabras, una tabla join te permite acceder directamente a las claves primarias de cada lado de la relacin, almacenado pares arbitrarios de claves extranjeras en una fila:

La anotacin @JoinTable modela esta tcnica. Como ejemplo, mostraremos la relacin entre las entidades Item y Category. Recordemos que una Categora puede contener mltiples artculos y un Artculo puede pertenecer a varias Categoras:

@Entity @Table(name="CATEGORIES") public class Category implements Serializable { @Id @Column(name="CATEGORY_ID") protected Long categoryId; @ManyToMany @JoinTable(name="CATEGORY_ITEMS", joinColumns = @JoinColumn(name="CI_CATEGORY_ID", referencedColumnName="CATEGORY_ID"), inverseJoinColumns = @JoinColumn(name="CI_ITEM_ID", referencedColumnName="ITEM_ID") ) protected Set<Item> items; ... } Pgina 36 de 80

Persistencia con JPA EJB 3

@Entity @Table(name=ITEMS) public class Item implements Serializable { @Id @Column(name=ITEM_ID) protected Long itemId; ... @ManyToMany(mappedBy=items) protected Set<Category> categories; ... }

El elemento name de la anotacin @JoinColumn especifica el nombre de la tabla join, en nuestro caso CATEGORIES_ITEMS. Esta tabla tiene slo dos columnas: CI_CATEGORY_ID y CI_ITEMS_ID. La primera columna es una clave extranjera que referencia a la clave primaria de la tabla CATEGORIES, mientras que la segunda columna es una clave extranjera que referencia a la clave primaria de la tabla ITEMS. Los elementos joinColumns y inverseJoinColumns indican esto. Cada uno de estos dos elementos describen una condicin de join en cada lado de la relacin muchos-a-muchos. El elemento joinColumns describe el propietario de la relacin y el elemento inverseJoinColumns describe el subordinado. En este tipo de relaciones la distincin del lado propietario de la relacin es puramente arbitraria. El cdigo de ejemplo muestra la implementacin de una relacin muchos-a-muchos bidireccional (debido a que en uno de los lados hemos utilizados el elemento mappedBy). Para implementar una relacin del mismo tipo pero unidireccional, bastara con omitir el elemento mappedBy.

3.4. Mapeando la Herencia


En este apartado veremos como mapear los varios tipos de usuarios que tenemos en nuestra aplicacin de ejemplo. Recordemos que contamos con dos tipos de usuarios, los vendedores y los compradores y una superclase User que mantendr los datos y comportamiento comunes a todos los tipos de usuarios. Por su parte, los compradores y vendedores heredarn estos datos y comportamiento comn y mantendrn datos y comportamiento especfico a cada uno de ellos.

Para mapear la herencia existen tres tipos de estrategias, que veremos por separado: Mapeo en una sola tabla Mapeo en tablas join Tabla por clase Pgina 37 de 80

Persistencia con JPA EJB 3

3.4.1. Estrategia Single-Table


En la estrategia sigle-table, todas las clases en la jerarqua hereditaria se mapean a una sola tabla. Esto significa que la tabla contendr un conjunto con todos los datos alamcaneados en la jerarqua de clases. Los diferentes objetos en la jerarqua OO se diferencian entre s mediante una columna especial denominada discrimininador.

Como se muestra en la figura, la tabla USERS contiene datos comunes a todos los tipos usuarios (USER_ID y USER_NAME), datos especifcos de los Compradores (BID_FRECUENCY) y los datos especficos de los vendedores (CREDIT_WORTH). Los registros 1 y 2 representan compradores, mientras que el 3 representa a un vendedor. Esto viene indicado por la columna USERT_TYPE, que acta como la columna discriminadora. Un valor B, indica que el registro mapea un usuario de tipo Comprador, mientras que un valor S indica que el registro representa a un usuario de tipo Vendedor. El proveedor de persistencia mapea cada tipo de usuario a la tabla, almacenando los datos persistentes de cada uno de los tipos en las columnas correspondientes, estableciendo el valor de la columna USER_TYPE aplicable y dejando el resto de columnas a NULL. Para implementar este tipo de estrategia debemos hacerlo de la siguiente manera:

@Entity @Table(name="USERS") @Inheritance(strategy=InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name=USER_TYPE, discriminatorType=DiscriminatorType.STRING, length=1) (1) public abstract class User ... @Entity @DiscriminatorValue(value=S) (2) public class Seller extends User ... @Entity @DiscriminatorValue(value=B) (3) public class Bidder extends User

Lo primero es especificar la estrategia de mapeo a usar y el nombre y tipo de la columna discriminadora en la raz de la jerarqua OO (1). Para indicar el tipo de estrategia usamos la anotacin @Inheritance. Con la anotacin @DiscriminatorColumn, indicamos el nombre de la columna discriminadora, el tipo y la longitud. La clase seller especifica que su valor de discriminacin es S, a travs de la anotacin @DiscriminatorValue (2), por su parte, en la clase Bidder se especifica un valor igual a B (3).

Pgina 38 de 80

Persistencia con JPA EJB 3

La estrategia sinble-table es la estrategia de mapeo de la herencia por defecto en EJB3. Aunque es una estrategia simple de usar, tiene una gran desventaja: no utiliza el poder de las bases de datos relacionales en trminos de uso de claves primarias / Extranjeras y adems, acaba habiendo una gran nmero de columnas con valor a NULL.

3.4.2. Estrategia Joined-tables


Este tipo de estrategia utiliza relaciones uno-a-uno para modelar la jerarqua OO. Esta estrategia supone, por un lado, la creacin de tablas separadas por cada entidad en la jerarqua y, por otro, relacionar a los descendientes directos en la herencia con relaciones uno-a-uno. En esta estrategia, el padre en la jerarqua contiene slo las columnas comunes a sus hijos.

En la figura, vemos que la tabla USERS contiene las columnas comunes a todos los tipos de usuarios (USER_ID y USERNAME). Cada tabla hija en la herencia contiene columnas especficas a cada tipo de usuario. Como vemos, tanto la tabla BIDDER, como la tabla SELLER contienen columnas que son especficas a las entidades Bidder y Seller, respectivamente. La jerarqua OO padre-hijo se implementa usando relaciones uno-a-uno. El valor discriminador en la tabla USERS todava se debe seguir usando, principalmente como una manera de, fcilmente, diferenciar los tipos de usuarios en la herencia. En el siguiente cuadro vemos como implmentar esta estrategia:

@Entity @Table(name="USERS") @Inheritance(strategy=InheritanceType.JOINED) @DiscriminatorColumn(name=USER_TYPE, discriminatorType=STRING, length=1) public abstract class User ... @Entity @Table(name="SELLERS") @DiscriminatorValue(value="S") @PrimaryKeyJoinColumn(name="USER_ID") public class Seller extends User ... @Entity @Table(name="BIDDERS") @DiscriminatorValue(value="B") @PrimaryKeyJoinColumn(name="USER_ID") public class Seller extends User ... Pgina 39 de 80

Persistencia con JPA EJB 3

Esta estrategia es, probablemente, la mejor eleccin para mapear la herencia desde una perspectiva de diseo, ya que no llena la tabla de valores NULL y cumple la 3 FN. Desde una perspectiva de rendimiento, es peor que la estrategia single-table porque requiere de varias joins para querys polimrficas.

3.4.3. Estrategia Table-per-class


Este tipo de estrategia es, probablemente, la ms simple de entender para cualquier persona. Sin embargo, esta estrategia es la peor desde un punto de vista tanto relacional, como OO. En esta estrategia, la superclase y las subclases de la herencia se almacenan cada una en su propia tabla, sin ningn tipo de relacin entre ellas:

Como muestra la figura, los datos de las entidades se almacenan en sus propias tablas. Esto ocurre incluso para el valor de la PK, USER_ID, lo que implica que las PK en todas las tablas deben ser mutuamente exclusivas para que este esquema funcione. Adems, las columnas heredadas se duplican en todas las tablas, como ocurre con la columna USERNAME. Veamos como implementar esta estrategia:

@Entity @Table(name="USERS") @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) public class User { ... @Entity @Table(name="SELLERS") public class Seller extends User { ... @Entity @Table(name="BIDDERS") public class Bidder extends User { Pgina 40 de 80

Persistencia con JPA EJB 3

Como vemos, la estrategia usada se especifica en la entidad que acta como superclase. Todas las subclases en la jerarqua se mapean a tablas separadas usando la anotacin @Table. La gran desventaja de usar este tipo de mapeo es que no tiene un buen soporte para querys polimrficas, siendo necesarias SQL UNIONs, o bien, la recuperacin de cada entidad con sentencias SQL separadas para cada subclase en la herencia. Es una estrategia difcil de implementar para los proveedores de persistencia. Por esto, la especificacin EJB3 ha hecho que sea opcional soportar este tipo de estrategia para los proveedores. A continuacin, mostramos una tabla resumen con las caractersticas de cada tipo de estrategia: Caracterstica Single Table
Una tabla para todas las clases en la herencia: Columnas obligatorias pueden tener valores NULL La tabla crece cuando se aaden ms subclases Si

Joined Tables
Una para la clase padre y una para cada subclase donde se almacenan las propiedades polimrficas. Las tablas de mapeo estn normalizadas. Si

Table per class

Traduccin a Tablas

Una por cada clase en la jerarqua d entidades.

Usa columnas discriminadoras? SQL generado para recuperar la jerarqua de entidades

No Una SELECT pos cada sublace o UNION de SELECTs

Una SELECT

Clausula SELECT con mltiples JOINS

SQL generado para insertar y actualizar

Simple INSERT o UPDATE para todas las entidades en la jerarqua Buena Buena Requerido

Mltiples INSERT, UPDATE: uno para clase padre y una por cada subclase Buena Buena Requerido Pobre Pobre Opcional

Relacin polimrfica Querys polimrficas Soportado en EJB3 JPA?

3.5. Manipulando entidades: el interfaz EntityManager


3.5.1. El interfaz EntityManager
El API del EntityManager es la caracterstica ms importante e interesante del API de JPA. Este interfaz acta como puente entre el mundo OO y el mundo relacional; nos permite realizar las operaciones CRUD (Create, Read, Update y Delete) sobre los objetos de nuestro modelo de dominio. Interpreta la configuracin ORM (nuestras anotaciones de mapeo) y lo traduce en en lenguaje SQL de la base de datos subyacente:

Pgina 41 de 80

Persistencia con JPA EJB 3

Cuando solcitamos que una entidad de nuestro modelo de dominio sea creada, el EntityManager traduce la entidad a un nuevo registro en la BD. Cuando solicitamos que una entidad sea actualizada, averigua los datos relacionales que corresponden a la entidad y los actualiza. De la misma manera, el EntityManager elimina los datos de una entidad cuando solicitamos que dicha entidad sea borrada. Por ltimo, si queremos recuperar una entidad, el EntityManager lee los datos relacionados asociados con dicha entidad, rellena la entidad y devuelve la entidad al mundo OO. En la siguiente tabla vemos un resumen de los mtodos ms usados del interfaz EntityManager: Mtodo public void persist(Object entity); Descripcin Graba (persiste) una entidad en la base de datos y marca la entidad como gestionada. Mezcla la entidad con el contexto de persistencia del EntityManager y devuelve la entidad mezclada. Actualiza el estado de la entidad en BD Elimina una entidad de la BD Encuentra una instancia de una entidad a travs de su PK Sicroniza el estado de las entidades en el contexto de persistencia del EntityManager con la BD Cambia el modo de flush del contexto de persistencia del EntityManager. El modo flush puede ser AUTO o COMMIT. Por defecto el modo esta establecido a AUTO, lo que significa que el EntityManager intenta sincronizar las entidades con la BD automticamente. Recupera el modo flush actual. Refresca (resetea) la entidad desde la BD Crea una query dinmica usando una sentencia JPQL. Pgina 42 de 80

public <T> T merge(T entity);

public void remove(Object entity); public <T> T find(Class<T> entityClass, Object primaryKey); public void flush();

public void setFlushMode(FlushModeType flushMode);

public FlushModeType getFlushMode();

public void refresh(Object entity);

public Query createQuery(String jpqlString);

Persistencia con JPA EJB 3

public Query createNamedQuery(String name);

Crea una instancia de query a partir del nombre suministrado en la instancia de la entidad.

public Query createNativeQuery(String sqlString); public Query createNativeQuery(String sqlString, Class result Class); public Query createNativeQuery(String sqlString, String resultSetMapping); public void close(); Cierra un EntityManager Comprueba si un EntityManager esta abierto Recupera un objeto EntityTransation que puede ser usado para comenzar o terminar una transaccin manualmente. Consulta al EntityManager si puede unirse a una transaccin JTA existente. Crea una query dinmica usando una sentencia SQL nativa

public boolean isOpen();

public EntityTransaction getTransaction();

public void joinTransaction();

3.5.2. El ciclo de vida de una entidad


El EntityManager no conoce nada sobre un POJO a pesar de que ste est anotado como una entidad, hasta que le dices al EntityManager que comienza a tratar un POJO como una entidad JPA. Esto es lo totalmente opuesto a como gestiona el contenedor los POJOS anotados como beans de sesin o MOBS, que son cargados y gestionados por l desde el mismo momento en que la aplicacin arranca. Adems, el comportamiento por defecto del EntityManager es gestionar una entidad durante el menor tiempo posible. Otra vez, esto es opuesto a como el contenedor gestiona los beans de sesin y MOBS, que permanecen gestionados hasta que la aplicacin termina. Una entidad que el EntityManager esta gestionando se denomina attached o manager. Por otro lado, cuando el EntityManager deja de gestionar una entidad, se dice que la entidad esta detached. Una entidad que nunca ha sido gestionada se denomina tansient o new.

Pgina 43 de 80

Persistencia con JPA EJB 3

Entidades gestionadas Cuando hablamos de gestin del estado de una entidad, lo que queremos decir es que el EntityManager asegura que los datos de la entidad estn sincronizados con la base de datos. El EntityManager asegura esto haciendo dos cosas: Primero, tan pronto como le decimos al EntityManager que comienze a gestionar una entidad, el EntityManager sincroniza el estado de la entidad con la BD. Segundo, mientras que la entidad est gestionada, el EntityManager asegura que cualquier cambio en los datos de la entidad (por ejemplo, al invocar algn mtodo de la entidad) es reflejado en la BD. El EntityManager acomete esta tarea manteniendo una referencia a la entidad gestionada y comprobando peridicamente los datos, por si necesitan ser actualizados.

El EntityManager deja de gestionar una entidad cuando la entidad es eliminada, o bien es movida fuera del alcance del contexto de persistencia. Una entidad puede ser attached al contexto de un EntityManager cuando pasas la entidad a los mtodos persist, merge o refresh. Tambin una entidad pasa a estar gestionada cuando la recuperas usando el mtodo find o una query dentro de una transaccin. El estado en que se encuentre la entidad determinar que mtodo debes usar para que pase a un estado gestionado: Cuando se instancia la entidad por primera vez, su estado es new o transient, ya que el EntityManager no conoce nada de su existencia: Bid bid = new Bid(); Esta instancia estar gestionada cuando el mtodo persist del EntityManager cree un registro en la base de datos que represente a dicha entidad: manager.persist(bid); Una entidad gestionada pasa a un estado detached cuando sea eliminada, serializada, clonada o salga del alcance del contexto de persistencia. Por ejemplo, la instancia de la entidad Bid pasar a estado detached cuando la transaccin subyacente sea comitida. A diferencia de las entidades creadas explcitamente usando el operador new, una entidad recuperada desde la base de datos usado el mtodo find o una query pasa a estado gestionado inmediatamente, si es recuperada dentro de un contexto Pgina 44 de 80

Persistencia con JPA EJB 3

transaccional. Una instancia recuperada fuera de una transaccin es inmediatamente pasada a estado detached. Los mtodos merge y refresh se usan para entidades que han sido recuperadas desde la BD, pero se encuentran actualmente en estado detached. La operacin merge actualiza la BD con los datos contenidos en la entidad, mientras que refresh realiza la accin opuesta, resetea el estado de la entidad con los datos de BD.

Entidades desconectadas (detached) Una entidad detached es una entidad que ya no se encuentra gestionada por el EntityManager y, por tanto, no hay garanta de que el estado de la entidad est sincronizado con la BD. Las entidades detached son tiles si quieres pasarlas a lo largo de las capas de una aplicacin. Por ejemplo, puedes desconectar una entidad y pasarla a la capa Web, actualizarla y entonces, mandarla de vuelta a la capa EJB, donde puedes ejecutar un merge para volverla a adjuntarla al contexto de persistencia. Como ya mencionamos anteriormente, una entidad puede pasar a estar detached cuando escapa del alcance del contexto de persistencia (por ejemplo, cuando finaliza el mtodo en donde recuperamos la entidad), cuando realizamos una clonacin, una serializacin o cuando llamamos al mtodo remove (lo que, adems, provoca que el registro/s asociados con la entidad en la BD sean eliminados).

3.5.3. Contextos de persistencia, alcance, y el EntityManager


El contexto de persistencia juega un papel vital en la funcionalidad interna del EntityManager. Aunque ejecutemos las operaciones de persistencia invocando mtodos sobre el EntityManager, el EntityManager, en s mismo, no gestiona el ciclo de vida de una entidad. En realidad, el EntityManager delega esta tarea al contexto de persistencia actualmente disponible. DEF. Un contexto de persistencia es una coleccin de entidades gestionadas por un EntityManager durante un mbito de persistencia dado. El mbito de persistencia es la duracin en tiempo durante la que el conjunto de entidades permanece gestionada. Exiten dos tipos diferentes de mbitos de persistencia: transaccional y extendido.

Pgina 45 de 80

Persistencia con JPA EJB 3

Transaction-scoped EntityManager Un EntityManager asociado con un contexto de persistencia de mbito transaccional es conocido como un transaction-scoped EntityManager (EntityManager de mbito transaccional).

Si un contexto de persistencia esta bajo gestionadas por l son automticamente transaccin termina. Dicho de otra manera, transaccional slo mantienen las entidades transaccin que los rodea. Extended EntityManager

un mbito transaccional, las entidades pasadas a estado detached cuando la los contextos de persistencia con mbito gestionadas dentro de los lmites de la

La vida de un EntityManager extendido permanece a lo largo de varias transacciones. Slo puede ser usado con beans de sesin con estado y permanece durante el tiempo en que la instancia del bean se mantiene.

Para un contexto de persistencia extendido, una vez que una entidad es adjuntada por una transaccin dada, dicha entidad es gestionada por todas las transacciones en el tiempo de vida del contexto de persistencia.

Pgina 46 de 80

Persistencia con JPA EJB 3

Usando el EntityManager Vamos a explorar el interfaz EntityManager implementando un componente de nuestra aplicacin de ejemplo. Implementaremos el bean de sesin sin estado ItemManagerBean, que usaremos para proporcionar las operaciones necesarias para manipular los artculos de nuestra aplicacin.

@Stateless public class ItemManagerBean implements ItemManager { @PersistenceContext(unitName="actionBazaar") private EntityManager entityManager; (1) public ItemManagerBean() {} public Item addItem(String title, String description, byte[] picture, double initialPrice, long sellerId) { Item item = new Item(); item.setTitle(title); item.setDescription(description); item.setPicture(picture); item.setInitialPrice(initialPrice); Seller seller = entityManager.find(Seller.class, sellerId); (2) item.setSeller(seller); entityManager.persist(item); (3) return item; } public Item updateItem(Item item) { entityManager.merge(item); (4) return item; } public Item undoItemChanges(Item item) { entityManager.refresh(entityManager.merge(item)); (5) return item; } public void deleteItem(Item item) { entityManager.remove(entityManager.merge(item)); (6) } }

Primero, lo que hacemos es inyectar una instancia del Entitymanager usando la anotacin @PersistenceContext (1). Esta instancia ser usada por todos los mtodos del bean que manipulen entidades. El mtodo addItem es usado por la capa de presentacin para aadir un nuevo artculo, puesto a subasta por un vendedor, a la BD. El mtodo persist se usa dentro del mtodo addItem para aadir una nueva entidad Item a la base de datos (3). Por otro lado, el mtodo addItem recupera el vendedor del artculo, usando la PK de ste (2) y lo asocia al artculo. El mtodo updateItem actualiza los datos de la entidad Item en la base de datos usando el mtodo merge (4). El mtodo undoItemChanges utiliza el mtodo refresh para deshacer cualquier cambio realizado sobre la entidad Item, recargando los datos almacenados en la base de datos para esta entidad (5). Por ltimo, una entidad Item puede ser eliminada de la base de datos, usando el mtodo remove. Pgina 47 de 80

Persistencia con JPA EJB 3

3.5.4. Creando instancias del EntityManager


En el anterior ejemplo, vimos como obtener una instancia del EntityManager usando la anotacin @PersistenceContext. Si estamos ejecutando nuestra aplicacin sobre un contenedor, esto es ms o menos todo lo que necesitaramos conocer sobr como obtener una instacia del Entitymanager. Todas las instancias del EntityManager inyectadas mediante el uso de la anotacin @PersistenceContext son gestionadas por el contenedor. Esto significa que el contenedor se encarga de las tareas de bsqueda, apertura y cerrado del EntityManager en nuestro nombre. Adems, a menos que se especifique lo contrario, los EntityManager inyectados tienen un mbito transaccional. Del mismo modo que no estamos obligados a usar el mbito transaccional, tampoco estamos obligados a usar EntityManagers gestionados por el contenedor. JPA permite la creacin de Entitymanager gestionados por la propia aplicacin en la que, explcitamente, se crea, se utiliza y se libera un EntityManager, incluyendo el control de cmo el EntityManager ha de manejar las transacciones. En este apartado exploraremos como crear y manejar tanto los EntityManager gestionados por el contenedor, como los gestionados por la aplicacin: EntityManagers gestionados por el contenedor. Como ya vimos, los EntityManager gestionados por el contenedor se inyectan usando la anotacin @PersistenceContext. A continuacin vemos la definicin de esta anotacin:

@Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME) public @interface PersistenceContext { String name() default ""; String unitName() default ""; PersistenceContextType type default TRANSACTION; PersistenceProperty[] properties() default {}; }

El primer elemento de la anotacin, name, especifica el nombre JNDI del contexto de persistencia. En la mayora de las situaciones, dejar este elemento vaco es lo mejor, a menos que uses la anotacin a nivel de clase. El elemento unitName especifica el nombre de la unidad de persistencia. Una unidad de persistencia es, esencialmente, el grupo de entidades usadas en la aplicacin. La idea es til cuando tienes una aplicacin Java EE muy grande y quieres dividirla en varias reas lgicas (como paquetes Java). Las unidades de persistencia se configuran a travs del fichero DD persistente.xml, que veremos ms adelante. En el caso tpico en que slo tenemos una unidad de persistencia, especificar el unitName puede parecer redundante. De hecho, la mayora de los proveedores de persistencia resolvern la unidad de persistencia correctamente si no la especificas. Sin embargo, es recomendable especificar el nombre de la unidad de persistencia aunque Pgina 48 de 80

Persistencia con JPA EJB 3

slo tengamos una. Esto asegura que no dependamos de una funcionalidad especfica del contenedor. El Elemento type especifica el mbito del EntityManager, que, como ya vimos, puede ser TRANSACTION o EXTENDED. Si se deja sin indicar, se asume el TRANSACTION por defecto. EntityManagers gestionados por la aplicacin Los EntityManager que son gestionados por la aplicacin requieren que codifiquemos el control de todos los aspectos relacionados con el cliclo de vida del EntityManager. Los EntityManager gestionados por la aplicacin son apropiados para entornos donde un contenedor no esta disponible, como aplicaciones Java SE, o contenedores ligeros como Tomcat. Sin embargo, una justificacin para usar EntityManagers gestionados por la aplicacin dentro de un contenedor Java EE es mantener un control de bajo nivel sobre el ciclo de vida de los EntityManagers y/o para la gestin de transacciones. Por esta razn, as como para mantener la flexibilidad, el API EJB3 proporciona unas cuantas convenciones para el uso de EntityManagers gestionadas por la aplicacin dentro de un contenedor. Veamos una aproximacin de esto, re-escribiendo el ItemManagerBean:

@Stateless public class ItemManagerBean implements ItemManager { @PersistenceUnit private EntityManagerFactory entityManagerFactory; (1) private EntityManager entityManager; public ItemManagerBean() {} @PostConstruct public void initialize() { entityManager = entityManagerFactory.createEntityManager(); (2) } ... public Item updateItem(Item item) { entityManager.joinTransaction(); (3) entityManager.merge(item); return item; } ... @PreDestroy public void cleanup() { if (entityManager.isOpen()) { entityManager.close(); (4) } } ... } Lo primero es inyectar una instancia del EntityManagerFactory, por medio de la anotacin @PersistenceUnit (1). Creamos un EntityManager usando la factora inyectada despus de la construccin del bean (2) y lo cerramos antes de que el bean sea destruido (4). En el punto (3) nos unimos explcitamente a una transaccin gestionada por el contenedor. Este mtodo esta especialmente dirigido al uso de EntityManagers gestionados por la aplicacin dentro de un contenedor, donde normalmente contamos con transacciones JTA, cosa que no ocurre si nuestra aplicacin se ejecuta fuera de un contenedor Java EE. Pgina 49 de 80

Persistencia con JPA EJB 3

EntityManagerFactory Como vimos en el ejemplo anterior, para obtener una instancia de un EntityManager gestionado por la aplicacin usamos la interfaz EntityManagerFactory. Es similar a la idea de crear un Connection a partir de una factora DriverManager en JDBC. En un entorno Java EE tenemos la facilidad de inyectar una instancia del EntityManagerFactory usando la anotacin @PersistenceUnit:

@Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME) public @interface PersistenceUnit { String name() default ""; String unitName() default ""; }

Los elementos name y unitName tienen exactamente el mismo propsito que tenan en la anotacin @PersistenceContext.

EntityManagers gestionados por la aplicacin fuera de un contenedor Java EE Como ya hemos comentado, en entornos Java SE, JTA no es una posibilidad. Por tanto, debemos gestionar las transacciones a mano. Para ello, se define la interfaz EntityTransaction. Explicaremos esta interfaz reimplementando el cdigo para actualizar un artculo en nuestra aplicacin:

EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(actionBazaar); (1) EntityManager entityManager = Pgina 50 de 80

Persistencia con JPA EJB 3

entityManagerFactory.createEntityManager(); (2) try { EntityTransaction entityTransaction = entityManager.getTransaction(); (3) entityTransaction.begin(); (4) entityManager.merge(item); (5) entityTransaction.commit(); (6) entityManager.close(); (7) entityManagerFactory.close(); (7) } catch (Exception e) {} finally { entityManager.close(); (7) entityManagerFactory.close(); (7) }

El mtodo createEntityManagerFactory del objeto javax.persistence.Persistance es, bsicamente, un sustituto de la anotacin @PersistenceUnit (1). El nico parmetro de este mtodo es el nombre del del EntityManagerFactory definido en el fichero DD persistente.xml. Como no contamos con la ayuda del contenedor, es importante cerrar el EntityManager devuelto por el mtodo (7). Igual que en el anterior ejemplo, el mtodo createEntityManager se usa para crear el EntityManager gestionado por la aplicacin (2). Sin embargo, antes de ejecutar la actualizacin de la entidad Item a la BD (5), debemos crear un EntityTransaction llamando al mtodo getTransaction de la interfaz EntityManager (3). El EntityTransaction es en esencia una abstraccin de alto nivel sobre una transaccin local JDBC, a diferencia de la transaccin distribuida a la que nos unamos en el ejemplo anterior. Es fcil pensar que, al igual que en el ejemplo anterior, una llamada a joinTransaction sera necesaria para englobar al EntityManager en la transaccin actual, pero como en este caso es el propio EntityManager el que esta creando la transaccin en lugar del contenedor, es l el que sigue la pista de los EntityTransactions creados, as que el join no es necesario. El resto del cdigo de la transaccin es autoexplicativo (4), el begin comienza la transaccin y el commit (6) realiza el commit de la misma. Es importante destacar que los EntityManagers gestionados por la aplicacin nunca son de mbito transaccinal, es decir que mantienen las entidades gestionadas hasta que se cierra el EntityManager explcitamente. Adems el atributo transaction-type del fichero persistente.xml, debe ser establecido a RESOURCE_LOCAL si queremos usar el interfza EntityTransaction.

Pgina 51 de 80

Persistencia con JPA EJB 3

3.6. Gestionando las operaciones de persistencia


3.6.1. Persistiendo entidades
public Item addItem(String title, String description, byte[] picture, double initialPrice, long sellerId) { Item item = new Item(); item.setTitle(title); item.setDescription(description); item.setPicture(picture); item.setInitialPrice(initialPrice); Seller seller = entityManager.find(Seller.class, sellerId); item.setSeller(seller); entityManager.persist(item); // Persiste la entidad return item; }

Invocando al mtodo persist del interfaz EntityManager convertimos una entidad transient en gestionada. Cuando la transaccin es comitida, el estado de la entidad se sicroniza con la base de datos. Las sentencia (o sentencias) INSERT que crean el registro en BD correspondiente a la entidad no tienen por qu ser tratados inmediatamente. Para EntityManagers con mbito transaccional, la sentencia es ejecutada cuando la transaccin que engloba la operacin es comitida. En nuestro ejemplo, esto significa que las sentencias SQL son tratadas cuando termina el mtodo addItem. Para EntityManagers con mbito extendido, la sentencia INSERT ser ejecutada antes de que el EntityManager sea cerrado. El INSERT tambin puede ser ejecutado cuando se llama al mtodo flush del EntityManager. La sentencia INSERT generada para el ejemplo podra ser algo parecido a esto: INSERT INTO ITEMS (TITLE, DESCRIPTION, SELLER_ID, ...) VALUES (Toast with the face of Bill Gates on it, This is an auction for..., 1, ...) Como podemos observar, la PK ITEM_ID, que la identidad de la entidad Item no se incluye en la sentencia INSERT generada. Esto es porque establecimos como mtodo de generacin de claves primarias a IDENTITY. Si hubiramos especificado el esquema de generacin a SEQUENCE o TABLE, el EntityManager habra generado una sentenci SELECT para recuperar el valor de la clave y lo hubiera incluido en la sentencia INSERT generada.

3.6.2. Persistiendo relaciones entre entidades

Pgina 52 de 80

Persistencia con JPA EJB 3

En el ejemplo anterior, la entidad Seller se recupera usando el mtodo find, as que pasa a ser gestionada automticamente y cualquier cambio hecho en ella es transaparentemente sincronizado. La relacin entre Item y Seller es una uno-a-muchos bidireccional y se establece en nuestro ejemplo en el momento en que hacemos item.setSeller(seller). Tal relacin se implementa aadiendo una clave extranjera SELLER_ID en la tabla ITEMS. Como el Seller ya esta persistido, lo nico que debe hacer el EntityManager es establecer el valor de la clave extranjera SELLER_ID en la sentencia INSERT generada. Las cosas llegan a ser ms interesantes cuando pensamos en el caso en que la entidad relacionada a otra que intentamos persistir no existe todava en la base de datos. Este caso no ocurre con demasiada frecuencia en relaciones uno-a-muchos ni en relaciones muchos-amuchos. Sin embargo, suele ocurrir con bastante frecuencia en relaciones uno-a-uno. Para ilustrar este caso, veremos como funcionaria con la relacin uno-a-uno bidireccional que hay entre User y BillingInfo:

public User addUser(String username, String email, String creditCardType, String creditCardNumber, Date creditCardExpiration) { User user = new User(); user.setUsername(username); user.setEmail(email); BillingInfo billing = new BillingInfo(); billing.setCreditCardType(creditCardType); billing.setCreditCardNumber(creditCardNumber); billing.setCreditCardExpiration(creditCardExpiration); user.setBillingInfo(billing); entityManager.persist(user); (1) //persiste tanto User, como BillingInfo return user; }

Como podemos ver, ni la entidad User ni la entidad BillingInfo estn gestionadas cuando se llama al mtodo persist, ya que ambas se encuentran en estado transient. Asumamos que la entidad User y la entidad BillingInfo son almacenadas en las tablas USERS y BILLING_INFO respectivamente, con una relacin uno-a-uno modelada con una clave extranjera en la tabla USERS que referencia a la clave BILLING_ID en la tabla BILLING_INFO. JPA crear dos sentencias INSERT, una para el User y otra para la BillingInfo. La sentencia INSERT para la tabla USERS contendr la clave extranjera apropiada, referenciado al nuevo registro creado en la tabla BILLING_INFO.

3.6.3. Operaciones de persistencia en cascada


Quizs sorpredentemente, este NO es el comportamiento por defecto para JPA a la hora de persistir entidades relacionadas. Por defecto, la entidad BillingInfo no sera persistida, por tanto, no se generara una sentencia INSERT para persistirla a la tabla BILLING_INFO. Para que esto funcione como indicamos en el ejemplo anterior, la clave est en la definicin que hicimos de la relacin uno-a-uno en la entidad User:

public class User { @OneToOne(cascade=CascadeType.PERSIST) public void setBillingInfo(BillingInfo billing) { La cascada de operaciones en un ORM es similar al concepto de casacada en bases de datos. El elemento cascade le dice al EntityManager cmo, o si debe propagar una determinada operacin de persistencia, a las entidades relacionadas con una entidad dada. Pgina 53 de 80

Persistencia con JPA EJB 3

Por defecto, este elemento esta vaco, lo que significa que ninguna operacin ser propagada a las entidades relacionadas. Podemos establecer los siguientes valores para el elemento cascade: Valor CascadeType.MERGE CascadeType.PERSIST CascadeType.REFRESH CascadeType.REMOVE CascadeType.ALL Efecto Slo la operacin EntityManager.merge entidades relacionadas Slo la operacin EntityManager.persist entidades relacionadas Slo la operacin EntityManager.refresh entidades relacionadas Slo la operacin EntityManager.remove entidades relacionadas Todas las operaciones del EntityManager entidades relacionadas

se propaga a las se propaga a las se propaga a las se propaga a las se propagan a las

Como en nuestro ejemplo establecimos el valor de cascade a PERSIST, cuando ejecutamos la operacin persist sobre la entidad User, el EntityManager averigua que la entidad BillingInfo asociada con el usuario tambin debe ser persistida. Si no hubiramos establecido ningn valor, o hubiramos puesto alguno de los otros (salvo ALL), es decir MERGE, REFRESH o REMOVE, el EntityManager no sabra que tambin debe persistir la entidad BillingInfo y nuestro cdigo debera ser escrito de la siguiente manera para que funcionase:

public User addUser(String username, String email, String creditCardType, String creditCardNumber, Date creditCardExpiration) { User user = new User(); user.setUsername(username); user.setEmail(email); BillingInfo billing = new BillingInfo(); billing.setCreditCardType(creditCardType); billing.setCreditCardNumber(creditCardNumber); billing.setCreditCardExpiration(creditCardExpiration); entityManager.persist(billing); //primero persistimos BillingInfo user.setBillingInfo(billing); //establecemos la relacin entityManager.persist(user); //persistimos User return user; }

3.6.4. Recuperando entidades por clave primaria


JPA soporta varias maneras de recuperar instancias de entidades desde la base de datos. La ms sencilla es recuperar una entidad a partir de su clave primaria, usando el mtodo find. La otra manera implica el uso del API JPQL, que veremos ms adelante. En el mtodo addItem de nuestro ejemplo, recuperbamos una instancia de la entidad Seller para establer la relacin con Item: Seller seller = entityManager.find(Seller.class, sellerId); El primer parmetro especifica el tipo Java de la entidad a recuperar. El segundo especifica el valor de la identidad de la entidad a recuperar. Puede ser un tipo simple de Java anotado con @Id, o una clase que modela una clave compuesta a travs de la anotacin @EmbeddedId o @IdClass. Pgina 54 de 80

Persistencia con JPA EJB 3

El mtodo find inspecciona los detalles de la entidad pasada y genera una sentencia SELECT, suministrndole la clave primaria que recibe. Por ejemplo, la sentencia que se creara en nuestro ejemplo sera algo como esto: SELECT * FROM seller WHERE seller_id = 1 Si no se encuentra ninguna entidad que coincida con el id especificado, el mtodo find devolver null. Unas de las ms importantes caractersticas del mtodo find es que utiliza el cacheo del EntityManager. Si tu proveedor de persistencia soporta cacheo y la entidad ya existe en la cach, entonces el EntityManager devuelve una instancia cacheada de la entidad, en lugar de recuperarla de la base de datos. Hay una caracterstica todava ms importante dirigida a la optimizacin de la aplicacin: lazy y eager loading (carga perezosa y carga ansiosa). En nuestro caso, la sentencia SELECT generada intenta recuperar todos los campos de la entidad cuando se invoca el mtodo find. Este es el comportamiento por defecto, sin embargo, en algunos casos, ste no es el comportamiento deseado. Los Fetch modes nos permiten cambiar este comportamiento para optimizar el rendimiento de la aplicacin cuando sea necesario. Entity Fetch Modes Como ya hemos indicado, el EntityManager carga todos los datos de la instancia cuando una entidad es recuperada de la base de datos. En lenguaje ORM, esto se llama eager fetching, o eager loading. Esto no siempre es buena idea, pues puede suponer una gran prdida de rendimiento. El ejemplo ms clsico es cuando intentamos cargar BLOBs, como por ejemplo, imgenes. Debido a que la carga de tipos de datos BLOB suele suponer operaciones costosas de entrada / salida, slo deberan ser recuperados cuando fueran necesarios. Esta estrategia de optimizacin (recuperar un dato cuando es solicitado explcitamente) se llama lazy loading. JPA tiene ms de un mecanismo para soportar lazy fetching. Especificar el fetch-mode de una columna usando la anotacin @Basic es el ms fcil de entender. Por ejemplo, podemos establecer el fech mode para la propiedad picture de la entidad Item para que sea recuperada de manera perezosa:

@Column(name="PICTURE") @Lob @Basic(fetch=FetchType.LAZY) public byte[] getPicture() { return picture; }

Una sentencia SELECT generada por el mtodo find para recuperar entidades Item no incluir la columna ITEMS.PICTURE. En vez de ello, el campo picture ser recuperado automticamente desde la base de datos cuando la propiedad sea accedida por primera vez a travs del mtodo getPicture(). La carga perezosa es un arma de doble filo. Especificar que una columna sea recuperada de manera perezosa significa que el EntityManager crear una sentencia SELECT adicional slo para recuperar el campo picture cuando sea accedido por primera vez. Imagina que podra ocurrirle a la base de datos si todos los datos de las entidades de la aplicacin fueran recuperados perezosamente. Adems, y ms importante, es que la carga perezosa es una Pgina 55 de 80

Persistencia con JPA EJB 3

caracterstica OPCIONAL, lo que significa que no todos los proveedores de persistencia la implementarn. Cargando entidades relacionadas Uno de los usos ms complicados de los fetch modes es el control de la recuperacin de las entidades relacionadas. Por defecto, algunos tipos de relaciones se recuperan de manera perezosa y algunos otros se recuperan de manera ansiosa (eager). Para ver como funciona la carga en cada tipo de relacin, nos fijaremos en la entidad Item, que tiene una relacin muchos-a-uno con Seller, una relacin uno-a-muchos con la entidad Bid, y una relacin muchos-a-muchos con la entidad Category:

public class Item { @ManyToOne public Seller getSeller(){ ... @OneToMany public List<Bid> getBids(){ ... @ManyToMany public List<Category> getCategories() { ... }

Cuando recuperamos una instancia de la entidad Item, cada una de las relaciones se recupera de una manera distinta. Primero nos fijaremos en la relacin con Seller. En este caso, la relacin se recupera de manera ansiosa, ya que el fetch mode por defecto para la anotacin @ManyToOne es EAGER. El EntityManager implementa el modo eager mediante una JOIN:

SELECT * FROM ITEMS INNER JOIN SELLER ON ITEM.SELLER_ID = SELLER.SELLER_ID WHERE ITEM.ITEM_ID = 100

El resultado de esta JOIN ser una sola fila que contendr columnas tanto de la tabla SELLERS como de la tabla ITEMS. En trminos de rendimiento de la base de datos, esto es Pgina 56 de 80

Persistencia con JPA EJB 3

ms eficiente que usar una SELECT para recuperar el Item y luego ejecutar otra SELECT para recuperar el Seller relacionado. Lo antes descrito tambin se aplica para relaciones @OneToOne, ya que su fetch mode por defecto es tambin EAGER. Por otro lado, las anotaciones @OneToMany y @ManyToMany son por defecto, lazy loading. Esto es as porque en ambas relaciones ms de una entidad debe ser recuperada junto con la entidad que queremos obtener. Imaginemos que queremos recuperar varias instancias de Item (por ejemplo, mediante una query JPQL, como veremos en el siguiente apartado). (N1 + N2 + + NX) filas seran devueltas, donde Ni es el nmero de entidades relacionadas con el isimo registro Item. Para nmeros no triviales de N e i, el conjunto recuperado puede ser muy grande, causando importantes problemas de rendimiento en la base de datos. Por esto, es por lo que las anotaciones @OneToMany y @ManytoMany son por defecto, lazy loading. En el siguiente cuadro vemos un resumen de los comportamientos por defecto en cada tipo de relacin: Tipo de Relacin Uno-a-uno Uno-a-muchos Muchos-a-uno Muchos-a-muchos Comportamiento por defecto EAGER LAZY EAGER LAZY Nmero de entidades recuperadas Una sola entidad recuperada Coleccin de entidades Un sola entidad recuperada Coleccin de entidades

3.6.5. Actualizando Entidades


Como ya hemos comentado, el EntityManager asegura que los cambios hechos en las entidades gestionadas por l son siempre sincronizados con la base de datos automticamente. Esto se puede ver en el siguiente ejemplo, en el que se realiza el clculo de la solvencia de un Vendedor:

public void calculateCreditWorthiness (Long sellerId) { PowerSeller seller = entityManager.find(PowerSeller.class, sellerId); seller.setCreditWorth(seller.getCreditWorth() * CREDIT_FACTOR * getRatingFromCreditBureauRobberBarons(seller)); seller.setCreditWorth(seller.getCreditWorth() + (seller.getCreditWorth() * FEEDBACK_FACTOR * seller.getBuyerFeedbackRating())); seller.setCreditWorth(seller.getCreditWorth() + (seller.getCreditWorth() * SELLING_FACTOR * getTotalHistoricalSales(seller))); } Como la entidad PowerSeller se recupera a travs del mtodo find, pasa a estar automticamente gestionada por el EntityManager, con lo que cuando acabe el clculo de la solvencia (cuando finalice el mtodo), el EntityManager se encargar de actualizar el registro de la base de datos correspondiente a esta entidad. Detachment and Merge operations

Pgina 57 de 80

Persistencia con JPA EJB 3

Aunque las entidades gestionadas son muy tiles, no siempre es fcil mantener las entidades adjuntas a un EntityManager. Muchas veces el problema es que las entidades necesitan ser detached y serializadas a la capa Web, donde la entidad es modificada fuera del alcance del EntityManager. Adems, no hay garantas de que todas las llamadas realizadas desde un mismo un cliente a un bean de sesin sin estado sean servidas por la misma instancia del bean. Esto significa que no esta garantizado que una entidad ser manejada por la misma instancia del EntityManager a lo largo de las llamadas entre mtodos, haciendo que la persistencia automtica sea ineficiente.

Para este propsito se utiliza el mtodo merge del EntityManager. ste mtodo slo debe ser usado con entidades que ya existan en la base de datos. Un intento de merge sobre una entidad inexistente tendr como resultado un IllegalArgumentException. Lo mismo ocurrir si intentas hacer un merge sobre una entidad que ha sido borrada a travs del mtodo remove, incluso si la sentencia DELETE no ha sido ejecutada an. Merging relaciones Por defecto, la operacin merge no se propaga a las entidades relacionadas con la entidad a la que se le aplica dicha operacin. Como ya vimos, este comportamiento por defecto se puede variar. Si establecemos el elemento cascade de la relacin a MERGE o a ALL, la operacin se propagar a las entidades relacionadas. Por ejemplo, el siguiente cdigo provocar que la entidad Seller relacionada con el Item sea actualizada (merge) cuando se ejecuta esta operacin sobre una entidad Item:

public class Item { @ManyToOne(cascade=CascadeType.MERGE) public Seller getSeller() {

Pgina 58 de 80

Persistencia con JPA EJB 3

3.6.6. Borrando entidades


El mtodo deleteItem en nuestra implementacin del bean ItemManagerBean, elimina un Item de la base de datos. Es importante darse cuenta de que el artculo a borrar ser primero adjuntado al EntityManager, usando la operacin merge: public void deleteItem(Item item) { entityManager.remove(entityManager.merge(item)); } Esto es porque el mtodo remove puede slo borrar entidades que estn actualmente gestionadas. Si una entidad detached es pasada al mtodo remove, ste lanzar un IllegalArgumentException. Antes de que el mtodo deleteItem termine, el registro correspondiente al Item ser eliminado de base de datos usando una sentencia como esta: DELETE FROM ITEMS WHERE item_id = 1 Al igual que las operaciones persist y merge, debes establecer el elemento cascade de una anotacin de relacin a REMOVE o ALL si quieres que las entidades relacionadas sean eliminadas en cascada. Por ejemplo, podemos establecer que la entidad BillingInfo relacionada con un Bidder sea eliminada cuando la entidad Bidder propietaria sea elimina: @Entity public class Bidder { @OneToOne(cascade=CascadeType.REMOVE) public BillingInfo setBillingInfo() {

3.6.7. Refrescando entidades


La operacin refresh vuelve a dejar los datos de una entidad tal y como estn en base de datos, esto es, el proveedor de persistencia busca el registro que coincide con la entidad y resetea la entidad con los datos recuperados de base de datos.

Pgina 59 de 80

Persistencia con JPA EJB 3

3.7. Gestin de Transacciones en EJB 3


DEF. Una transaccin es una agrupacin de tareas que deben ser procesadas como una unidad inseparable. Esto significa que todas las tareas que son parte de la transaccin deben procesarse exitosamente para que la transaccin concluya de manera satisfactoria. Si cualquier tarea falla, la transaccin falla. Las transacciones deben cumplir cuatro propiedades que se conocen con el nombre de ACID (Atomicity, Consistency, Isolation and Durability): 1. Atomicidad (Atomicity): es la propiedad que asegura que la operacin se ha realizado o no. Si algo inesperado e irrecuperable ocurre durante la ejecucin de una transaccin, todos lo cambios producto de su ejecucin son deshechos y, por tanto, no tienen efecto en el sistema. Por otro lado, los resultados de la ejecucin satisfactoria de una transaccin, tendrn un efecto permanente en el sistema. 2. Coherencia (Consistency): es la propiedad que asegura que slo se empieza aquello que se puede acabar. Por lo tanto se ejecutan aquellas operaciones que no van a romper la reglas y directrices de integridad de la base de datos. Si el sistema se encuentra en un estado coherente con las reglas del negocio antes de que una transaccin comience, debe permanecer en un estado coherente despus de la ejecucin de la misma. Un corolario aadido a esta propiedad es que NO es necesario que el sistema est en un estado coherente durante la ejecucin de la transaccin. 3. Aislamiento (Isolation): es la propiedad que asegura que una operacin no puede afectar a otras. Esto asegura que la realizacin de dos transacciones sobre la misma informacin nunca generar ningn tipo de error. Existen cuatro niveles de aislamiento: a. Read uncommited: En este nivel de aislamiento, las transacciones pueden leer datos no comitidos de otras transacciones, lo que se conoce como dirty read. No se debe usar este nivel de aislamiento en un entorno multihilo. b. Read commited: Las transacciones nunca leern los cambios no comitidos de otras transacciones. Este es el nivel por defecto para la mayora de bases de datos. c. Repeatable read: La transaccin garantiza que obtendr los mismos datos en mltiples lecturas de la misma fila hasta que la transaccin termina. d. Serializable: Este es el nivel de aislamiento ms alto y garantiza que ninguna de las tablas que toquemos cambiarn durante la transaccin, incluyendo la adiccin de nuevas filas. Este nivel de aislamiento es muy propenso a causar cuellos de botella. 4. Permanencia (Durability): es la propiedad que asegura que una vez realizada la operacin, sta persistir y no se podr deshacer aunque falle el sistema.

3.7.1. Transacciones gestionadas por el contendor (CMT)


En un CMT, el contenedor comienza, comite, y deshace (roll back) una transaccin en nuestro nombre. Los lmites de la transaccin en transacciones declarativas son siempre marcados por el comienzo y el final de los mtodos de negocio de los EJB. El contenedor comienza una transaccin JTA antes de que el mtodo del EJB sea invocado. Invoca el mtodo y, dependiendo de lo que ocurra durante su ejecucin, o bien confirma, o bien deshace la transaccin.

Pgina 60 de 80

Persistencia con JPA EJB 3

Todo lo que debemos hacer es decirle al contenedor como gestionar las transacciones usando anotaciones e informarle de cmo deshacer la transaccin cuando sea necesario. Por defecto, el contenedor asume que usars CMT en todos tus mtodos de negocio.

@Stateless @TransactionManagement(TransactionManagementType.CONTAINER) (1) public class OrderManagerBean { @Resource private SessionContext context; (2) ... @TransactionAttribute(TransactionAttributeType.REQUIRED) (3) public void placeSnagItOrder(Item item, Customer customer){ try { if (!bidsExisting(item)){ validateCredit(customer); chargeCustomer(customer, item); removeItemFromBidding(item); } } catch (CreditValidationException cve) { context.setRollbackOnly(); (4) } catch (CreditProcessingException cpe){ context.setRollbackOnly(); (4) } catch (DatabaseException de) { context.setRollbackOnly(); (4) } } }

Lo primero es indicar al contenedor que gestione las transacciones de este bean (1). Si no especificamos la anotacin @TransactionManagement o el elemento transacitionType, el contenedor asume por defecto el uso de CMT. El contexto del EJB se inyecta en el bean mediante la anotacin @Resource (2). Marcamos que el mtodo placeSnagItOrder requiere una transaccin y que debera ser iniciada por el contenedor cuando sea necesaria. Si una excepcin ocurre durante la ejecucin del mtodo, le indicamos al contenedor que haga un roll back usando el mtodo setRollBackOnly del contexto EJB inyectado (4). La anotacin @TransactionManagement

Esta anotacin especifica si usaremos CMT o BMT (Bean Management Transaction) para un bean en particular. En nuestro caso indicamos que las transacciones sean gestionadas por el contenedor indicando TransactionManagementType.CONTAINER. En su lugar, si queremos indicar que queremos gestionar las transacciones programticamente, especificaremos TransactionManagementType.BEAN en el tipo. La anotacin @TransactionAttribute

Esta anotacin le dice al contenedor cmo manejar las transacciones. Puede ser aplicada individualmente a cada mtodo del bean o al bean entero. Si la anotacin es aplicada al nivel del bean, todos los mtodos del bean heredan el valor de del atributo de transaccin especificado.

Pgina 61 de 80

Persistencia con JPA EJB 3

3.7.2. Transacciones gestionadas por el Bean (BMT)


Usando CMT, estamos limitados a tener los lmites de las transacciones vinculados al comienzo y al final de los mtodos de negocio. BMT, sin embargo, nos permite especificar estos detalles programticamente.

@Stateless) @TransactionManagement(TransactionManagementType.BEAN) (1) public class OrderManagerBean { @Resource private UserTransaction userTransaction; (2) public void placeSnagItOrder(Item item, Customer customer){ try { userTransaction.begin(); (3) if (!bidsExisting(item)){ validateCredit(customer); chargeCustomer(customer, item); removeItemFromBidding(item); } userTransaction.commit(); (4) } catch (CreditValidationException cve) { userTransaction.rollback(); (5) } catch (CreditProcessingException cpe){ userTransaction.rollback(); (5) } catch (DatabaseException de) { userTransaction.rollback(); (5) } catch (Exception e) { e.printStackTrace(); } } Pgina 62 de 80

Persistencia con JPA EJB 3

Lo primero que hacemos es especificar que las transacciones sern gestionadas por el bean (1). La anotacin @TransactionAttribute no aparece ningn lado, ya que slo es aplicable en CMT. Una UsertTransaction, la representacin JTA de una BMT se inyecta va la anotcai @Resource (2). Con la instancia inyectada, explcitamente se indica el comienzo de la transaccin (3), el commit (4), o el roll back de la transaccin (5) en caso de error.

3.8. Descripcin del mdulo de persistencia: persistente.xml


En EJB 3 las entidades pueden ser usadas tanto en un contender EJB, como en uno Web, como en aplicacin Java SE. Cuando usamos entidades en nuestra aplicacin, tenemos que empaquetar dichas entidades en un mdulo EJB-JAR, WAR o un simple JAR, con un fichero DD muy simple llamado persistente.xml. El fichero persistence.xml debe ir en la carpeta META-INF/ Existe un fichero opcional, denominado orm.xml, que sirve para realizar el mapeo de entidades con metadatos sobre el xml, en lugar de usar anotaciones.

<persistence> <persistence-unit name = "actionBazaar" transaction-type = "JTA"> (1) <provider> oracle.toplink.essentials.PersistenceProvider (2) </provider> <jta-data-source>jdbc/ActionBazaarDS</jta-data-source> (3) <class>ejb3inaction.persistence.Category</class> (4) <class>ejb3inaction.persistence.Bid</class> (4) ... <properties> <property name = "toplink.ddl-generation" value = "drop-andcreate-tables"/> (5) </properties> </persistence-unit> </persistence>

Lo primero es definir nuestra unidad de persistencia por medio del elemento persistente-unit (1). El tipo de transaccin se define en el elemento transaction-type. Los posibles valores son JTA, si vamos a desplegar nuestra aplicacin en un servidor de aplicaciones, o RESOURCE_LOCAL si nuestra aplicacin se ejecutar fuera de un contenedor Java EE. Lo siguiente que especificamos es la clase factora del proveedor de persistencia usado (2). En este caso, la clase especificada corresponde al motor de persistencia TopLink. Si no especificamos este valor, se asumir el proveedor por defecto integrado en nuestro servidor de aplicaciones (en el caso de WAS, el proveedor por defecto es OpenJpa) El proveedor de persistencia se conecta a la base de datos para almacenar y recuperar las entidades, as que debemos especificar el datasource para conectar con la misma (3). Pgina 63 de 80

Persistencia con JPA EJB 3

Como podemos declarar varias unidades de persistencia en el mismo fichero persistente.xml, necesitamos indicar que clases conforman cada unidad definida (4). En el caso que slo tengamos una unidad de persistencia no es necesario indicar las clases, pues se asumir que todas las clases anotadas con @Entity pertencen a dicha unidad. Por ltimo y, opcionalente, podemos especificar propiedades de configuracin especficas de nuestro proveedor de persistencia, usando el elemento properties (5)

3.9. Usando JPQL para recuperar entidades


El API de consultas de JPA consta de los mtodos de la interfaz EntityManager para crear instancias de consultas, los mtodos de la interfaz Query que definen y ejecutan las consultas y JPQL, como lenguaje de consultas OO.

Los pasos que hemos de seguir para crear una consulta con JPA son muy similares a los que tenemos que hacer para ejecutar una consulta tradicional con JDBC. En la siguiente tabla comparamos estos dos puntos de vista: Pasos bsicos para ejecutar una consulta usando consultas SQL con JDBC
1. Obtener una conexin con la base de datos 2. Crear una sentencia con la consulta 3. Ejecutar la sentencia 4. Recuperar los resultados (registros de la base de datos)

Pasos bsicos para ejecutar una consulta JPA con JPQL


1. Obtener una instancia EntityManager 2. Crear una instancia de Query 3. Ejecutar la consulta de un

4. Recuperar los resultados (entidades)

3.9.1. Anatoma de una consulta


El API de consultas soporta dos tipos de querys: named y dynamic. Named Querys: estn pensadas para ser almacenadas y reutilizadas. Por ejemplo, supongamos que nuestra aplicacin requiere que mostremos una lsita con artculos ms populares en una categora especfica. Como este requerimiento ser ejecutado Pgina 64 de 80

Persistencia con JPA EJB 3

en varios mdulos de la aplicacin, crearemos una named query que podr ser llamada tantas veces como queramos. Dynamic querys: son creadas y ejecutas en el momento. Por ejemplo, supongamos que queremos recuperar todas las categoras en el sistema:

@PersistenceContext em; (1) ... public List findAllCategories() { Query query = em.createQuery("SELECT c FROM Category c"); ... (2) return query.getResultList(); ............ (3) }

Lo primero que hacemos es obtener una instancia del EntityManager va inyeccin de dependencias (1). Entonces creamos una instancia de Query usando el mtodo EntityManager.createQuery (2), pasando la consulta en JPQL. Una vez tenemos la instancia el paso final es obtener la lista de resultados usando el mtodo getResultList.

3.9.2. Definir named querys


Cualquier consulta que sea usada en varios componentes de tu aplicacin es un candidato a ser una named query. Este tipo de querys tienen tres importantes beneficios: Mejoran la reutilizacin de las consultas Mejoran la mantenibilidad del cdigo; las consultas no estn dispersas por la lgica del negocio. Pueden mejorar el rendimiento ya que son preparadas una vez y pueden ser eficientemente reutilizadas.

Para declarar una named query, usaremos la anotacin @javax.persistence.NamedQuery:

@Entity @NamedQuery( name = "findAllCategories", query = "SELECT c FROM Category c WHERE c.categoryName LIKE :categoryName ") public class Category implements Serializable { .. }

En aplicaciones ms complejas, probablemente tendremos varias named querys. En este caso, podemos usar la anotacin @javax.persistence.NamedQueries para especificar varias named querys:

@Entity @NamedQueries( { @NamedQuery( name = "findCategoryByName", query = "SELECT c FROM Category c WHERE c.categoryName LIKE :categoryName order by c.categoryId" ), @NamedQuery( name = "findCategoryByUser", Pgina 65 de 80

Persistencia con JPA EJB 3

query = "SELECT c FROM Category c JOIN c.user u WHERE u.userId = ?1" ) } ) @Table(name = "CATEGORIES") public class Category implements Serializable { }

Las named querys tienen un mbito que abarca a toda la unidad de persistencia, as que deben tener un nombre nico.

3.9.3. Ejecutando las consultas


1. Creando una instancia de Query El interfaz EntityManager nos proporciona los siguientes mtodos para crear consultas, ya sea con sentencias JPQL o con sentencias SQL nativas: Mtodo public Query createQuery(String jpqlString); Propsito Crea una query dinmica usando una sentencia JPQL. Crea una instancia de Query a partir del nombre suministrado de una named query. Este mtodo puede ser usado tanto para consultas con JPQL como para consultas con SQL nativo

public Query createNamedQuery(String name);

public Query createNativeQuery(String sqlString); public Query createNativeQuery(String sqlString, String resultSetMapping);

Crea una query dinmica usando una sentencia SQL nativa

Crea una query dinmica usando SQL nativo que recupera un conjunto de resultado con varios tipos de entidades. Crea una query dinmica usando una sentencia SQL que recupera un solo tipo de entidad.

public Query createNativeQuery(String sqlString, Class result Class); Ejemplos: a. Crear una named query:

Query query = em.createNamedQuery("findAllCategories"); b. Crear una dynamic query: Query query = em.createQuery("SELECT i FROM Item i"); 2. Trabajando con la interfaz Query Mtodo public List getResultList() Propsito Recupera el resultado de una consulta. Pgina 66 de 80

Persistencia con JPA EJB 3

public Object getSingleResult() public int executeUpdate()

Recupera un slo resultado u objeto. Ejecuta una sentencia UPDATE o DELETE Establece el mximo nmero de objetos que deben ser recuperados. Establece la posicin inicial para el primer resultado obtenido por la consulta. Establece un hint especfco del proveedor de persistencia a la consulta. Establece el valor para un parmetro con nombre name. Establece el valor para un parmetro con nombre name cuando el parmetro es del tipo Date Establece el valor para un parmetro con nombre name cuando el parmetro es del tipo Calendar Establece un posicional. valor para un parmetro

public Query setMaxResults(int maxResult)

public Query setFirstResult(int startPosition)

public Query setHint(String hintName, Object value)

public Query setParameter(String name, Object value)

public Query setParameter(String name, Date value, TemporalType temporalType)

public Query setParameter(String name, Calendar value, TemporalType temporalType)

public Query setParameter(int position, Object value) public Query setParameter(int position, Calendar value, TemporalType temporalType)

Establece un valor para un parmetro posicional cuando el parmetro es de tipo Calendar.

public Query setFlushMode( FlushModeType flushMode)

Establece el mod flush.

a. Ejemplo: query = em.createNamedQuery("findCategoryByName"); query.setParameter("categoryName", categoryName); (1) query.setMaxResults(10); query.setFirstResult(3); List categories = query.getResultList(); (2) En este ejemplo, creamos una instancia de una named query que definimos en un ejemplo anterior. Queremos recuperar una List de entidades de tipo Category por nombre, por tanto establecemos el parmetro (1). Limitamos el nmero de resultados devueltos a 10 e indicamos que la primera entidad devuelta sea la que ocupa la posicin 3. Finalmente, recuperamos el resultado (2) 3. Estableciendo parmetros para una query

Pgina 67 de 80

Persistencia con JPA EJB 3

El nmero de entidades recuperadas en una consulta puede limitarse especificando una clusula WHERE. Si quiesieramos recuperar todas las instancias de la entidad Item que tengan un precio especfico, la consulta JPQL podra ser como esta: SELECT i FROM Item i WHERE i.initialPrice = ?1 En esta sentencia, hemos usado un parmetro para la clausula WHERE. Hay dos maneras de especificar estos parmetros: mediante un nmero o mediante un nombre. Cuando tenemos un entero como parmetro, el parmetro se denomina posicional. Antes de ejecutar esta consulta, debemos suministrar un valor al parmetro posicional: query.setParameter(1, 100.00); Podemos especificar varios parmetros para una query: SELECT i FROM Item i WHERE i.initialPrice > ?1 AND i.initialPrice < ?2 En tal caso, antes de ejecutar la consulta, debemos dar valor a los dos parmetros: query.setParameter(1, 100.00); query.setParameter(2, 200.00); Cuando especificamos un nombre para un parmetro, el parmetro se denominda nombrado (named parameter). Este tipo de parmetro es ms recomendable, ya que mejoran la legibilidad del cdigo. El ejemplo anterios con una parmetro nombrado sera asi: SELECT i FROM Item i WHERE i.initialPrice = :price Para dar valor al parmetro antes de ejcutar la consulta: query.setParameter(price, 100.00); 4. Recuperar una sola entidad Para recuperar una sola instancia de una determinada entidad usamos el mtodo Query.getSingleResult. Hay que estar seguros de que la consulta devuelve un solo resultado, pues en el caso en que devuelva ms de un resultado, si usamos este mtodo el proveedor de persistencia lanzar una NonUniqueResultException. Si la consulta no devuelve ningn resultado, se lanzar una NoResultException. query.setParameter(1, "Recycle from Mr. Dumpster"); Category cat = (Category)query.getSingleResult(); 5. Recuperar una collecin de entidades Para recuperar ms de una instancia de una determinada entidad usamos el mtodo Query.getResultList. Si el mtodo no recupera ningn resultado para la consulta indicada, devolver una lista vaca. No se lanza ninguna excepcin. Por ejemplo, si queremos recuperar todas las instancias de la entidad Item cuyo precio este entre 100 y 200, usando named parameters: query.setParameter("lowPrice", lowPriceValue) query.setParameter("highPrice", highPriceValue) List items = query.getResultList(); 6. Paginacin de una lista Una consulta puede devolver cientos, miles o incluso millones de entidades. Supongamos que queremos crear un informe en el que cada pgina debe mostrar un mximo de 50 Pgina 68 de 80

Persistencia con JPA EJB 3

entidades y, cada vez que el usuario pulse en el botn Siguiente, las siguientes 50 entidades sean mostradas. JPA proporciona la habilidad de paginar los resultados de una consulta de manera sencilla: query.setMaxResults(50); query.setFirstResult(0); List items = query.getResultList(); El mtodo setMaxResults nos permite especificar el mximo de entidades que sern recuperadas en la lista resultado y el mtodo setFirstResult nos permite establecer la posicin del primer resultado a partir del cual contar. En el cdigo anterior, recuperbamos las primeras 50 entidades. Si queremos recuperar las 50 siguientes, deberamos hacer lo siguiente: query.setMaxResults(50); query.setFirstResult(50); List items = query.getResultList();

Pgina 69 de 80

Persistencia con JPA EJB 3

3.10. El lenguaje JPQL


La principal diferencia entre JPQL y SQL es que JPQL opera sobre clases y objetos (entidades) en el mundo Java. SQL, por su parte, opera sobre tablas, columnas y filas en el mundo de las bases de datos relacionales. Aunque parezcan muy parecidos a simple vista, operan en mundos completamente diferentes. Cuando definimos una consulta en JPQL, el JPQL Query Parser o Processor Engine del proveedor de persistencia traduce dicha sentecia JPQL al SQL nativo de la base de datos usada por el proveedor de persistencia:

3.10.1.

Tipos de sentencias

JPQL soporta tres tipos de sentencias: SELECT, UPDATE y DELETE: 1. Usando la sentencia SELECT

SELECT c FROM Category c WHERE c.categoryName LIKE :categoryName ORDER BY c.categoryId

Una consulta JPQL tiene (o puede tener) lo siguiente: Una clusula SELECT que especifica el tipo de objeto, entidad o valores a recuperar Una clusula FROM que especifica la entidad que ser usada por el resto de clusulas Una clusula WHERE (opcional) para filtrar los resultados devueltos por la consulta. Una clusula ORDER BY (opcional) para ordenar los resultados recuperados por la consulta. Una clusula GROUP BY (opcional) para ejecutar agregacin Una clusula HAVING (opcional) para ejecutar un filtrado en la agregacin.

2. Usando la sentencia UPDATE Cuando ejecutamos un UPDATE usando JPQL suele ser porque queremos actualizar ms de una entidad con la misma sentencia, ya que si no deberamos usar el mtodo merge que nos proporciona el EntityManager. Esto es lo que se llama un bulk update (actualizacin en masa). Slo se puede especificar un tipo de entidad en una sentencia UPDATE, y deberamos proporcionar una clusula WHERE que limitara el nmero de entidades afectadas por la sentencia: Pgina 70 de 80

Persistencia con JPA EJB 3

UPDATE entityName indentifierVariable SET single_value_path_expression1 = value1, ... single_value_path_expressionN = valueN WHERE where_clause

Por ejemplo, imaginemos que queremos proporcionar un status Gold y una tarifa de comisin del 10% a todos los vendedores cuyo apellido comience con Packrat: UPDATE Seller s SET s.status = 'G', s.commissionRate = 10 WHERE s.lastName like 'PackRat%' 3. Usando la sentencia DELETE De la misma manera que con la sentencia UPDATE, usar la sentencia DELETE de JPQL normalmente significa que queremos ejecutar un borrado en masa (bulk delete) de varias entidades con una sola sentencia.

DELETE entityName indentifierVariable WHERE where_clause

Por ejemplo, si queremos borrar todas las instancias de Seller con un status Silver usaramos la siguiente sentecia: DELETE Seller s WHERE s.status = 'Silver'

3.10.2.

La clusula FROM

La clusula FROM define el dominio de una consulta, esto es, los nombres de las entidades que sern usadas en la consulta. Se define de la siguiente manera:

FROM entityName [AS] identificationVariable

Los corchetes indican que la palabra reservada AS es opcional. El valor de entityName ser el nombre de la entidad a consultar y el valor identificationVariable es un identificador de variable o alias que podemos usar en el resto de sentencias para referirnos a la entidad a consultar. Ejemplo: FROM Category c Filtrando con WHERE

La clusula WHERE nos permite filtrar los resultados de una consulta. Slo qaqullas entidades que cumplan la condicin especificada en la consulta sern recuperadas. Cuando queremos recuperar todas las instancias de la entidad Category, podemos usar una sentencia JPQL sin clausula WHERE: SELECT c FROM Category c Pgina 71 de 80

Persistencia con JPA EJB 3

Si queremos recuperar un subconjunto de la entidad Category que cumpla una determinada condicin (en el ejemplo, que el campo categoryId sea mayor que 500) usamos una clausula WHERE para especificar dicha condicin: SELECT c FROM Category c WHERE c.categoryId > 500

3.10.3.

Expresiones condicionales y Operadores

Una condicin en una clusula WHERE que filtra los resultados de una consulta, se conoce como expresin condicin. Podemos construir expresiones condicionales usando los operadores soportados por JPQL, listados a continuacin por orden de precedencia:

Usando BETWEEN

El operador BETWEEN se usa en expresiones aritmticas para comparar una variable con un rango de valores:

path_expression [NOT] BETWEEN lowerRange and upperRange

Ejemplo: WHERE c.categoryId BETWEEN :lowRange AND :highRange Usando el operador IN

El operador IN nos permite crear expresiones condicionales basadas en si un path expresin existe en un lista de valores:

path_expression [NOT] IN (List_of_values)

Pgina 72 de 80

Persistencia con JPA EJB 3

Esta lista puede ser una lista de valores separados por comas, o una lista devuelta por una subquery. Supongamos que queremos recuperar una lista de usuarios donde el campo userId se encuentre entre los especificados en una lista: WHERE u.userId IN ('viper', 'drdba', 'dumpste') Si queremos recuperar la lista de usuario que NO existan en la lista anterior basta con negar el operador IN: WHERE u.userId NOT IN ('viper', 'drdba', 'dumpste') Por ltimo, un ejemplo del operador IN usando una subquery: WHERE c.user IN ( SELECT u FROM User u WHERE u.userType = 'A') Usando el operador LIKE

Este operador nos permite determinar si un campo simple coincide con un determinado patrn:

string_value_path_expression [NOT] LIKE pattern_value_

El valor de pattern_value es un literal o un parmetro (ya sea con nombre o numrico) Este patrn puede contener un guin bajo (_) o un tanto por ciento (%), el primero para indicar que puede ser cualquier carcter, el segundo para indicar cualquier secuencias de caracteres. Ejemplos: WHERE c.itemName LIKE '_ike' WHERE c.categoryName LIKE 'Recycle%' WHERE c.categoryName NOT LIKE '%Recycle%' WHERE c.categoryName NOT LIKE ?1 Trabajando con valores nulos y colecciones vacas

Para comprobar si un campo simple es nulo, podemos usar los operadores IS NULL o IS NOT NULL (ste ltimo para comprobar si un campo tiene un valor no nulo): WHERE c.parentCategory IS NOT NULL No podemos usar estos operadores para comprobar campos que no sean simples, tales como las colecciones de entidades. Para este propsito JPQL proporciona el operador IS [NOT] EMPTY: WHERE c.items IS EMPTY Por ltimo, si queremos comprobar si una determinada entidad pertenece a una coleccin de entidades, debemos usar el operador MEMBER OF. Este operador tiene la siguiente sintaxis:

entity_expression [NOT] MEMBER [OF] collection_value_path_expression

Pgina 73 de 80

Persistencia con JPA EJB 3

En este ejemplo, la expresin condicional devolver true si la instancia pasada (como :item) existe en la coleccin c.items para una entidad Category c: WHERE :item MEMBER OF c.items

3.10.4.

Trabajando con funciones JPQL

JPQL proporciona varias funciones predefinidas para ejecutar operaciones aritmticas o con cadenas. Estas funciones pueden usarse en clusulas WHERE o HAVING Funciones para tratamiento de cadenas

Ejemplos: WHERE CONCAT(u.firstName, u.lastName) = 'ViperAdmin' WHERE SUBSTRING(u.lastName, 1, 3) = 'VIP'

Pgina 74 de 80

Persistencia con JPA EJB 3

Funciones aritmticas

Ejemplo. La expresin devolver trae si el tamao de la lista c.items es 5, y false en otro caso: WHERE SIZE(c.items) = 5 Funciones con fechas

Destacar que los valores recuperados mediante estas funciones se obtienen de base de datos, por tanto, pueden variar ligeramente con respecto a los valores devueltos por la JVM, en caso de que la JVM y el SGBD no se ejecuten sobre el mismo servidor.

3.10.5.
Sintaxis:

La clusula SELECT

SELECT [DISTINCT] expression1, expression2, .... expressionN

Una clusula SELECT puede tener ms de una variable de identificacin (alias), uno o ms campos simples o funciones de agregacin separados por comas. Ejemplos: SELECT c FROM Category AS c SELECT c.categoryName, c.createdBy FROM Category c SELECT DISTINCT c.categoryName, c.createdBy FROM Category c

Pgina 75 de 80

Persistencia con JPA EJB 3

SELECT c.categoryName, c.items FROM Category La ltima sentencia es incorrecta, ya que c.items es una coleccin. Las colecciones no pueden ser especificadas en la clusula SELECT. Usando constructores en la clusula SELECT

Podemos usar un constructor en una clusula SELECT para devolver una o ms instancias de objetos Java inicializados con los datos devueltos para una consulta. NO es necesario que los objetos sean entidades (no es necesario que estn mapeados a la base de datos): SELECT NEW actionbazaar.persistence.ItemReport (c.categoryID, c.createdBy) FROM Category WHERE categoryId.createdBy = :userName

3.10.6.

Usando las funciones de agregacin

JPQL proporciona las siguientes funciones de agregacin: AVG, COUNT, MAX, MIN y SUM:

Ejemplos: o Si queremos encontrar el valor mximo para el campo i.itemPrice entre todas las entidades Item, usamos la siguiente consulta: SELECT MAX(i.itemPrice) FROM Item i o Si queremos averiguar cantas categoras existen en nuestro sistema usaremos: SELECT COUNT(c) FROM Category c

Pgina 76 de 80

Persistencia con JPA EJB 3

Agrupamiento con GROUP BY y HAVING

En las aplicaciones empresariales, muchas veces es necesario generar informes agrupando datos por algn campo persistente. Asumiendo que hay una relacin uno-a-muchos entre User y Category, la siguiente consulta generar un informe que lista el nmero de categoras creadas por cada usuario: SELECT c.user, COUNT(c.categoryId) FROM Category c GROUP BY c.user Como se puede ver en el ejemplo, hemos realizado la agrupacin sobre una entidad asociada (User). Podemos agrupar tambin por campos simples. Cuando usamos la clusula GROUP BY, slo se permite el uso de funciones de agregacin. Usando la clusula HAVING podemos filtrar el resultado de una consulta agregada: SELECT c.user, COUNT(c.categoryId) FROM Category c GROUP BY c.user HAVING COUNT(c.categoryId) > 5 Aparte, podemos tener una clusula WHERE en una consulta con la clusula GROUP BY: SELECT c.user, COUNT(c.categoryId) FROM Category c WHERE c.createDate is BETWEEN :date1 and :date2 GROUP BY c.user HAVING COUNT(c.categoryId) > 5

3.10.7.

Ordenando los resultados de una consulta

Podemos controlar el orden de los valores y objetos recuperados usando la clusula ORDER BY. Su sintaxis es la siguiente:

ORDER BY path_expression1 [ASC | DESC], ... path_expressionN [ASC | DESC]

Ejemplos: SELECT c FROM Category c ORDER BY c.categoryName ASC SELECT c.categoryName, c.createDate FROM Category c ORDER BY c.categoryName ASC, c.createDate DESC

Pgina 77 de 80

Persistencia con JPA EJB 3

3.10.8.

Usando subquerys

Una subquery es una query dentro de otra. Puedes usar subquerys dentro de las clusulas WHERE y HAVING. A diferencia de las subquerys en SQL, no se pueden tener subquerys en la clusula FROM en JPA. Sintaxis:

[NOT] IN / [NOT] EXISTS / ALL / ANY / SOME (subquery)

Ejemplos: Subquery con el operador IN: SELECT i FROM Item i WHERE i.user IN ( SELECT c.user FROM Category c WHERE c.categoryName LIKE :name) Subquery con operador EXISTS. Este operador comprueba si la subquery devuelve algn resultado. Devolver trae si la subquery devuelve al menos un resultado y false en caso contrario: SELECT i FROM Item i WHERE EXISTS ( SELECT c FROM Category c WHERE c.user = i.user) El resultado de esta query es el mismo que el del ejemplo anterior con el operador IN. Normalmente, es preferible usar el operador EXISTS en lugar de IN, sobre todo cuando la tabla subyacente tiene un gran nmero de resgistros. Esto es porque las bases de datos obtienen un mayor rendimiento usando EXISTS. Subquery con los operadores ANY, ALL y SOME. El uso de ANY, ALL y SOME es similar al uso del operador IN. Puedes usar estos operadores con cualquier operador de comparacin, como =, >, >=, <, <= y <> SELECT c FROM Category c WHERE c.createDate >= ALL (SELECT i.createDate FROM Item i WHERE i.user = c.user) Si incluimos el predicado ALL, las subquery devolver true si todos los resultados recuperados por ella cumplen la condicin, en otro caso, la expresin devolver false. En nuestro ejemplo, la subquery devuelve false si algn item en la subquery tienen una createDate mayor que la createDate para la categora en la query principal. Por su parte, si usamos ANY o SOME, la expresin devolver true si cualquiera de las entidades recuperadas en la subquery cumplen la condicin. Podemos usar ANY o SOME indistintamente, pues son sinnimos: SELECT c FROM Category c WHERE c.createDate >= ANY (SELECT i.createDate FROM Item i WHERE i.seller = c.user) Pgina 78 de 80

Persistencia con JPA EJB 3

3.10.9.

Joins entre entidades

El operador JOIN nos permite crear un producto cartesiano entre dos entidades. Normalmente, proporcionamos una clusula WEHRE para especificar la condicin de la JOIN, en lugar de slo crear un producto cartesiano. Tenemos que especificar las entidades en la clusula FROM para crear una JOIN entre dos o ms entidades. Las dos entidades sern unidas basndose en la relacin entre ellas, o bien, a partir de campos arbitrarios. Supongamos, por ejemplo, que queremos unir las entidades Category e Item usando la relacin entre ellas y recuperar slo aqullas que cumplen la condicin de unin (que estn relacionadas). Este tipo de uniones se conocen con el nombre de INNER JOIN. En cambio, supongamos que queremos recuperar los resultados que satisfagan la condicin de la JOIN, pero que tambin se incluyan las entidades de uno de los lados del dominio, aunque no estn relacionadas con ninguna entidad del otro lado. Por ejemplo, queremos recuperar todas las instancias de la entidad Category, aunque no tengan ninguna instancia de la entidad Item relacionada. Este tipo de JOIN se conoce con el nombre de OUTER JOIN. Las OUTER JOIN pueden ser LEFT, RIGTH, o de ambos lados, dependiendo del extremo de la relacin que queramos recuperar completo. INNER JOINs (REUNIONES NATURALES)

[INNER] JOIN join_association_path_expression [AS] identification_variable

Ejemplo: En nuestra aplicacin, la entidad Category y la entidad User tienen una relacin muchos-a-uno. Para recuperar todos los usuarios y sus categoras relacionadas haramos lo siguiente: SELECT u FROM User u INNER JOIN u.category c OUTER JOINS.

Las OUTER JOINs nos permiten recuperar las entidades adicionales que no cumplen la condicin de JOIN cuando las asociacin entre las entidades son opcionales. Son particularmente tiles para reporting. Asumamos que hay una relacin opcional entre las entidades User y Category y queremos generar un informe que muestre todas las categoras asociadas a cada usuario. Si el usuario no tiene ninguna categora asociada, entonces imprimiremos NULL. Si especificamos la entidad User en el lado izquierdo de la JOIN, podemos usar una LEFT JOIN (o LEFT OUTER JOIN) de la siguiente manera: SELECT u FROM User u LEFT OUTER JOIN u.category c WHERE u.userId LIKE ?1 Esta consulta recuperar todas las entidades User aunque no tengan ninguna categora asociada. Si usramos una INNER JOIN, los usuarios que no tuvieran ninguna categora asociada no seran devueltos. Pgina 79 de 80

Persistencia con JPA EJB 3

FETCH JOIN

En una aplicacin tpica, necesitamos consultar una entidad en particular, pero recuperando tambin sus entidades asociadas al mismo tiempo. Por ejemplo, cuando recuperamos una entidad Bid, queremos cargar e inicializar tambin la instancia del Bidder asociada a la puja: SELECT b FROM Bid b FETCH JOIN b.bidder WHERE b.bidDate >= :bidDate Las fetch join son muy tiles cuando tenemos la carga de relaciones perezosa activada en nuestra aplicacin, pero queremos realizar, en una coinsulta especfica, una carga ansiosa. Podemos usar las FETCH JOIN, tanto en INNER JOINs como en OUTER JOINs

Pgina 80 de 80

Você também pode gostar