Iniciación JPA

download Iniciación JPA

If you can't read please download the document

Transcript of Iniciación JPA

Introduccin a JPAIntroduccin a JPA................................................................................................................................2 1.1 INICIO ......................................................................................................................................2 1.2 UNA ENTIDAD SIMPLE.........................................................................................................2 1.3 IDENTIDAD.............................................................................................................................3 1.4 CONFIGURACIN POR DEFECTO ......................................................................................3 1.5 LECTURA TEMPRANA Y LECTURA DEMORADA ...........................................................4 1.6 TIPOS ENUMERADOS............................................................................................................5 1.7 TRANSIENT ............................................................................................................................6 1.8 COLECCIONES BSICAS .....................................................................................................7 1.9 TIPOS INSERTABLES ............................................................................................................7 1.10 TIPO DE ACCESO .................................................................................................................8 2.1 ASOCIACIONES ...................................................................................................................10 2.2 ASOCIACIONES UNIDIRECCIONALES ...........................................................................10 2.3 ASOCIACIONES BIDIRECCIONALES................................................................................12 2.4 LECTURA TEMPRANA Y LECTURA DEMORADA DE ASOCIACIONES ....................12 2.5 ORDENACIN DE ASOCIACIONES ..................................................................................13 2.6 HERENCIA ............................................................................................................................13 2.7 MAPPED SUPERCLASSES, CLASES ABSTRACTAS Y NO-ENTIDADES ....................16 3.1 PERSISTENCIA .....................................................................................................................18 3.2 TRANSACCIONES ...............................................................................................................19 3.3 ESTADO DE UNA ENTIDAD ..............................................................................................20 3.4 PERSISTIR UNA ENTIDAD .................................................................................................20 3.5 LEER UNA ENTIDAD ..........................................................................................................23 3.6 ACTUALIZAR UNA ENTIDAD ...........................................................................................23 3.7 ELIMINAR UNA ENTIDAD..................................................................................................24 3.8 OPERACIONES EN CASCADA ...........................................................................................25 3.9 MTODOS CALLBACK .......................................................................................................25 3.10 CLASES LISTENER ............................................................................................................26 4.1 JPQL BSICO ........................................................................................................................29 4.2 SENTENCIAS CONDICIONALES .......................................................................................30 4.3 PARMETROS DINMICOS ..............................................................................................31 4.4 ORDENAR LOS RESULTADOS ..........................................................................................32 4.5 OPERACIONES DE ACTUALIZACIN .............................................................................32 4.6 OPERACIONES DE BORRADO ..........................................................................................33 4.7 EJECUCION DE SENTENCIAS JPQL .................................................................................33 4.8 EJECUCIN DE SENTENCIAS CON PARMETROS DINMICOS ..............................34 4.9 CONSULTAS CON NOMBRE (ESTATICAS) ......................................................................35 4.10 CONSULTAS NATIVAS SQL ..............................................................................................36 4.11 FINAL ...................................................................................................................................37 ANEXO: Configuracion de un entorno para JPA .........................................................................37

Introduccin a JPA Con esta entrada se da comienzo a una serie de cuatro artculos que introducen JPA en su versin 2.0. En ellos vamos a ver, de forma introductoria, como realizar ORM de forma sencilla mediante JPA. Los artculos seguirn este orden: 1. ORM Bsico 2. Asociaciones y Herencia 3. Persistencia, Transacciones, Callbacks y Lsteners 4. JPQL Tanto los cuatro artculos del tutorial, as como el anexo que los acompaa (donde se explica como poner en marcha un entorno de desarrollo para probar todo el cdigo que se ir mostrando), estn disponibles en la pgina de tutoriales.

