Introducción práctica a JPA2

Post on 18-Jul-2015

161 views 5 download

Transcript of Introducción práctica a JPA2

Introducción práctica a JPA

(Java Persistence API)

Por: Manuel Schnidrig Año: 2011

Introducción

• Nuestro proyecto esta escrito en torno a objetos, sin embargo la mayoría de las soluciones actuales de Base de Datos se basan en relaciones y vínculos entre estas: son Bases de Datos Relacionales (RDB).

Introducción

• Los objetos contienen datos muy diversos y dinámicos. Un mismo objeto puede contener datos simples, datos complejos, datos de tamaño variable, colecciones variables de datos, incluso mas objetos dentro de éste!

Introducción

• Las tablas o relaciones por lo general almacenan objetos escalares y de un tamaño fijo conocido.

Introducción

• Problema planteado: ¿Cómo persistir nuestros objetos en un entorno de Base de Datos Relacional (RDB)?

Introducción

• El núcleo del problema reside en traducir estos objetos a formas que puedan ser almacenadas en la base de datos para recuperarlas fácilmente, mientras se preservan las propiedades de los objetos y sus relaciones; estos objetos se dice entonces que son persistentes.

Introducción

• Desde el punto de vista de un programador, el sistema debe lucir como un almacén de objetos persistentes. Uno puede crear objetos y trabajar normalmente con ellos, los cambios que sufran terminarán siendo reflejados en la base de datos.

Mapeo Objeto-relacional (ORM)

• Es una técnica de programación que sirve para convertir datos entre el sistema de tipos utilizado en un lenguaje de programación orientado a objetos y el utilizado en una Base de Datos Relacional.

• Se crea una «Base de Datos Orientada a Objetos» virtual.

Mapeo Objeto-relacional (ORM)

• Existen varios frameworks para distintos lenguajes y aplicaciones que nos realizan de manera automática este mapeo.

• En esta ocasión veremos como funciona JPA, el framework estándar de Java para realizar la persistencia de objetos.

¿Qué es JPA?

• Framework del lenguaje de programación Java que maneja datos relacionales en aplicaciones usando la Plataforma Java en sus ediciones Standard (Java SE) y Enterprise (Java EE).

• Es una abstracción sobre JDBC que nos permite realizar dicha correlación objeto-relacional de forma sencilla, realizando por nosotros toda la conversión entre nuestros objetos y las tablas de una base de datos.

¿Qué es JPA?

• Nos permite realizar tanto la conversión objetos-tabla así como la recuperación de estos objetos de las tablas correspondientes, sin utilizar ni siquiera una línea de SQL.

• Puede crear por nosotros el esquema completo de tablas, y modificaciones posteriores, simplemente a partir de nuestra estructura de objetos persistentes!

¿Qué es JPA?

• También nos permite basarnos en un esquema de tablas ya existente y modelarlo a través de objetos sin tener q adaptar ninguna tabla (el framework se adapta a las tablas y sus relaciones).

Componentes de JPA

• La API en sí misma, definida en javax.persistence.package

• La Java Persistence Query Language (JPQL)

• Metadatos objeto/relacional

Interfaz JPA

• JPA solo establece una interfaz común que es implementada por diversos proveedores de persistencia, dándonos la posibilidad de elegir entre varias implementaciones.

• Algunos ejemplos de proveedores de persistencia son:

Estructura de JPA

Conceptos básicos: Entidad

• El concepto principal sobre el que se apoya este framework es el de Entidad.

• Una entidad de JPA es el equivalente a lo que sería una entidad del modelo Entidad-Relación

• Una entidad es la mínima unidad de persistencia.

• Modelamos las entidades mediante clases Java que cumplen ciertos requisitos.

Entidad: Requisitos

• Las entidades se modelan mediante POJO’s (Plain Old Java Objects)

• La clase entidad debe cumplir cuatro requisitos:

– Tener un constructor sin argumentos (public o protected).

– Ser una clase de primer nivel (no interna).

– No ser declarada final.

– Implementar la interface java.io.Serializable si es accedida remotamente.

Entidad: Requisitos

• Generalmente, las clases de entidad serán simples clases que solo contendrán variables de instancia y sus respectivos getters y setters.

Entidad: Requisitos

Entidad: ¿Qué se persiste?

