TopLink Jpa Parte 1 - Leonardo Torres Altez

16
ejecutándose en una aplicación Java SE. En este articulo , mo- delare un simple libro de direcciones (Address book ) para una compañía de músi- ca ficticia llamada Wa- termelon , para guardar las direcciones de los clientes en la base de datos . Watermelon vende y distribuye artí- culos de música así como instrumentos , amplificadores y li- bros . Voy a usar una incremental e iterativa aproximación para des- arrollar y persistir el modelo del negocio. Mapeo Objeto Relacio- nal (ORM) ,en otras palabras persistir los objetos Java en una base de datos relacio- nal - ha tenido su ma- yor cambio reciente- mente, gracias, en par- te a la proliferación de métodos avanzados que intentan hacer esta tarea mas fácil . Entre estas tecnologías están los Entity Beans 2.x , TopLink , Hiber- nate , JDO , y también JDBC con DAO . Con muchas alternativas incompatibles , el gru- po Java EE experto toma inspiración de estos frameworks po- pulares y creo el api de persistencia de Java (JPA) , el cual se puede usar desde aplicaciones Java EE o SE. En pocas palabras JPA , usa el modelo de programación POJO para la persistencia. A pesar de que este mo- delo esta incluido en la especificación EJB 3 , JPA puede ser usado fuera de un contenedor EJB , y esta es la forma que será usada en este articulo . “Plain Old Java Objects” ( POJO ) Que es ORM ? CURSO JAVA DEVELOPER - INSTRUCTOR : LEONARDO TORRES ALTEZ Java Persistence Api LINKS DE INTERES : EJB3 : http://www.jcp.org/ en/jsr/detail?id=220 JPA API : http:// java.sun.com/javaee/5/ docs/api/javax/ persistence/package- summary.html DAO: http://java.sun.com/ blueprints/ corej2eepatterns/Patterns/ DataAccessObject.html Como trabaja JPA ? Inspirado en los frame- works como Hiberna- te , JPA usa anotacio- nes para mapear obje- tos a la base de datos relacional. Estos obje- tos , usualmente llama- dos entidades, no tie- nen nada en común con los Entity Beans 2.x . Las entidades JPA son clases POJOs, no extienden de ninguna clase y no implemen- tan ninguna interface. Usted no necesita ar- chivos descriptores XML para hacer los mapeos . Si uno se fija en el API ( java doc ) uno observara que esta compuesto de pocas clases e interfaces. La mayoría del conteni- do de el paquete ja- vax.persitence son anota- ciones. Con esto expli- cado , veremos el si- guiente ejemplo de código : @Entity public class Customer { @Id private Long id; private String firstname; private String lastname; private String telephone; private String email; private Integer age; // constuctors, getters, setters } JPA JPA

description

Tutorial de TopLink Jpa Parte 1

Transcript of TopLink Jpa Parte 1 - Leonardo Torres Altez

Page 1: TopLink Jpa Parte 1 - Leonardo Torres Altez

ejecutándose en una aplicación Java SE. En este articulo , mo-delare un simple libro de direcciones (Address book ) para una compañía de músi-ca ficticia llamada Wa-termelon , para guardar las direcciones de los clientes en la base de datos . Watermelon vende y distribuye artí-culos de música así como instrumentos , amplificadores y li-bros . Voy a usar una incremental e iterativa aproximación para des-arrollar y persistir el modelo del negocio.

Mapeo Objeto Relacio-nal (ORM) ,en otras palabras persistir los objetos Java en una base de datos relacio-nal - ha tenido su ma-yor cambio reciente-mente, gracias, en par-te a la proliferación de métodos avanzados que intentan hacer esta tarea mas fácil . Entre estas tecnologías están los Entity Beans 2.x , TopLink , Hiber-nate , JDO , y también