1.1 INICIO En Java solucionamos problemas de negocio a travs de objetos, los cuales tienen estado y comportamiento. Sin embargo, las bases de datos relacionales almacenan la informacin mediante tablas, filas, y columnas, de manera que para almacenar un objeto hay que realizar una correlacin entre el sistema orientado a objetos de Java y el sistema relacional de nuestra base de datos. JPA (Java Persistence API - API de Persistencia en Java) es una abstraccin sobre JDBC que nos permite realizar dicha correlacin de forma sencilla, realizando por nosotros toda la conversin entre nuestros objetos y las tablas de una base de datos. Esta conversin se llama ORM (Object Relational Mapping - Mapeo Relacional de Objetos), y puede configurarse a travs de metadatos (mediante xml o anotaciones). Por supuesto, JPA tambin nos permite seguir el sentido inverso, creando objetos a partir de las tablas de una base de datos, y tambin de forma transparente. A estos objetos los llamaremos desde ahora entidades (entities). JPA establece una interface comn que es implementada por un proveedor de persistencia de nuestra eleccin (como Hibernate, Eclipse Link, etc), de manera que podemos elegir en cualquier momento el proveedor que ms se adecue a nuestras necesidades. As, es el proveedor quin realiza el trabajo, pero siempre funcionando bajo la API de JPA.