• Por defecto, el estado persistente de un objeto entidad es representado mediante sus variables de instancia (field access)

• Si se desea, puede indicarse al framework que en vez de persistir las variables de instancia utilice los getters y setters del objeto para obtener los datos a persistir (property access)

Relaciones

• El segundo concepto básico es el de las relaciones.

• Al igual que en el modelo Relacional, estas vincularán ciertas entidades entre si, con una cierta cardinalidad y una cierta navegabilidad.

• Las representaremos mediante punteros a las entidades correspondientes dentro de la entidad correspondiente.

Relaciones

Metadatos

• ¿Cómo le indicamos al framework cuales son nuestras entidades persistentes y las diversas configuraciones de las mismas?

• Dos opciones:

– Mediante un archivo de configuración XML

– Mediante Anotaciones en el código fuente

Anotaciones ¿Qué son?

• Son una forma de añadir metadatos al código fuente Java que están disponibles para la aplicación en tiempo de ejecución.

• El compilador Java almacena los metadatos de la Anotación en los archivos de clases. Posteriormente, la JVM u otros programas pueden buscar los metadatos para determinar cómo interactuar con los elementos del programa o cambiar su comportamiento.

Anotaciones ¿Qué aportan?

• Información para el compilador (ej.: Detectar errores, suprimir advertencias)

• Procesamiento de tiempo de compilador y de tiempo de instalación (ej.: Generación de XML)

• Procesamiento de tiempo de ejecución

Anotaciones

• Ej: JavaDoc.

Anotaciones

Configuración de las entidades mediante anotaciones

• @Entity: Le indica al framework cuales son nuestros objetos que se van a persistir.

• La clase marcada con esta anotación debe cumplir todos los requisitos anteriormente mencionados

Configuración de las entidades mediante anotaciones

Entidades: Propiedades persistentes

• Lo que se persistirá en nuestra DB por defecto son los valores de las variables de instancia (propiedades) de nuestras clases de entidad.

• Excepto que se lo indiquemos explícitamente, todas las propiedades de la entidad serán persistidas, al menos las de tipos básicos.

Entidades: Identidad

• Toda entidad debe tener al menos una propiedad marcada como identidad, cuyo valor para cada instancia particular la diferencie del resto.

• La variable de instancia que identificará a la entidad debe ser marcada con la anotación @Id

Entidades: Identidad

• Podemos también indicarle al proveedor de persistencia que nos genere automáticamente el valor de este campo a medida que creamos los objetos.

• La manera más sencilla de hacer esto es anotando al identificador con la anotación @GeneratedValue

Entidades: Identidad

Entidades: Configuración por defecto

• JPA trabaja por configuración por excepción

• Si no se especifica nada, se asume la configuración por defecto

• Las únicas anotaciones requeridas para que una entidad sea funcional son @Entity y @Id

Entidades: Propiedades no persistentes

• La manera de indicar que una propiedad específica de una entidad no debe ser persistida, es mediante la anotación @Transient

• Es útil para marcar propiedades derivables.

Entidades: @Table y @Column

• Podemos especificar de manera específica el nombre que tendrán en la DB los elementos mapeados.

• Muy útil si la aplicación debe utilizar un esquema de BD existente.

Entidades: @Table y @Column

• @Table nos permite definir el nombre que tendrá la tabla que será mapeada en base a una entidad.

• @Column sirve para indicar explícitamente el nombre que llevará la columna que guarde el estado de la propiedad marcada.

• Si no se utilizan las anotaciones @Table y @Column, JPA asignará los nombres de las clases y las propiedades.

Entidades: @Table y @Column

Entidades: Tipo de acceso a las propiedades

• Permite especificar la manera en que JPA accederá a los datos de la entidad para realizar el mapeo.

• Puede ser de dos tipos:

– Acceso a variable (Field access)

– Acceso a propiedad (Property access)

Entidades: Field Access

• Se realiza el mapeo a través de las propias variables de instancia de la entidad.

• Hay una correlación directa datos del objeto-datos en los campos de las tablas.

• Anotaciones colocadas sobre las variables de instancia.

Entidades: Property Access

• El mapeo se hace a través de los métodos getters y setters de la entidad.

• Permite una mayor encapsulación de los datos.

• Permite aplicar validaciones antes de cargar los datos desde la DB.

• Hay que asegurarse que su estructura permita recuperar exactamente el estado.