JDBC con DAO . Con muchas alternativas incompatibles , el gru-po Java EE experto toma inspiración de estos frameworks po-pulares y creo el api de persistencia de Java (JPA) , el cual se puede usar desde aplicaciones Java EE o SE. En pocas palabras JPA , usa el modelo de programación POJO para la persistencia. A pesar de que este mo-delo esta incluido en la especificación EJB 3 , JPA puede ser usado fuera de un contenedor EJB , y esta es la forma que será usada en este articulo . “Plain Old Java Objects” ( POJO )

Que es ORM ?

CURSO JAVA DEVELOPER - INSTRUCTOR : LEONARDO TORRES ALTEZ

Java Persistence Api

LINKS DE INTERES :

♦ EJB3 : http://www.jcp.org/en/jsr/detail?id=220

♦ JPA API : http://java.sun.com/javaee/5/

docs/api/javax/

persistence/package-

summary.html

♦ DAO: http://java.sun.com/blueprints/

corej2eepatterns/Patterns/

DataAccessObject.html

Como trabaja JPA ? Inspirado en los frame-works como Hiberna-te , JPA usa anotacio-nes para mapear obje-tos a la base de datos relacional. Estos obje-tos , usualmente llama-dos entidades, no tie-nen nada en común con los Entity Beans 2.x . Las entidades JPA son clases POJOs, no extienden de ninguna

clase y no implemen-tan ninguna interface. Usted no necesita ar-chivos descriptores XML para hacer los mapeos . Si uno se fija en el API ( java doc ) uno observara que esta compuesto de pocas clases e interfaces. La mayoría del conteni-do de el paquete ja-vax.persitence son anota-ciones. Con esto expli-cado , veremos el si-guiente ejemplo de código :

