1. CURSO JAVA DEVELOPER - INSTRUCTOR : LEONARDO TORRES ALTEZ
Java Persistence Api
Que es ORM ?
JDBC con DAO . Con LINKS DE INTERES :
JPA
muchas alternativas ejecutándose en una
incompatibles , el gru- aplicación Java SE. ♦ EJB3 : http://www.jcp.org/
po Java EE experto en/jsr/detail?id=220
toma inspiración de
estos frameworks po- En este articulo , mo-
♦ JPA API : http://
pulares y creo el api de delare un simple libro
persistencia de Java de direcciones java.sun.com/javaee/5/
Mapeo Objeto Relacio- (JPA) , el cual se puede (Address book ) para docs/api/javax/
nal (ORM) ,en otras usar desde aplicaciones una compañía de músi- persistence/package-
palabras persistir los Java EE o SE. ca ficticia llamada Wa- summary.html
objetos Java en una termelon , para guardar
base de datos relacio- En pocas palabras las direcciones de los ♦ DAO: http://java.sun.com/
nal - ha tenido su ma- JPA , usa el modelo de clientes en la base de blueprints/
yor cambio reciente- programación POJO datos . Watermelon corej2eepatterns/Patterns/
mente, gracias, en par- para la persistencia. A vende y distribuye artí-
DataAccessObject.html
te a la proliferación de pesar de que este mo- culos de música así
métodos avanzados delo esta incluido en la como instrumentos ,
que intentan hacer esta especificación EJB 3 , amplificadores y li-
tarea mas fácil . JPA puede ser usado bros . Voy a usar una
fuera de un contenedor incremental e iterativa
Entre estas tecnologías EJB , y esta es la forma aproximación para des-
están los Entity Beans que será usada en este arrollar y persistir el
2.x , TopLink , Hiber- articulo . “Plain Old modelo del negocio.
nate , JDO , y también Java Objects” ( POJO )
Como trabaja JPA ?
clase y no implemen-
tan ninguna interface.
Usted no necesita ar- @Entity
chivos descriptores public class Customer {
XML para hacer los
mapeos . Si uno se fija @Id
Inspirado en los frame- en el API ( java doc ) private Long id;
works como Hiberna- uno observara que esta private String firstname;
te , JPA usa anotacio- compuesto de pocas private String lastname;
nes para mapear obje- clases e interfaces. private String telephone;
tos a la base de datos private String email;
relacional. Estos obje- La mayoría del conteni- private Integer age;
tos , usualmente llama- do de el paquete ja- // constuctors, getters,
vax.persitence son anota-
dos entidades, no tie- setters
ciones. Con esto expli-
nen nada en común }
con los Entity Beans cado , veremos el si-
2.x . Las entidades JPA guiente ejemplo de
son clases POJOs, no código :
extienden de ninguna
2.
3. JAVA PERSISTENCE API Page 2
Lo mínimo necesario ...
una clase “Customer” sim-
ple . Esta tiene un identifi-
cador (id) , un nombre El código que sigue mues-
( firstname) , apellido ( last- tra lo mínimo requerido
name) un numero de telé- para definir un quot;persistence
fono ( telephone ) , mail objectquot; . Ahora vamos a
( email ) y edad del cliente manipular este objeto,
( age ) . Para ser persistente deseo persistir mi objeto
esta clase tiene que seguir “customer” , actualizar algu-
algunos reglas JPA simples : na de sus propiedades y
borrarlo de la base de da-
• La clase tiene que ser identifi- tos. Estas operaciones serán
cada como una entidad usan- echas a través de la interfa-
@Entity do la anotación
ce
public class Customer { Esta parte de códi- @javax.persistence.Entity
go que vimos javax.persistence.EntityManager de
( constructores , • Una propiedad de la clase JPA.
@Id tiene que tener un identifica-
getter y setter no
private Long id; son mostrados para
dor anotado con
@javax.persistence.Id Para esto, quienes estén
private String firstname; hacer esto mas fácil familiarizados con el patrón
private String lastname; de leer ) muestra • Tiene que haber un construc- DAO , el EntityManager
private String telephone; tor sin argumentos
private String email;
private Integer age;
// constuctors, EntityManager ...
//getters, setters
} puede ser visto co- “Customer” ( usando el mail usando los métodos
mo una clase DAO operador new como cual- quot;setquot; .
que nos provee un quier otro objeto JAVA ) y
set de métodos clá- le pasare algo de data co- La interface EntityManger
// métodos CRUD sicos ( persist , re- mo el quot;idquot; , quot;last namequot; , no tiene un método upda-
move ) y buscado- quot;emailquot; , etc. Usare el méto- te . Los updates se hacen a
public void createCustomer() {
// Gets an entity manager
res ( find ). do EntityManager.persist() través de las propiedades
EntityManagerFactory emf = Persisten- Después de crear el para insertar este objeto en quot;settersquot; . Luego borrare el
ce.createEntityManagerFactory(quot;watermelonPUquot;);
EntityManager em = emf.createEntityManager(); EntityManager la base de datos. Yo puedo objeto usando EntityMana-
EntityTransaction trans = em.getTransaction usando un factory luego buscar este objeto ger.remove() , notar que
();
( EntityManagerFac- por su identificador usando este código usa transaccio-
// Instantiates a customer object
Customer customer = new Customer(1L, quot;Johnquot;,
tory ) , instanciare el método EntityMan- nes explicitas . Es por eso
quot;Lennonquot;, quot;441909quot;, quot;john@lenon.comquot;, 66); mi objeto ger.find() y actualizar el que los métodos persist ,
// Persists the customer
trans.begin();
em.persist(customer);
trans.commit();
// Finds the customer by primary key
Persistence.xml
customer = em.find(Customer.class, 1L);
System.out.println(customer.getEmail()); update , y remove especifica unidad de persistencia ( watermelonPU en este
// Updates the customer email address son llamados entre caso ) . Una unidad de persistencia es declarada en el ar-
trans.begin(); transacion.begin() y chivo “persistence.xml” y contiene información como la
customer.setEmail(quot;john@beatles.co.ukquot;);
trans.commit(); transaction.commit base de datos a usar y el driver JDBC .
// Deletes the customer (). Vea los métodos
trans.begin(); CRUD
em.remove(customer); <persistence version=quot;1.0quot; xmlns=quot;http://java.sun.com/xml/ns/persistencequot;>
trans.commit();
Que base de datos <persistence-unit name=quot;watermelonPUquot; transaction-type=quot;RESOURCE_LOCALquot;>
// Closes the entity manager and the factory <provider>
em.close(); usamos ? , La res- oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider
emf.close();
} puesta esta en Enti- </provider>
<class>entity.Customer</class>
tyManagerFactory, <properties>
Esta toma un pará- <property name=quot;toplink.jdbc.urlquot;
metro que se refiere a una value=quot;jdbc:mysql://localhost:3306/watermelonDBquot;/>
<property name=quot;toplink.jdbc.userquot; value=quot;rootquot;/>
4.
5. Page 3 JAVA PERSISTENCE API
Unidad de Persistencia
<property name=quot;toplink.jdbc.driverquot; piedades comunes . En este de la tabla “customer”.
value=quot;com.mysql.jdbc.Driverquot;/>
<property na- caso , estas propiedades
me=quot;toplink.jdbc.passwordquot; value=quot;quot;/> son : url de la base de da- Este es el DDL que JPA
<property name=quot;toplink.target-
databasequot; value=quot;MySQL4quot;/> tos , driver JDBC , creden- gene-
<property name=quot;toplink.ddl- ciales. Bajo el elemento ra
generationquot; value=quot;create-tablesquot;/>
</properties>
quot;propertiesquot; usted encontra- auto-
</persistence-unit> ra propiedades especificas
</persistence>
para Top-link por ejemplo
toplink.ddl-generation . Esta
En el código arriba , hay propiedad es usada por
solo una unidad de persis- TopLink para generar las
tencia , llamada waterme- tablas automáticamente si
lonPU ( el archivo persis- estas no existen aun . Esto
tence.xml puede contener significa que una vez ejecu-
muchas unidades de persis- tado el código TopLink a
tencia ) . Usted puede pen- creado una tabla para guar- máticamente de la clase
sar en una unidad de per- dar la información de anotada “Customer” . Gra-
sistencia como un conjunto “customers”. la tabla 1 cias a la “codificación por
de entidades ( el elemento muestra la información DDL defecto” que guía JPA ( y
class ) que comparten pro- ( data definition languaje ) en general Java EE 5 ) No
Customizando
Usted solo necesita añadir lumna tiene String es mapeado a var-
código customizado cuando el mismo char(255) ).
el código por defecto es nombre de
inadecuado . En mi caso, las propie-
porque yo nunca especifico dades . Los
el nombre de la tabla o tipos de
columnas en la clase datos son
“Customer” JPA asume que también
el nombre de la tablas es mapeados
igual al nombre de la clase por defecto
y que el nombre de la co- ( ejemplo
Añadiendo funcionalidades y customizando el mapeo
En este punto yo quisiera primary Key en cuatro posi- quot;identity generatorquot; ,
ej :una columna definida
mejorar algunas cosas. Pri- bles modos: como auto_increment en
mero que todo, yo no quie- MySQL.
ro setear un identificador • AUTO (default) permite al
(primary key)del objeto proveedor de persistencia
pero en lugar de esto quie- (TopLink en mi caso) Ahora quiero mejorar mi
ro que JPA lo incremente decidir cual de las tres mapeo . Primero cambiar el
posibilidades usar. nombre de la tabla a
automáticamente. Gracias a
• SEQUENCE usar un SQL quot;t_customerquot; en lugar de
las anotaciones , esto es sequence para obtener el
fácil de hacer. próximo primary key
quot;customerquot;. Luego hare el
nombre ( first name ) y el
• TABLE requiere una tabla
apellido ( last name ) obli-
Solo necesito anotar mi con dos columnas : el
atributo identificador nombre de la secuencia y gatorios. El máximo largo
@javax.persitence.GeneratedValue
su valor ( esta es la estra- de un numero de teléfono
tegia por defecto de To- (telephone) tiene que ser
. pLink )
Esta anotación genera un 15 caracteres y la columna
• IDENTITY usa un
6.
7. JAVA PERSISTENCE API Page 4
email tiene que ser renombrada a “e_mail” con un quot;underscorequot; .
Todos estos pequeños cambios pueden ser hechos con las anotaciones. Para cambiar el
Una aplicación
puede ser
notificada antes
nombre ( name ) de la tabla anote la clase con @javax.persistence.Table . La anotación
o después de @javax.persistence.Column es usada para definir las columnas y tienen una serie de
ocurridos estos
eventos JPA
Anotaciones Callback
usando
quot;callback Ahora con el mapeo entre la una entidad es cargada , notatios” para mis necesida-
clase Customer y la tabla persistida , actualizada o des ? , Primero me encarga-
t_customer quedo mejor removida . Una aplicación re del formato del numero
annotationsquot;
gracias a que las anotacio- puede ser notificada antes o de teléfono . Quiero verifi-
nes @Column y @Table tie- después de ocurridos estos car que el primer carácter
del teléfono es quot;+quot; , Yo pue-
nen muchos atributos. eventos usando anotacio- do hacer esto antes que la
nes . JPA tiene un conjunto entidad sea persistida o ac-
Hay otras dos cosas que
de quot;callback annotatiosquot; que tualizada . solo tengo que
quisiera hacer . Primero ase-
pueden ser usadas en méto-
gurarme
que todo
teléfono es
ingresado
usando
códigos
internacio-
nales . co-
menzando
con el sím-
bolo '+' .
dos y permiten al desarrolla- crear un método ( validateP-
Segundo , calcular la edad honeNumber en mi ejem-
dor añadir cualquier regla
del “customer” a partir de plo , pero el nombre es irre-
de negocios que desee . JPA
la fecha de nacimiento . levante ) con algo de lógica
llamara al método anotado
Tengo muchas alternativas de negocios y con las anota-
antes o después de estos
de como hacer estas tareas , ciones @PrePersist y
eventos . La tabla 4 muestra @PreUpdate, JPA hace el
pero voy a usar quot;callback
las quot;callback anotationsquot; resto.
annotationsquot;
Durante su ciclo de vida , Como uso las “callback an- El código de ejemplo en la
8.
9. siguiente pagina.
@PrePersist
@PreUpdate
Page 5
private void validatePhoneNumber() {
if (telephone.charAt(0) != '+')
Para la edad del cliente , throw new IllegalArgumentException
hare algo similar, calculare la (quot;Invalid phone numberquot;);
edad del cliente antes que la }
fecha de nacimiento sea in- }
sertada ( @PostPersist ) o
actualizada ( @PostUpdate ) ,
y claro cada vez que el clien- @PostLoad
te es cargado de la base de @PostPersist
datos ( @PostLoad ). @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;
}
Anotaciones Callback .. añadir anotaciones
Para hacer que esto funcio- TAMP ) . Entonces estoy en tabla no tendrá una colum-
ne . necesito añadir un nue- la capacidad de calcular la na edad mas ) ver tabla 5.
vo atributo a la clase Custo- edad del cliente pero yo
mer : “date of birth” . Para necesito persistir esa infor-
notificar a JPA que mapee mación ? , NO , conociendo
este atributo a una fecha , que el valor cambia todos
uso la anotación @Temporal los años . Para hacer que
con el atributo TemporalTy- esta propiedad (age) edad
pe.DATE ( las opciones son no sea persistente , usare la
DATE , TIME , TIMES- anotación @Transient ( La
10.
11. JAVA PERSISTENCE API Page 6
One to One Relationship
Ahora que tengo mapeada Un Customer tiene una y por su cumpleaños . Lo
mi clase Customer y tengo solo una representare como una cla-
anotaciones callback para “address“ (dirección) enton- se separada, clase Address
validar y calcular data , ne- ces Watermelon pueden con un id , una calle
cesito añadir una dirección. enviar al cliente un regalo (street) , una ciudad (city) ,
un
có-
al persistir o
remover
“customer”
One to One Relationship public void createCustomerWithAddress() {
también se hace // Instantiates a Customer and an Address objecy
Customer customer = new Customer(quot;Johnquot;, quot;Lennonquot;,
Como tu pueden ver en la quot;customer'squot; uso el código
quot;+441909quot;, quot;john@lenon.comquot;, dateOfBirth);
con su “address” tabla 6 , la clase Address que sigue : homeAddress = new Address(quot;Abbey Roadquot;,
Address
quot;Londonquot;, quot;SW14quot;, quot;UKquot;);
usa la anotación @Entity customer.setHomeAddress(homeAddress);
para notificar a JPA que es
una clase persistente y // Persists the customer with its address
@Column para customizar trans.begin();
el mapeo. Creando la rela- em.persist(homeAddress);
ción entre Customer y Ad- em.persist(customer);
trans.commit();
dress es simple . Yo simple-
mente añado una propie- // Deletes the customer and the address
dad Address en la clase trans.begin();
Customer . Para persistir la em.remove(customer);
dirección de los em.remove(homeAddress);
trans.commit();
}
One to One Relationship
el customer también remue- por defecto hará las asocia-
Como este código muestra ,
vo el address. ciones opcionales y basa-
usted tiene primero que
das en mis requerimientos,
instanciar un objeto Custo- Pero persistiendo y remo-
es decir al persistir o remo-
mer y un Adress , Para lin- viendo ambos objetos pare-
ver customer también se
kear los dos ,uso un méto- ce que se hiciera mas traba-
hace con su address.
do setter ( set HomeAd- jo que el que necesito. Se-
dress ) y luego persistido ria mejor si yo pudiera per-
Quiero que un Customer
cada objeto , cada uno en sistir o remover justo el
tenga exactamente un Ad-
la misma transacción. Por- objeto raíz ( el Customer ) y
dress , significa que un va-
que no tiene sentido tener permitir las dependencias
lor null no es permitido .
un adress en el sistema que que se persistan o remue-
Puedo lograr todo eso
no este linkeado a un cus- van automáticamente ? ,
usando la anotación
tomer , cuando yo remuevo TopLink y la codificación
12.
13. Page 7 JAVA PERSISTENCE API
One to One Relationship
loading LAZY) , o constraint de integri-
@OneToOne es usada para
quot;eagerlyquot; (EAGER) porque dad ,para la relación entre
anotar una relación . Este
quiero cargar la dirección tablas t_customer y t_adress
tiene muchas propiedades
de la casa quot;homeAddressquot; ( ver tabla 7 )
incluyendo un cascade
tan pronto como el objeto
usado para quot;cascadingquot; de
Customer es cargado.
cualquier tipo de acción. En
este ejemplo , quiero La anotación @JoinColumn
quot;cascadequot; la acción de per- tiene los mismos atributos
sistir y remover. De esta como @Column , excepto
forma , cuando hago remo- que es usado para asociar
ve o persist de un objeto atributos , En este ejemplo ,
customer este automática- Yo renombro la llave forá-
mente lleva acabo esta ac- nea en un address_fk y no
ción para el quot;addressquot; . El permito ningún valor null
atributo fetch dice a JPA ( nullable=false)
que política usar cuando
cargamos una relación.
Esta puede ser una asocia- JPA entonces creara los El atributo
ción de carga tardía ( lazy siguientes DDLs con una
fetch dice a JPA
@Entity
que política usar
@Table(name = quot;t_customerquot;)
public class Customer { cuando
@Id cargamos una
@GeneratedValue
private Long id;
(...) relación
@Transient
private Integer age;
@OneToOne(fetch = FetchType.EAGER, cascade =
{CascadeType.PERSIST, CascadeType.REMOVE})
@JoinColumn(name = quot;address_fkquot;, nullable = false)
private Address homeAddress;
// constuctors, getters, setters
}
14.
15. JAVA PERSISTENCE API Page 8
Querying Objects
mite hacer querys comple- de operadores para filtrar la
Hasta ahora yo estoy usan- jos sobre objetos ( asocia- data ( IN , NOT IN , EXIST , LIKE , IS
do JPA para mapear mis ciones , herencia , clases NULL , IS NOT NULL ) o para con-
objetos a una base de datos abstractas ) trolar las colecciones ( IS
relacional y usar el entity EMPTY , IS NOT EMPTY , MEMBER
manager para hacer algunas Los querys usan las pala- OF ) , También hay funcio-
operaciones CRUD (Create, bras SELECT , FROM y nes para manejar Strings
( LOWER , UPPER , TRIM , CONCAT ,
read, update and delete ) , WHERE , mas un conjunto LENGTH , SUBS-
Pero JPA también TRING ) , nú-
permite que hagas // Finds the customers who are called John
meros ( ABS ,
querys sobre los SQRT , MOD ) , o
objetos. Esto usa Query query = em.createQuery(quot;SELECT c
colecciones
quot;Java Persistence FROM Customer c WHERE ( COUNT , MIN ,
Query Langua- c.firstname='John'quot;); MAX , SUM ). Co-
gequot; ( JPQL) , el List<Customer> customers = que- mo SQL , tu
cual es similar a ry.getResultList(); también pue-
SQL y es también des ordenar
Usted tiene que independiente de // Same query but using a parameter los resultados
la base de datos , //Query query = em.createQuery(quot;SELECT c FROM ( ORDER BY) o
hacer querys no Este es un lenguaje //Customer c WHERE c.firstname=:paramquot;); agruparlos
//query.setParameter(quot;:paramquot;, quot;Johnquot;); (GROUP BY)
rico que nos per-
sobre una tabla,
mas bien sobre
Querying Objects, sobre objetos ...
un objeto. Para hacer querys sobre los dos formas . De cualquiera
objetos se necesita Entity- de las dos formas el string
Manager para crear un ob- quot;Johnquot; es buscado : puede
jeto Query . Luego tengo el ser parte del query JPQL o
resultado del query llaman- puede ser pasado como
do el getResultList o gen- parámetro , en este ultimo
SingleResult cuando hay caso necesito usar el méto-
solo un objeto retornado . do setParameter.
En el ejemplo que sigue ,
quiero buscar todos los
“Customers” quienes tienen
el primer nombre quot;Johnquot; ,
Yo puedo hacer esto de
JPQL Queries, ejemplos ...
objeto “customer” y el to address. SELECT c FROM
Como usted puede ver en Customer c WHERE
este query JPQL usa la no- “c.firstname” es la primera
c.homeAddress.country='US';
tación objeto . Usted tiene propiedad del objeto
que hacer query no sobre “customer” . Si ustedes Acá hay un conjunto de
una tabla, mas bien sobre quieren buscar todos los querys que nosotros pode-
un objeto. El carácter “customers” que viven en mos hacer con JPQL . Ver
“c” ( el nombre es irrele- U.S. , ustedes pueden usar tabla 8
vante ) es el alias de un esta notación , para obtener
al atributo country del obje-