• Las anotaciones se realizan sobre los «getters» de la entidad

Entidades: Tipo de acceso a las propiedades

Entidades: Mapeo de tipos Básicos

• Los tipos mas simples de Java son mapeados directamente en la BD como columnas del tipo mas aproximado al tipo de Java (que permita recuperar exactamente los valores).

• Por lo general no es necesario marcarlos, al menos que necesitemos indicar alguna opción de mappeado. Si es así, se utiliza la marca @Basic.

Entidades: Acceso temprano y demorado

• Podemos indicar el momento en que una propiedad específica va a ser realmente obtenida de la BD.

• Esto nos permite ahorrarnos el coste de cargar dicha propiedad inmediatamente si por su naturaleza o por su ubicación es ineficiente traerla sino hasta el momento de su utilización.

Entidades: Acceso temprano y demorado

• Por defecto, la mayoría de los tipos Java es obtenido mediante una lectura temprana. De todos modos, puede explicitarse, por ejemplo en un tipo básico, mediante

– @Basic(fetch = FetchType.EAGER)

• Para indicar explícitamente la lectura demorada sobre un tipo básico utilizamos:

– @Basic(fetch = FetchType.LAZY)

Entidades: Acceso temprano y demorado

Entidades: Mapeo de colecciones

• Una entidad puede tener propiedades de tipo java.util.Collection y/o java.util.Map que contengan tipos básicos.

• Los elementos de estas colecciones serán almacenados en una tabla diferente a la que contiene la entidad donde están declarados.

• El tipo de colección tiene que ser concreto (como ArrayList o HashMap) y nunca una interface.

Entidades: Mapeo de colecciones

• Podemos definir la lectura temprana o demorada de los elementos de estas colecciones mediante la anotación:

– @ElementCollection(fetch = FetchType.EAGER)

– @ElementCollection(fetch = FetchType.LAZY)

• Por defecto la lectura será demorada (Lazy).

Entidades: Mapeo de tipos enumerados

• Podemos mapear tipos enumerados (enum) mediante la anotacion @Enumerated

• La configuración por defecto de JPA mapeará cada valor ordinal de un tipo enumerado a una columna de tipo numérico en la base de datos.

Entidades: Mapeo de tipos embebidos

• Los tipos insertables (embeddables) son objetos que no tienen identidad, por lo que para ser persistidos deben ser primero insertados dentro de una entidad.

• Cada propiedad del tipo insertable será mapeada a la tabla de la entidad que lo contenga, como si fuera una propiedad declarada en la propia entidad.

Entidades: Mapeo de tipos embebidos

• Definimos un tipo insertable con la anotación @Embeddable

• Y lo insertamos en una entidad (u otro tipo insertable) con la anotación @Embedded

Asociaciones entre entidades

• Los modelos de datos incluyen asociaciones entre las distintas entidades que lo comprenden.

Asociaciones entre entidades

• Estas asociaciones incluyen además de las entidades involucradas, una cardinalidad que indica la participación de cada parte en la asociación.

Asociaciones entre entidades

• Otro concepto involucrado es la navegabilidad, que nos permite determinar la visión que tiene cada entidad sobre la relación en cuestión.

Asociaciones entre entidades

• En una Base de datos relacional, estas asociaciones están representadas mediante campos de clave foranea referenciando a algún campo clave de alguna tabla que o bien es la entidad referenciada o bien es una tabla auxiliar para acceder a los datos referenciados.

Asociaciones entre entidades

Asociaciones entre entidades

Asociaciones entre entidades

• En nuestro modelo de objetos, las asociaciones simplemente se representan mediante punteros al objeto o a los objetos en cuestión.

Asociaciones entre entidades

Asociaciones entre entidades

• JPA nos gestiona automáticamente las relaciones binarias, creando automáticamente las tablas auxiliares necesarias.

• Según su navegabilidad, existen dos tipos de relaciones binarias:

– Relaciones unilaterales

– Relaciones bilaterales

Asociaciones entre entidades

• Para que el framework realmente maneje estas asociaciones, necesitamos indicarlas mediante las anotaciones correspondientes.

– @OneToOne

– @OneToMany

– @ManyToOne

– @ManyToMany

Asociaciones unidireccionales