@Entity public class Customer { @Id private Long id; private String firstname; private String lastname; private String telephone; private String email; private Integer age; // constuctors, getters, setters }

JPAJPA

Page 2: TopLink Jpa Parte 1 - Leonardo Torres Altez
Page 3: TopLink Jpa Parte 1 - Leonardo Torres Altez

especifica unidad de persistencia ( watermelonPU en este caso ) . Una unidad de persistencia es declarada en el ar-chivo “persistence.xml” y contiene información como la base de datos a usar y el driver JDBC . <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"> <persistence-unit name="watermelonPU" transaction-type="RESOURCE_LOCAL"> <provider> oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider </provider> <class>entity.Customer</class> <properties> <property name="toplink.jdbc.url" value="jdbc:mysql://localhost:3306/watermelonDB"/> <property name="toplink.jdbc.user" value="root"/>

update , y remove son llamados entre transacion.begin() y transaction.commit(). Vea los métodos CRUD Que base de datos usamos ? , La res-puesta esta en Enti-tyManagerFactory, Esta toma un pará-

metro que se refiere a una

Lo mínimo necesario ...

Persistence.xml

“Customer” ( usando el operador new como cual-quier otro objeto JAVA ) y le pasare algo de data co-mo el "id" , "last name" , "email" , etc. Usare el méto-do EntityManager.persist() para insertar este objeto en la base de datos. Yo puedo luego buscar este objeto por su identificador usando el método EntityMan-ger.find() y actualizar el

mail usando los métodos "set" . La interface EntityManger no tiene un método upda-te . Los updates se hacen a través de las propiedades "setters" . Luego borrare el objeto usando EntityMana-ger.remove() , notar que este código usa transaccio-nes explicitas . Es por eso que los métodos persist ,

EntityManager ...

puede ser visto co-mo una clase DAO que nos provee un set de métodos clá-sicos ( persist , re-move ) y buscado-res ( find ). Después de crear el EntityManager usando un factory ( EntityManagerFac-tory ) , instanciare mi objeto

Esta parte de códi-go que vimos ( constructores , getter y setter no son mostrados para hacer esto mas fácil de leer ) muestra

una clase “Customer” sim-ple . Esta tiene un identifi-cador (id) , un nombre ( firstname) , apellido ( last-name) un numero de telé-fono ( telephone ) , mail ( email ) y edad del cliente ( age ) . Para ser persistente esta clase tiene que seguir algunos reglas JPA simples : • La clase tiene que ser identifi-

cada como una entidad usan-do la anotación @javax.persistence.Entity

• Una propiedad de la clase tiene que tener un identifica-dor anotado con @javax.persistence.Id

• Tiene que haber un construc-tor sin argumentos

El código que sigue mues-tra lo mínimo requerido para definir un "persistence object" . Ahora vamos a manipular este objeto, deseo persistir mi objeto “customer” , actualizar algu-na de sus propiedades y borrarlo de la base de da-tos. Estas operaciones serán echas a través de la interfa-ce javax.persistence.EntityManager de JPA. Para esto, quienes estén familiarizados con el patrón DAO , el EntityManager

Page 2 JAVA PERSISTENCE API

@Entity public class Customer { @Id private Long id; private String firstname; private String lastname; private String telephone; private String email; private Integer age; // constuctors, //getters, setters }

// métodos CRUD public void createCustomer() { // Gets an entity manager EntityManagerFactory emf = Persisten-ce.createEntityManagerFactory("watermelonPU"); EntityManager em = emf.createEntityManager(); EntityTransaction trans = em.getTransaction(); // Instantiates a customer object Customer customer = new Customer(1L, "John", "Lennon", "441909", "[email protected]", 66); // Persists the customer trans.begin(); em.persist(customer); trans.commit(); // Finds the customer by primary key customer = em.find(Customer.class, 1L); System.out.println(customer.getEmail()); // Updates the customer email address trans.begin(); customer.setEmail("[email protected]"); trans.commit(); // Deletes the customer trans.begin(); em.remove(customer); trans.commit(); // Closes the entity manager and the factory em.close(); emf.close(); }

Page 4: TopLink Jpa Parte 1 - Leonardo Torres Altez
Page 5: TopLink Jpa Parte 1 - Leonardo Torres Altez

<property name="toplink.jdbc.driver" value="com.mysql.jdbc.Driver"/> <property na-me="toplink.jdbc.password" value=""/> <property name="toplink.target-database" value="MySQL4"/> <property name="toplink.ddl-generation" value="create-tables"/> </properties> </persistence-unit> </persistence> En el código arriba , hay solo una unidad de persis-tencia , llamada waterme-lonPU ( el archivo persis-tence.xml puede contener muchas unidades de persis-tencia ) . Usted puede pen-sar en una unidad de per-sistencia como un conjunto de entidades ( el elemento class ) que comparten pro-

piedades comunes . En este caso , estas propiedades son : url de la base de da-tos , driver JDBC , creden-ciales. Bajo el elemento "properties" usted encontra-ra propiedades especificas para Top-link por ejemplo toplink.ddl-generation . Esta propiedad es usada por TopLink para generar las tablas automáticamente si estas no existen aun . Esto significa que una vez ejecu-tado el código TopLink a creado una tabla para guar-dar la información de “customers”. la tabla 1 muestra la información DDL ( data definition languaje )

de la tabla “customer”. Este es el DDL que JPA gene-ra auto-

máticamente de la clase anotada “Customer” . Gra-cias a la “codificación por defecto” que guía JPA ( y en general Java EE 5 ) No

primary Key en cuatro posi-bles modos: • AUTO (default) permite al

proveedor de persistencia (TopLink en mi caso) decidir cual de las tres posibilidades usar.

• SEQUENCE usar un SQL sequence para obtener el próximo primary key

• TABLE requiere una tabla con dos columnas : el nombre de la secuencia y su valor ( esta es la estra-tegia por defecto de To-pLink )

• IDENTITY usa un

En este punto yo quisiera mejorar algunas cosas. Pri-mero que todo, yo no quie-ro setear un identificador (primary key)del objeto pero en lugar de esto quie-ro que JPA lo incremente automáticamente. Gracias a las anotaciones , esto es fácil de hacer. Solo necesito anotar mi atributo identificador @javax.persitence.GeneratedValue . Esta anotación genera un

"identity generator" , ej :una columna definida como auto_increment en MySQL.

Ahora quiero mejorar mi mapeo . Primero cambiar el nombre de la tabla a "t_customer" en lugar de "customer". Luego hare el nombre ( first name ) y el apellido ( last name ) obli-gatorios. El máximo largo de un numero de teléfono (telephone) tiene que ser 15 caracteres y la columna

Unidad de Persistencia

Añadiendo funcionalidades y customizando el mapeo

lumna tiene el mismo nombre de las propie-dades . Los tipos de datos son también mapeados por defecto ( ejemplo

String es mapeado a var-char(255) ).

Customizando

Usted solo necesita añadir código customizado cuando el código por defecto es inadecuado . En mi caso, porque yo nunca especifico el nombre de la tabla o columnas en la clase “Customer” JPA asume que el nombre de la tablas es igual al nombre de la clase y que el nombre de la co-

Page 3 JAVA PERSISTENCE API

Page 6: TopLink Jpa Parte 1 - Leonardo Torres Altez
Page 7: TopLink Jpa Parte 1 - Leonardo Torres Altez

una entidad es cargada , persistida , actualizada o removida . Una aplicación puede ser notificada antes o después de ocurridos estos eventos usando anotacio-nes . JPA tiene un conjunto de "callback annotatios" que pueden ser usadas en méto-

dos y permiten al desarrolla-dor añadir cualquier regla de negocios que desee . JPA llamara al método anotado antes o después de estos eventos . La tabla 4 muestra las "callback anotations"

Como uso las “callback an-

Ahora con el mapeo entre la clase Customer y la tabla t_customer quedo mejor gracias a que las anotacio-nes @Column y @Table tie-nen muchos atributos.

Hay otras dos cosas que quisiera hacer . Primero ase-gurarme que todo teléfono es ingresado usando códigos internacio-nales . co-menzando con el sím-bolo '+' . Segundo , calcular la edad del “customer” a partir de la fecha de nacimiento . Tengo muchas alternativas de como hacer estas tareas , pero voy a usar "callback annotations"

Durante su ciclo de vida ,

notatios” para mis necesida-des ? , Primero me encarga-re del formato del numero de teléfono . Quiero verifi-car que el primer carácter del teléfono es "+" , Yo pue-do hacer esto antes que la entidad sea persistida o ac-tualizada . solo tengo que

crear un método ( validateP-honeNumber en mi ejem-plo , pero el nombre es irre-levante ) con algo de lógica de negocios y con las anota-ciones @PrePersist y @PreUpdate, JPA hace el resto.

El código de ejemplo en la

Anotaciones Callback

email tiene que ser renombrada a “e_mail” con un "underscore" .

Todos estos pequeños cambios pueden ser hechos con las anotaciones. Para cambiar el

nombre ( name ) de la tabla anote la clase con @javax.persistence.Table . La anotación @javax.persistence.Column es usada para definir las columnas y tienen una serie de

Una aplicación

puede ser

notificada antes

o después de

ocurridos estos

eventos JPA

usando

"callback

annotations"

Page 4 JAVA PERSISTENCE API

Page 8: TopLink Jpa Parte 1 - Leonardo Torres Altez
Page 9: TopLink Jpa Parte 1 - Leonardo Torres Altez

siguiente pagina.

Para la edad del cliente , hare algo similar, calculare la edad del cliente antes que la fecha de nacimiento sea in-sertada ( @PostPersist ) o actualizada ( @PostUpdate ) , y claro cada vez que el clien-te es cargado de la base de datos ( @PostLoad ).

TAMP ) . Entonces estoy en la capacidad de calcular la edad del cliente pero yo necesito persistir esa infor-mación ? , NO , conociendo que el valor cambia todos los años . Para hacer que esta propiedad (age) edad no sea persistente , usare la anotación @Transient ( La

Para hacer que esto funcio-ne . necesito añadir un nue-vo atributo a la clase Custo-mer : “date of birth” . Para notificar a JPA que mapee este atributo a una fecha , uso la anotación @Temporal con el atributo TemporalTy-pe.DATE ( las opciones son DATE , TIME , TIMES-

tabla no tendrá una colum-na edad mas ) ver tabla 5.

Anotaciones Callback .. añadir anotaciones

@PostLoad @PostPersist @PostUpdate public void calculateAge() { Calendar birth = new GregorianCalendar(); birth.setTime(dateOfBirth); Calendar now = new GregorianCalendar(); now.setTime(new Date()); int adjust = 0; if (now.get(Calendar.DAY_OF_YEAR) - birth.get(Calendar.DAY_OF_YEAR) < 0) { adjust = -1; } age = now.get(Calendar.YEAR) - birth.get(Calendar.YEAR) + adjust; }

@PrePersist @PreUpdate private void validatePhoneNumber() { if (telephone.charAt(0) != '+') throw new IllegalArgumentException("Invalid phone number"); } }

Page 5

Page 10: TopLink Jpa Parte 1 - Leonardo Torres Altez
Page 11: TopLink Jpa Parte 1 - Leonardo Torres Altez

el customer también remue-vo el address.

Pero persistiendo y remo-viendo ambos objetos pare-ce que se hiciera mas traba-jo que el que necesito. Se-ria mejor si yo pudiera per-sistir o remover justo el objeto raíz ( el Customer ) y permitir las dependencias que se persistan o remue-van automáticamente ? , TopLink y la codificación

Como este código muestra , usted tiene primero que instanciar un objeto Custo-mer y un Adress , Para lin-kear los dos ,uso un méto-do setter ( set HomeAd-dress ) y luego persistido cada objeto , cada uno en la misma transacción. Por-que no tiene sentido tener un adress en el sistema que no este linkeado a un cus-tomer , cuando yo remuevo

por defecto hará las asocia-ciones opcionales y basa-das en mis requerimientos, es decir al persistir o remo-ver customer también se hace con su address. Quiero que un Customer tenga exactamente un Ad-dress , significa que un va-lor null no es permitido . Puedo lograr todo eso usando la anotación

One to One Relationship

One to One Relationship

"customer's" uso el código que sigue :

One to One Relationship

Como tu pueden ver en la tabla 6 , la clase Address usa la anotación @Entity para notificar a JPA que es una clase persistente y @Column para customizar el mapeo. Creando la rela-ción entre Customer y Ad-dress es simple . Yo simple-mente añado una propie-dad Address en la clase Customer . Para persistir la dirección de los

Ahora que tengo mapeada mi clase Customer y tengo anotaciones callback para validar y calcular data , ne-cesito añadir una dirección.

Un Customer tiene una y solo una “address“ (dirección) enton-ces Watermelon pueden enviar al cliente un regalo

por su cumpleaños . Lo representare como una cla-se separada, clase Address con un id , una calle (street) , una ciudad (city) ,

un có-

Page 6 JAVA PERSISTENCE API

al persistir o

remover

�customer�

también se hace

con su �address�

public void createCustomerWithAddress() { // Instantiates a Customer and an Address objecy Customer customer = new Customer("John", "Lennon", "+441909", "[email protected]", dateOfBirth); Address homeAddress = new Address("Abbey Road", "London", "SW14", "UK"); customer.setHomeAddress(homeAddress); // Persists the customer with its address trans.begin(); em.persist(homeAddress); em.persist(customer); trans.commit(); // Deletes the customer and the address trans.begin(); em.remove(customer); em.remove(homeAddress); trans.commit(); }

Page 12: TopLink Jpa Parte 1 - Leonardo Torres Altez
Page 13: TopLink Jpa Parte 1 - Leonardo Torres Altez

@OneToOne es usada para anotar una relación . Este tiene muchas propiedades incluyendo un cascade usado para "cascading" de cualquier tipo de acción. En este ejemplo , quiero "cascade" la acción de per-sistir y remover. De esta forma , cuando hago remo-ve o persist de un objeto customer este automática-mente lleva acabo esta ac-ción para el "address" . El atributo fetch dice a JPA que política usar cuando cargamos una relación. Esta puede ser una asocia-ción de carga tardía ( lazy

loading LAZY) , o "eagerly" (EAGER) porque quiero cargar la dirección de la casa "homeAddress" tan pronto como el objeto Customer es cargado.

La anotación @JoinColumn tiene los mismos atributos como @Column , excepto que es usado para asociar atributos , En este ejemplo , Yo renombro la llave forá-nea en un address_fk y no permito ningún valor null ( nullable=false)

JPA entonces creara los siguientes DDLs con una

constraint de integri-dad ,para la relación entre tablas t_customer y t_adress ( ver tabla 7 )

One to One Relationship

Page 7 JAVA PERSISTENCE API

El atributo

fetch dice a JPA

que política usar

cuando

cargamos una

relación

@Entity @Table(name = "t_customer") public class Customer { @Id @GeneratedValue private Long id; (...) @Transient private Integer age; @OneToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.REMOVE}) @JoinColumn(name = "address_fk", nullable = false) private Address homeAddress; // constuctors, getters, setters }

Page 14: TopLink Jpa Parte 1 - Leonardo Torres Altez
Page 15: TopLink Jpa Parte 1 - Leonardo Torres Altez

objeto “customer” y el “c.firstname” es la primera propiedad del objeto “customer” . Si ustedes quieren buscar todos los “customers” que viven en U.S. , ustedes pueden usar esta notación , para obtener al atributo country del obje-

Como usted puede ver en este query JPQL usa la no-tación objeto . Usted tiene que hacer query no sobre una tabla, mas bien sobre un objeto. El carácter “c” ( el nombre es irrele-vante ) es el alias de un

to address. SELECT c FROM Customer c WHERE c.homeAddress.country='US';

Acá hay un conjunto de querys que nosotros pode-mos hacer con JPQL . Ver tabla 8

Querying Objects

JPQL Queries, ejemplos ...

dos formas . De cualquiera de las dos formas el string "John" es buscado : puede ser parte del query JPQL o puede ser pasado como parámetro , en este ultimo caso necesito usar el méto-do setParameter.

Querying Objects, sobre objetos ... Para hacer querys sobre los objetos se necesita Entity-Manager para crear un ob-jeto Query . Luego tengo el resultado del query llaman-do el getResultList o gen-SingleResult cuando hay solo un objeto retornado . En el ejemplo que sigue , quiero buscar todos los “Customers” quienes tienen el primer nombre "John" , Yo puedo hacer esto de

Hasta ahora yo estoy usan-do JPA para mapear mis objetos a una base de datos relacional y usar el entity manager para hacer algunas operaciones CRUD (Create, read, update and delete ) , Pero JPA también permite que hagas querys sobre los objetos. Esto usa "Java Persistence Query Langua-ge" ( JPQL) , el cual es similar a SQL y es también independiente de la base de datos , Este es un lenguaje rico que nos per-

mite hacer querys comple-jos sobre objetos ( asocia-ciones , herencia , clases abstractas )

Los querys usan las pala-bras SELECT , FROM y WHERE , mas un conjunto

de operadores para filtrar la data ( IN , NOT IN , EXIST , LIKE , IS

NULL , IS NOT NULL ) o para con-trolar las colecciones ( IS EMPTY , IS NOT EMPTY , MEMBER

OF ) , También hay funcio-nes para manejar Strings ( LOWER , UPPER , TRIM , CONCAT ,

LENGTH , SUBS-

TRING ) , nú-meros ( ABS ,

SQRT , MOD ) , o colecciones ( COUNT , MIN ,

MAX , SUM ). Co-mo SQL , tu también pue-des ordenar los resultados ( ORDER BY) o agruparlos (GROUP BY)

Page 8 JAVA PERSISTENCE API

// Finds the customers who are called John Query query = em.createQuery("SELECT c FROM Customer c WHERE c.firstname='John'"); List<Customer> customers = que-ry.getResultList(); // Same query but using a parameter //Query query = em.createQuery("SELECT c FROM //Customer c WHERE c.firstname=:param"); //query.setParameter(":param", "John");

Usted tiene que

hacer querys no

sobre una tabla,

mas bien sobre

un objeto.

Page 16: TopLink Jpa Parte 1 - Leonardo Torres Altez