1.2 UNA ENTIDAD SIMPLE Este es el ejemplo ms simple de entidad: package es.davidmarco.tutorial.jpa; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class Pelicula {

@Id @GeneratedValue private Long id; private String titulo; private int duracion; // Getters y Setters } Las entidades suelen ser POJOs. La clase Pelicula se ha anotado con @Entity, lo cual informa al proveedor de persistencia que cada instancia de esta clase es una entidad. Para ser vlida, toda entidad debe: - Tener un constructor por defecto - Ser una clase de primer nivel (no interna) - No ser final - Implementar la interface java.io.Serializabe si es accedida remotamente La configuracin de mapeo puede ser especificada mediante un archivo de configuracin XML, o mediante anotaciones. A esta configuracin del mapeo la llamamos metadatos. Para mantener las cosas simples, este tutorial utilizar la segunda opcin (anotaciones). Cual de ellas elegir depende de preferencias tanto personales como del proyecto. El descriptor XML tiene como ventaja que no es necesario recompilar nuestro cdigo para cambiar la configuracin de mapeo; por contra, exige mantener un archivo externo. La configuracin mediante anotaciones permite tener en un mismo lugar el cdigo Java y sus instrucciones de comportamiento; por contra, exige una recompilacin cada vez que deseamos cambiar el comportamiento del mapeo, adems de resultar en cdigo menos portable (otra aplicacin no-JPA que use nuestras entidades deber incluir todas las libreras JPA en su classpath para poder compilar correctamente). Cuando ambas opciones son utilizadas al mismo tiempo, la configuracin XML prevalece sobre las anotaciones, de manera que si el descriptor XML establece unos atributos para una propiedad que tambin ha sido configurada mediante anotaciones, sern los primeros los que el proveedor de persistencia tenga en cuenta.

1.3 IDENTIDAD Todas las entidades tienen que poseer una identidad que las diferencie del resto, por lo que deben contener una propiedad marcada con la anotacin @Id (es aconsejable que dicha propiedad sea de un tipo que admita valores null, como Integer en lugar de int). Existen formas ms complejas de identidad (@EmbeddedId, @IdClass) que no vamos a explicar aqu, para mantener los ejemplos sencillos. De manera adicional, y en lo que respecta a este tutorial, la identidad de una entidad va a ser gestionada por el proveedor de persistencia, as que ser dicho proveedor quien le asigne un valor la primera vez que almacene la entidad en la base de datos. Para tal efecto, le aadimos a la propiedad de identidad la anotacin @GeneratedValue.

1.4 CONFIGURACIN POR DEFECTO

JPA aplica a las entidades que maneja una configuracin por defecto, de manera que una entidad es funcional con una mnima cantidad de informacin (las anotaciones @Entity y @Id en nuestro caso). Con esta configuracin por defecto, todas las entidades del tipo Pelicula sern mapeadas a una tabla de la base de datos llamada PELICULA, y cada una de sus propiedades ser mapeada a una columna con el mismo nombre (la propiedad id ser mapeada en la columna ID, la propiedad titulo ser mapeada en la columna TITULO, etc). Sin embargo, no siempre es posible ceirse a los valores de la configuracin por defecto: imaginemos que tenemos que trabajar con una base de datos heredada, con nombres de tablas y filas ya definidos. En ese caso, podemos configurar el mapeo de manera que se ajuste a dichas tablas y filas: @Entity @Table(name = "TABLA_PELICULAS") public class Pelicula { @Id @GeneratedValue @Column(name = "ID_PELICULA") private Long id; ... } La anotacin @Table nos permite configurar el nombre de la tabla donde queremos almacenar la entidad mediante el atributo name. As mismo, mediante la anotacin @Column podemos configurar el nombre de la columna donde se almacenar una propiedad, si dicha propiedad puede contener un valor null, etc. Puedes descargar la especificacin completa de JPA 2.0 desde aqu.

1.5 LECTURA TEMPRANA Y LECTURA DEMORADA Cuando leemos una entidad desde la base de datos, ciertas propiedades pueden no ser necesarias en el momento de la creacin del objeto. JPA nos permite leer una propiedad desde la base de datos la primera vez que un cliente intenta leer su valor (lectura demorada), en lugar de leerla cuando la entidad que la contiene es creada (lectura temprana). De esta manera, si la propiedad nunca es accedida, nos evitamos el coste de crearla. Esto puede ser til si la propiedad contiene un objeto de gran tamao: @Basic(fetch = FetchType.LAZY) private Imagen portada; La propiedad portada es un objeto que representa una imagen de la portada de una pelcula (un objeto de gran tamao). Puesto que el coste de crear este objeto al leer la entidad Pelicula es muy alto, lo hemos marcado como una propiedad de lectura demorada mediante la anotacin @Basic(fetch = FetchType.LAZY). El comportamiento por defecto de la mayora de tipos Java es el contrario (lectura temprana). Este comportamiento, a pesar de ser implcito, puede ser declarado explcitamente de la siguiente manera:

@Basic(fetch = FetchType.EAGER) private Imagen portada; Solo los objetos de gran tamao y ciertos tipos de asociacin (concepto que veremos en el segundo artculo de este tutorial) deben ser leidos de forma demorada. Si, por ejemplo, marcamos todas las propiedades de tipo int, String o Date de una entidad con lectura demorada, cada vez que accedamos por primera vez a cada una de ellas el proveedor de persistencia tendr que hacer una llamada a la base de datos para leerla. Esto, evidentemente, va a provocar que se efecten multitud de llamadas a la base de datos cuando con solo una (al crear la entidad en memoria) podran haberse leido todas con apenas coste. Por tanto, es importante tener presente que la manera de configurar el tipo de lectura de una propiedad puede afectar enormemente al rendimiento de nuestra aplicacin. Por otro lado, la anotacin @Basic solo puede ser aplicada a tipos primitivos, sus correspondientes clases wrapper, BigDecimal, BigInteger, Date, arrays, algunos tipos del paquete java.sql, enums, y cualquier tipo que implemente la interface Serializable. Adems del atributo fetch, la anotacin @Basic admite otro atributo, optional, el cual permite definir si la propiedad sobre la que se est aplicando la anotacin puede contener el valor null (esto es debido a que en bases de datos relacionales, algunas columnas pueden definir una constriccin de tipo non null, la cual impide que se inserte un valor null; por tanto, con @Basic(optional=false) nos ajustaramos a la citada constriccin).

1.6 TIPOS ENUMERADOS JPA puede mapear los tipos enumerados (enum) mediante la anotacin Enumerated: @Enumerated private Genero genero; La configuracin por defecto de JPA mapear cada valor ordinal de un tipo enumerado a una columna de tipo numrico en la base de datos. Por ejemplo, siguiendo el ejemplo anterior, podemos crear un tipo enum que describa el gnero de una pelcula: public enum Genero { TERROR, DRAMA, COMEDIA, ACCION, } Si la propiedad genero del primer ejemplo tiene el valor Genero.COMEDIA, en la columna correspondiente de la base de datos de insertar el valor 2 (que es el valor ordinal de Genero.COMEDIA). Sin embargo, si en el futuro introducimos un nuevo tipo de

genero en una posicin intermedia, o reordenamos las posiciones de los generos, nuestra base de datos contendr valores erroneos que no se correspondern con los nuevos valores ordinales del tipo enumerado. Para evitar este problema potencial, podemos forzar a la base de datos a utilizar una columna de texto en lugar de una columna numrica: de esta manera, el valor almacenado ser el nombre del valor enum, y no su valor ordinal: @Enumerated(EnumType.STRING) private Genero genero;

1.7 TRANSIENT Ciertas propiedades de una entidad pueden no representar su estado. Por ejemplo, imaginemos que tenemos una entidad que representa a una persona: @Entity public class Persona { @Id @GeneratedValue private Long id; private String nombre; private String apellidos private Date fechaNacimiento; private int edad; // getters y setters } Podemos considerar que la propiedad edad no representa el estado de Persona, ya que si no es actualizada cada cumpleaos, terminar conteniendo un valor erroneo. Ya que su valor puede ser calculado gracias a la propiedad fechaNacimiento, no vamos a almacenarlo en la base de datos, si no a calcular su valor en tiempo de ejecucin cada vez que lo necesitemos. Para indicar al proveedor de persistencia que ignore una propiedad cuando realice el mapeo, usamos la anotacin @Transient: @Transient private int edad; Ahora, para obtener el valor de edad utilizamos su metodo getter: public int getEdad() { // calcular la edad y devolverla }

1.8 COLECCIONES BSICAS Una entidad puede tener propiedades de tipo java.util.Collection y/o java.util.Map que contengan tipos bsicos (todos los tipos wrapper, String, BigDecimal, BigInteger, tipos enumerados y tipos insertables; estos ltimos los veremos en la prxima seccin). Los elementos de estas colecciones sern almacenados en una tabla diferente a la que contiene la entidad donde estn declarados. El tipo de coleccin tiene que ser concreto (como ArrayList o HashMap) y nunca una interface. private ArrayList comentarios; El cdigo de arriba es mapeado por defecto con unos valores predeterminados por JPA, como vimos en la seccin 1.4. Y por supuesto, podemos cambiar dicha configuracin por defecto mediante diversas anotaciones, entre las que se encuentran: @ElementCollection(fetch = FetchType.LAZY) @CollectionTable(name = "TABLA_COMENTARIOS") private ArrayList comentarios; @ElementCollection permite definir el tipo de lectura (temprana o demorada), as como la clase de objetos que contiene la coleccin (obligatorio en caso de que la coleccin no sea de tipo generico). Por otro lado, @CollectionTable nos permite definir el nombre de la tabla donde queremos almacenar los elementos de la coleccin. Si nuestra colecin es de tipo Map, podemos aadir la anotacin @MapKeyColumn(name = "NOMBRE_COLUMNA"), la cual nos permite definir el nombre de la columna donde se almacenarn las claves del Map.

1.9 TIPOS INSERTABLES Los tipos insertables (embeddables) son objetos que no tienen identidad, por lo que para ser persistidos deben ser primero insertados dentro de una entidad (u otro insertable que ser a su vez insertado en una entidad, etc). 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. Definimos un tipo insertable con la anotacin @Embeddable: @Embeddable public class Direccion { private String calle; private int codigoPostal; ... } Y lo insertamos en una entidad (u otro tipo insertable) con la anotacin @Embedded

@Entity public class Persona { ... @Embedded private Direccion direccion; } Ahora, la tabla que contenga las entidades de tipo Persona contendr sus propias columnas, ms las definidas en el tipo insertable Direccion.

1.10 TIPO DE ACCESO Para terminar este primer artculo del tutorial de introduccion a JPA, vamos a ver que son los tipos de acceso, y como aplicarlos correctamente. JPA permite definir dos tipos de acceso: - Acceso a variable (Field access) - Acceso a propiedad (Property access) El tipo de acceso que usar una entidad est definido por el lugar donde situemos sus anotaciones de mapeo. Si las anotaciones estn en las variables que conforman la clase (como hemos hecho hasta ahora), estaremos indicando a JPA que debe realizar acceso a variable New > JPA Project Escribimos un nombre para el proyecto (en mi caso IntroduccionJPA). Si hemos instalado un contenedor compatible con Java EE 6, podremos seleccionarlo en esta pantalla. Si no, dejamos todo por defecto. Para continuar pulsamos Next. En la siguiente pantalla podemos pulsar Finish para terminar, o Next si queremos seleccionar un proveedor de persistencia (por defecto es EclipseLink 1.1.0). Cuando finalmente pulsemos Finish, nos aparecera una ventana preguntandonos si queremos asociar la perspectiva JPA al proyecto. Eclipse dispone de varias perspectivas que adaptan el workbench (la pantalla donde trabajamos) al tipo de proyecto que estamos realizando. Selecciona Yes. Ahora puedes ver a la izquierda del workbench la pestaa Project Explorer, y dentro de ella nuestro recien creado proyecto. Pinchamos con el boton derecho encima de el y seleccionamos: Apache Derby > Add Apache Derby nature Esto aadira a nuestro proyecto todos los jars que necesita para conectar con Derby. Ahora volvemos a pinchar con el boton derecho en el proyecto y seleccionamos: Apache Derby > Start Derby Network Server Nos aparecera un cuadro indicandonos que Derby va a ser iniciado en el puerto 1527. Pulsamos OK y ya tenemos la base de datos levantada y funcionando. Podemos probarlo desde la consola de comandos de Derby, llamada ij. Volvemos a pinchar con el boton derecho en el proyecto y seleccionamos: Apache Derby > ij (Interactive SQL)

Abajo a la derecha del workbench vemos que la pantalla Console nos muestra el prompt de ij. Si hacemos doble click sobre la pestaa de console, o cualquier pestaa, esta se agrandara hasta ocupar el tamao del workbech. Si volvemos a hacer doble click, volvera a su tamao original devolviendo al workbench su aspecto anterior: Escribimos el comando 'help' en la ventana Console para comprobar que Derby funciona: ij> help; Veremos que aparece una menu de ayuda de Derby. Es importante terminar los comandos de ij con un punto y coma para que los comandos se ejecuten. Recuerda que debes haber iniciado Derby (start Derby Network Server) antes de introducir un comando en ij o de realizar cualquier tarea de persistencia con JPA o JDBC. Por ultimo vamos a crear una base de datos y una tabla que vamos a problar con algunos datos para verificar que todo esta funcionando correctamente. ij> connect 'jdbc:derby://localhost:1527/IntroduccionJPA;create=true'; Este comando conectara con derby y creara la base de datos IntroduccionJPA si no existe, y volvera a mostrar el prompt de ij indicando que esta listo para recibir mas comandos. A continuacion, vamos a crear una tabla y a introducir algunos datos: ij> create table prueba ( id integer, contenido varchar(20) ); ij> insert into prueba (id, contenido) values (1, 'Entrada de prueba'); Despues de ejecutar el primer bloque, veremos que ij nos informa de 0 rows inserted/updated/deleted (o lineas insertadas/actualizadas/borradas) puesto que solo hemos creado la tabla. Despues de ejecutar el segundo bloque, ij nos informa de 1 row inserted/updated/deleted, puesto que hemos insertado una nueva linea en la base de datos. Podemos comprobarlo con el siguiente comando: ij> select * from prueba; Veremos el registro que hemos insertado en la linea anterior. Todo funciona. Por ultimo, vamos a eliminar la tabla que hemos creado: ij> drop table prueba;

4. RESUMEN En este documento hemos visto como montar de forma sencilla un entorno de desarollo con Eclipse IDE y Derby Plug-in para seguir los capitulos de introduccion a JPA del blog. Aunque los articulos estan basados en JPA 2.0, el 99% del codigo que aparece en ellos es compatible con el API de JPA 1.1, y por tanto con nuestro entorno de desarrollo.