• Solo requieren especificar del lado que es dueño de la relación (el lado que «ve» la misma) la propiedad que referencia al objeto más la anotación correspondiente al tipo de asociación.

Asociaciones unidireccionales

Asociaciones bidireccionales

• Requieren de un lado dueño de la relación, el cual deberá ser anotado de la misma manera que en las relaciones unidireccionales, y un lado no-dueño o lado inverso, el cual deberá tener la referencia a su dueño mas la anotación inversa correspondiente modificada por la opción «mappedBy»

Asociaciones bidireccionales

Asociaciones: Ejemplos «OneToOne»

• Es la relación mas sencilla.

• En relaciones bidireccionales la foreign key se coloca del lado dueño de la relación.

Asociaciones: Ejemplos «OneToOne»

Asociaciones: Ejemplos «OneToOne»

(Bidireccional)

Asociaciones: Ejemplos «OneToOne»

(Bidireccional)

Asociaciones: Ejemplos «OneToMany» y «ManyToOne»

• Veremos primero el caso bidireccional, que es el mas sencillo y se aplica también para relaciones ManyToOne unidireccionales.

• En este caso, el dueño de la relación debe ser el lado «Many»

Asociaciones: Ejemplos «OneToMany» y «ManyToOne»

Asociaciones: Ejemplos «ManyToMany»

• Requieren de una tabla auxiliar intermedia que contenga pares de asociación.

Asociaciones: Ejemplos «ManyToMany»

Asociaciones: Ejemplos «ManyToMany»

• Podemos especificar los nombres que tendrán tanto la tabla auxiliar como sus columnas con la anotación @JoinTable en el lado dueño

Asociaciones: Ejemplos «ManyToMany»

• Una relacion ManyToMany puede ser unidireccional.

• El mapeado será realizado de la misma manera, solo que una de las entidades nunca usara la tabla de join.

Asociaciones: Ejemplos «OneToMany» unidireccional

• Dado que no podemos colocar una cantidad variable de foreign keys en la tabla de la entidad, requeriremos aquí también una tabla auxiliar (Join Table)

Asociaciones: Ejemplos «OneToMany» unidireccional

Persistencia

• Con lo visto hasta acá, podemos construir nuestros objetos del negocio e indicar (mediante anotaciones) la forma en que se relacionan estas entidades y como queremos que se persistan en la DB.

• Pero necesitamos ahora un mecanismo que haga efectiva esta persistencia.

Persistencia

• Podríamos pensar que la simple manipulación de los objetos declarados como entidades resultará en la persistencia inmediata de los cambios realizados en la DB; En realidad no funciona de esa manera, y tampoco sería una forma eficiente de hacerlo.

• Necesitamos más control sobre que queremos persistir y cuando.

Persistencia: Entity Manager

• Este control esta dado por un conjunto de acciones encapsuladas en un objeto denominado «EntityManager».

• Un EntityManager nos permite básicamente realizar todas las operaciones de persistencia (CRUD) de los objetos desde y hacia la DB.

Persistencia: Entity Manager

Persistencia: Persistence Context

• Para JPA, una entidad puede estar en uno de los dos estados siguientes:

– Managed (gestionada)

– Detached (separada)

• Al crear un entityManager, se asocia al mismo un «Contexto de Persistencia» (inicialmente vacío) el cuál contiene el conjunto de entidades que están siendo manejadas por él.

Persistencia: Persistence Context

• A medida que realizamos operaciones sobre nuestras entidades a través de un Entity Manager, éstas pasan a pertenecer al Contexto de Persistencia de éste.

• Las entidades que se encuentran gestionadas por algun EntityManager son sincronizadas con la Base de Datos mientras pertenezcan a ese Contexto de Persistencia.

Persistencia: Transacciones

• El EntityManager trabaja mediante Transacciones.

• Podemos trabajar con dos tipos de transacciones:

– Manejada por la aplicacion (RESOURCE_LOCAL)

– Manejada por el contenedor (JTA)

• Utilizaremos RESOURCE_LOCAL.

Persistencia: Transacciones

Persistencia: Peristence Unit

• Los EntityManager son configurados para persistir ciertos objetos, para trabajar contra una base de datos en particular, y para utilizar un determinado tipo de transacciones.

• Esta configuración es agrupada en lo que se denomina un Unidad de Persistencia («PersistenceUnit»)

Persistencia: Resumen

• El mapeo de entidades se realiza a través de transacciones utilizando un Entity Manager.

• Las entidades que está manejando actualmente un Entity Manager se denomina su Contexto de Persistencia.

• La configuración específica de los componentes que realizan el mapeo (ORM) se denomina Unidad de Persistencia.

Utilizando los EM

• Se verá como utilizar los Entity Manager en un entorno Java SE, es decir, «Entity Managers administrados por la aplicación».

• En entornos Java EE normalmente se utilizan Entity Managers administrados por el contenedor, los cuales si bien su administración es mas sencilla, no es posible utilizalos en aplicaciones Java SE.

Configurando un «Persistence Unit»

• Cada Unidad de Persistencia tiene un nombre y es declarada mediante un archivo xml.

• Básicamente especificamos tres cosas en una configuración determinada:

– Nuestras clases marcadas como entities.

– El tipo de transacciones a utilizar.

– El driver JDBC a utilizar para la conexión con la DB.

Persistence Unit

Persistence Unit

Persistence Unit

Persistence Unit

Obtención de un Entity Manager

• Al trabajar Con EntityMangers administrados por la aplicación, debemos explicitamente configurar y crear nuestros EntityManagers, iniciar las transacciones, hacer commit o rollback de las mismas, etc.

Obtención de un Entity Manager: Paso previo: Factory

• Esta factory nos permitirá obtener Entity Manager’s pertenecientes a una unidad de persistencia particular.

• Al crear la factory, debemos pasarle el nombre de la Unidad de Persistencia que queremos que utilice para crear nuestros Entity Manager.

EntityManager: Ciclo Básico de trabajo

Operaciones básicas

• Con lo visto, ya podemos comenzar a persistir y traer nuestros objetos de la DB.

• A continuación se presenta un conjunto de operaciones básicas que podemos realizar con nuestro Entity Manager una vez creado.

Persistir una entidad

• Utilizamos el método persist()

• Si el Entity Manager por alguna razón no puede completar esta acción, arroja una «PersistenceException»

Recuperar una entidad vía #id

• Podemos realizar esta acción directamente mediante el método find().

• Si la entidad cuyo id es el pasado como argumento no existe en la BD, find() retorna null.

Borrar una entidad

• Utilizamos el método remove()

• La entidad a borrar debe estar dentro del contexto de persistencia del Entity Manager.

Actualizar una entidad

• Para actualizar una entidad, trabajamos directamente sobre la entidad en cuestión, es decir no le avisamos al Entity Manager de los cambios a realizar, simplemente los efectuamos.

• Los cambios serán persistidos en la BD al momento del cierre de la transacción.

Actualizar una entidad

• Es importante aclarar que la entidad a modificar debe estar dentro del Contexto de Persistencia, de otra manera el Entity Manager nunca podrá saber que se han realizado cambios sobre tal entidad y por ende nunca los persistirá.

• Podemos hacer que una entidad desasociada de nuestro contexto pase a formar parte de el utilizando el comando merge()

Actualizar una entidad

Ciclo de vida de las entidades

Obtención avanzada de objetos

• Podemos realizar consultas sobre la DB utilizando un lenguaje muy parecido a SQL pero orientado a objetos, el JPQL

• JPA 2.0 nos permite crear consultas tipadas

JPQL: Ejemplo

• Con getResultList() obtenemos una lista de los objetos que resultan de la ejecucion de la consulta.

• Si realizamos una consulta la cual estamos seguros que retorna un único objeto como resultado, usamos getSingleResult()

Patrametros

• La utilización de parametros permite reutilizar una misma query.

• Esto es más eficiente que modificar el String y crear otra query ya que no debe recompilarse la consulta.

Otras caracteristicas

• Mapeo de objetos embebidos y enumerados

• Identificadores compuestos

• Restricciones en operaciones en cascada, orphan remove

• Herencia de entidades

• JPA Criteria API

Por donde seguir?

• Buenos tutoriales en internet:

– David Marco, Introducción a JPA: http://www.davidmarco.es/blog/entrada.php?id=144

– Tutorial oficial de Java EE 6, sección JPA: http://docs.oracle.com/javaee/6/tutorial/doc/

• Documentaciones de las páginas de los proveedores de persistencia

• Libros:

– Pro JPA 2: Mastering the Java™ Persistence API