Está en la página 1de 8

CURSO JAVA DEVELOPER

Java Persistence Api

Que es ORM ?
compatibles , el grupo LINKS DE INTERES :

JPA
Java EE experto toma En este articulo , modela-
inspiración de estos fra- re un simple libro de di- ♦ EJB3 : http://www.jcp.org/
meworks populares y creo recciones (Address book ) en/jsr/detail?id=220
el api de persistencia de para una compañía de
Java (JPA) , el cual se música ficticia llamada ♦ JPA API : http://
puede usar desde aplica- Watermelon , para guar-
ciones Java EE o SE. dar las direcciones de los java.sun.com/javaee/5/
Mapeo Objeto Relacional clientes en la base de da- docs/api/javax/persistence/
(ORM) ,en otras palabras En pocas palabras JPA , tos . Watermelon vende y package-summary.html
persistir los objetos Java usa el modelo de progra- distribuye artículos de
en una base de datos rela- mación POJO para la música así como instru- ♦ DAO: http://java.sun.com/
cional - ha tenido su ma- persistencia. A pesar de mentos , amplificadores y blueprints/
yor cambio recientemen- que este modelo esta in- libros . Voy a usar una corej2eepatterns/Patterns/
te, gracias, en parte a la cluido en la especificación incremental e iterativa
proliferación de métodos EJB 3 , JPA puede ser aproximación para des- DataAccessObject.html
avanzados que intentan usado fuera de un conte- arrollar y persistir el mo-
hacer esta tarea mas fácil . nedor EJB , y esta es la delo del negocio.
forma que será usada en
Entre estas tecnologías este articulo . “Plain Old
están los Entity Beans Java Objects” ( POJO )
2.x , TopLink , Hiberna- ejecutándose en una apli-
te , JDO , y también cación Java SE.
JDBC con DAO . Con
muchas alternativas in-

Como trabaja JPA ?


Inspirado en los frame- clases e interfaces.
works como Hibernate ,
JPA usa anotaciones para La mayoría del contenido @Entity
mapear objetos a la base de el paquete javax.persitence public class Customer {
de datos relacional. Estos son anotaciones. Con esto
objetos , usualmente lla- explicado , veremos el @Id
mados entidades, no tie- siguiente ejemplo de códi- private Long id;
nen nada en común con go : private String firstname;
los Entity Beans 2.x . Las private String lastname;
entidades JPA son clases private String telephone;
POJOs, no extienden de private String email;
ninguna clase y no imple- private Integer age;
mentan ninguna interface. // constuctors, getters,
Usted no necesita archi- setters
vos descriptores XML }
para hacer los mapeos . Si
uno se fija en el API ( java
doc ) uno observara que
esta compuesto de pocas
JAVA PERSISTENCE API Page 2

Lo mínimo necesario ...


simple . Esta tiene un identifi-
cador (id) , un nombre ( first- El código que sigue muestra lo
name) , apellido ( lastname) un mínimo requerido para definir
numero de teléfono ( telep- un "persistence object" . Ahora
hone ) , mail ( email ) y edad vamos a manipular este objeto,
del cliente ( age ) . Para ser deseo persistir mi objeto
persistente esta clase tiene que “customer” , actualizar alguna
seguir algunos reglas JPA sim- de sus propiedades y borrarlo
ples : de la base de datos. Estas ope-
raciones serán echas a través de
• La clase tiene que ser identificada la interface
como una entidad usando la javax.persistence.EntityManager de JPA.
anotación
@Entity @javax.persistence.Entity
public class Customer { Esta parte de código Para esto, quienes estén fami-
que vimos ( construc- • Una propiedad de la clase tiene liarizados con el patrón DAO ,
tores , getter y setter que tener un identificador anota- el EntityManager puede ser
@Id do con @javax.persistence.Id
private Long id; no son mostrados visto como una clase DAO que
private String firstname; para hacer esto mas • Tiene que haber un constructor nos provee un set de métodos
private String lastname; fácil de leer ) muestra sin argumentos clásicos ( persist , remove ) y
private String telephone; una clase “Customer” buscadores ( find ).
private String email;
private Integer age;
// constuctors, EntityManager ...
//getters, setters
} Después de crear el ger.persist() para insertar este tyManager.remove() , notar que
EntityManager usan- objeto en la base de datos. Yo este código usa transacciones
do un factory ( Entity- puedo luego buscar este objeto explicitas . Es por eso que los
ManagerFactory ) , por su identificador usando el métodos persist , update , y
// métodos CRUD instanciare mi objeto método EntityManger.find() y remove son llamados entre
public void createCustomer() {
“Customer” ( usando actualizar el mail usando los transacion.begin() y transac-
// Gets an entity manager el operador new como métodos "set" . tion.commit(). Vea los méto-
EntityManagerFactory emf = Persisten-
ce.createEntityManagerFactory("watermelonPU"); cualquier otro objeto dos CRUD
EntityManager em = emf.createEntityManager(); JAVA ) y le pasare La interface EntityManger no
EntityTransaction trans = em.getTransaction
(); algo de data como el tiene un método update . Los Que base de datos usamos ? ,
// Instantiates a customer object
"id" , "last name" , updates se hacen a través de las La respuesta esta en EntityMa-
Customer customer = new Customer(1L, "John", "email" , etc. Usare el propiedades "setters" . Luego nagerFactory, Esta toma un
"Lennon", "441909", "john@lenon.com", 66);
método EntityMana- borrare el objeto usando Enti- parámetro que se refiere a una
// Persists the customer
trans.begin();
em.persist(customer);
trans.commit();
Persistence.xml
// Finds the customer by primary key
customer = em.find(Customer.class, 1L);
System.out.println(customer.getEmail()); especifica unidad de <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence">

// Updates the customer email address persistencia ( water- <persistence-unit name="watermelonPU" transaction-type="RESOURCE_LOCAL">
trans.begin(); melonPU en este ca- <provider>
customer.setEmail("john@beatles.co.uk"); oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider
trans.commit(); so ) . Una unidad de </provider>
// Deletes the customer persistencia es decla- <class>entity.Customer</class>
<properties>
trans.begin();
em.remove(customer);
rada en el archivo <property name="toplink.jdbc.url"
trans.commit(); “persistence.xml” y value="jdbc:mysql://localhost:3306/watermelonDB"/>
<property name="toplink.jdbc.user" value="root"/>
// Closes the entity manager and the factory contiene información <property name="toplink.jdbc.driver" value="com.mysql.jdbc.Driver"/>
em.close(); como la base de datos <property name="toplink.jdbc.password" value=""/>
emf.close(); <property name="toplink.target-database" value="MySQL4"/>
} a usar y el driver <property name="toplink.ddl-generation" value="create-tables"/>
JDBC . </properties>
</persistence-unit>
</persistence>
Page 3 JAVA PERSISTENCE API

Unidad de Persistencia
En el código arriba , hay solo pLink para generar las tablas fecto hace mas fácil la progra-
una unidad de persistencia , automáticamente si estas no mación para el desarrollador .
llamada watermelonPU ( el existen aun . Esto significa que
archivo persistence.xml puede una vez ejecutado el código
contener muchas unidades de TopLink a creado una tabla
persistencia ) . Usted puede para guardar la información de
pensar en una unidad de persis- “customers”. la tabla 1 muestra
tencia como un conjunto de la información DDL ( data
entidades ( el elemento class ) definition languaje ) de la tabla
que comparten propiedades “customer”.
comunes . En este caso , estas
propiedades son : url de la base Este es el DDL que JPA gene-
de datos , driver JDBC , cre- ra automáticamente de la clase
denciales. Bajo el elemento anotada “Customer” . Gracias
"properties" usted encontrara a la “codificación por defecto”
propiedades especificas para que guía JPA ( y en general
Top-link por ejemplo to- Java EE 5 ) No tengo que
plink.ddl-generation . Esta hacer mucho para generar este
propiedad es usada por To- DDL , la codificación por de-

Customizando
Usted solo necesita añadir dades . Los
código customizado cuando el tipos de da-
código por defecto es inade- tos son tam-
cuado . En mi caso, porque yo bién mapea-
nunca especifico el nombre de dos por de-
la tabla o columnas en la clase fecto ( ejem-
“Customer” JPA asume que el plo String es
nombre de la tablas es igual al mapeado a
nombre de la clase y que el varchar
nombre de la columna tiene el (255) ).
mismo nombre de las propie-

Añadiendo funcionalidades y customizando el mapeo


En este punto yo quisiera me-
jorar algunas cosas. Primero • AUTO (default) permite al Ahora quiero mejorar mi ma-
que todo, yo no quiero setear proveedor de persistencia
peo . Primero cambiar el nom-
un identificador (primary key) (TopLink en mi caso) decidir
cual de las tres posibilidades bre de la tabla a "t_customer"
del objeto pero en lugar de usar. en lugar de "customer". Luego
esto quiero que JPA lo incre- • SEQUENCE usar un SQL hare el nombre ( first name ) y
mente automáticamente. Gra- sequence para obtener el el apellido ( last name ) obliga-
cias a las anotaciones , esto es próximo primary key
torios. El máximo largo de un
fácil de hacer. • TABLE requiere una tabla numero de teléfono
con dos columnas : el nombre
de la secuencia y su valor (telephone) tiene que ser 15
Solo necesito anotar mi atribu- caracteres y la columna email
( esta es la estrategia por
to identificador defecto de TopLink ) tiene que ser renombrada a
@javax.persitence.GeneratedValue .
• IDENTITY usa un “e_mail” con un "underscore" .
Esta anotación genera un pri- "identity generator" , ej :una
mary Key en cuatro posibles columna definida como
modos: auto_increment en MySQL.
JAVA PERSISTENCE API Page 4

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 atributos lista-
dos en la Tabla 3

Una aplicación

puede ser

notificada antes

o después de

ocurridos estos

eventos JPA
Anotaciones Callback
usando

"callback Ahora con el mapeo entre la actualizada o removida . Una Quiero verificar que el primer
clase Customer y la tabla aplicación puede ser notificada carácter del teléfono es "+" ,
annotations" t_customer quedo mejor gra- antes o después de ocurridos Yo puedo hacer esto antes que
cias a que las anotaciones estos eventos usando anotacio- la entidad sea persistida o actua-
@Column y @Table tienen nes . JPA tiene un conjunto de lizada . solo tengo que crear un
método ( validatePhoneNum-
muchos atributos. "callback annotatios" que pue-
ber en mi ejemplo , pero el
den ser usadas en métodos y nombre es irrelevante ) con
Hay otras dos cosas que quisie-
permiten al desarrollador añadir algo de lógica de negocios y con
ra hacer . Primero asegurarme
que todo
teléfono es
ingresado
usando códi-
gos interna-
cionales .
comenzando
con el
símbolo
'+' . Segun-
cualquier regla de negocios que las anotaciones @PrePersist y
do , calcular la edad del
desee . JPA llamara al método @PreUpdate, JPA hace el re-
“customer” a partir de la fecha
anotado antes o después de sto.
de nacimiento . Tengo muchas
estos eventos . La tabla 4 mues-
alternativas de como hacer estas El código de ejemplo en la si-
tra las "callback anotations"
tareas , pero voy a usar guiente pagina.
"callback annotations" Como uso las “callback annota-
Durante su ciclo de vida , una tios” para mis necesidades ? ,
entidad es cargada , persistida , Primero me encargare del for-
mato del numero de teléfono .
@PrePersist
@PreUpdate
Page 5
private void validatePhoneNumber() {
if (telephone.charAt(0) != '+')
Para la edad del cliente , hare throw new IllegalArgumentException
algo similar, calculare la edad del ("Invalid phone number");
cliente antes que la fecha de na- }
cimiento sea insertada }
( @PostPersist ) o actualizada
( @PostUpdate ) , y claro cada
vez que el cliente es cargado de @PostLoad
la base de datos ( @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;
}

Anotaciones Callback .. añadir anotaciones

Para hacer que esto funcione . estoy en la capacidad de calcu-


necesito añadir un nuevo atri- lar la edad del cliente pero yo
buto a la clase Customer : “date necesito persistir esa informa-
of birth” . Para notificar a JPA ción ? , NO , conociendo que el
que mapee este atributo a una valor cambia todos los años .
fecha , uso la anotación Para hacer que esta propiedad
@Temporal con el atributo (age) edad no sea persistente ,
TemporalType.DATE ( las usare la anotación @Transient
opciones son DATE , TIME , ( La tabla no tendrá una colum-
TIMESTAMP ) . Entonces na edad mas ) ver tabla 5.
JAVA PERSISTENCE API Page 6

One to One Relationship


Ahora que tengo mapeada mi tiene una y solo una una clase separada, clase Ad-
clase Customer y tengo anota- “address“ (dirección) entonces dress con un id , una calle
ciones callback para validar y Watermelon pueden enviar al (street) , una ciudad (city) , un
calcular data , necesito añadir cliente un regalo por su cum- código postal ( zip code ), y un
una dirección. Un Customer pleaños . Lo representare como país (country).

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("John", "Lennon",
Como tu pueden ver en la tabla "+441909", "john@lenon.com", dateOfBirth);
con su “address” Address homeAddress = new Address("Abbey Road",
6 , la clase Address usa la ano-
"London", "SW14", "UK");
tación @Entity para notificar a customer.setHomeAddress(homeAddress);
JPA que es una clase persisten-
te y @Column para customizar // Persists the customer with its address
el mapeo. Creando la relación trans.begin();
em.persist(homeAddress);
entre Customer y Address es em.persist(customer);
simple . Yo simplemente añado trans.commit();
una propiedad Address en la
clase Customer . Para persistir // Deletes the customer and the address
trans.begin();
la dirección de los "customer's" em.remove(customer);
uso el código que sigue : em.remove(homeAddress);
trans.commit();
}

One to One Relationship


también remuevo el address. opcionales y basadas en mis
Como este código muestra ,
requerimientos, es decir al per-
usted tiene primero que instan- Pero persistiendo y removien-
sistir o remover customer tam-
ciar un objeto Customer y un do ambos objetos parece que
bién se hace con su address.
Adress , Para linkear los se hiciera mas trabajo que el
dos ,uso un método setter ( set que necesito. Seria mejor si yo
Quiero que un Customer tenga
HomeAddress ) y luego persis- pudiera persistir o remover
exactamente un Address , sig-
tido cada objeto , cada uno en justo el objeto raíz ( el Custo-
nifica que un valor null no es
la misma transacción. Porque mer ) y permitir las dependen-
permitido . Puedo lograr todo
no tiene sentido tener un cias que se persistan o remue-
eso usando la anotación
adress en el sistema que no este van automáticamente ? , To-
@OneToOne junto con
linkeado a un customer , cuan- pLink y la codificación por
do yo remuevo el customer defecto hará las asociaciones @JoinColumn.
Page 7 JAVA PERSISTENCE API

One to One Relationship


@OneToOne es usada para ción de la casa "homeAddress"
anotar una relación . Este tiene tan pronto como el objeto
muchas propiedades incluyen- Customer es cargado.
do un cascade usado para La anotación @JoinColumn
"cascading" de cualquier tipo
tiene los mismos atributos co-
de acción. En este ejemplo , mo @Column , excepto que es
quiero "cascade" la acción de
usado para asociar atributos ,
persistir y remover. De esta En este ejemplo , Yo renom-
forma , cuando hago remove
bro la llave foránea en un ad-
o persist de un objeto custo- dress_fk y no permito ningún
mer este automáticamente lleva
valor null ( nullable=false)
acabo esta acción para el
"address" . El atributo fetch
dice a JPA que política usar JPA entonces creara los si-
cuando cargamos una relación. guientes DDLs con una cons-
Esta puede ser una asociación traint de integridad ,para la
de carga tardía ( lazy loading relación entre tablas
LAZY) , o "eagerly" (EAGER) t_customer y t_adress ( ver El atributo
porque quiero cargar la direc- tabla 7 )
fetch dice a JPA

@Entity
que política usar
@Table(name = "t_customer")
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 = "address_fk", nullable = false)
private Address homeAddress;

// constuctors, getters, setters


}
JAVA PERSISTENCE API Page 8

Querying Objects
complejos sobre objetos ( aso- radores para filtrar la data ( IN ,
Hasta ahora yo estoy usando ciaciones , herencia , clases NOT IN , EXIST , LIKE , IS NULL , IS
JPA para mapear mis objetos a abstractas ) NOT NULL ) o para controlar las
una base de datos relacional y colecciones ( IS EMPTY , IS NOT
usar el entity manager para Los querys usan las palabras EMPTY , MEMBER OF ) , También
hacer algunas operaciones SELECT , FROM y WHE- hay funciones para manejar
CRUD (Create, read, update RE , mas un conjunto de ope- Strings ( LOWER , UPPER , TRIM ,
and delete ) , Pero CONCAT , LENGTH , SUBSTRING ) ,
JPA también permite // Finds the customers who are called John números ( ABS ,
que hagas querys SQRT , MOD ) , o
sobre los objetos. colecciones
Query query = em.createQuery("SELECT c ( COUNT , MIN ,
Esto usa "Java Persis- FROM Customer c WHERE
MAX , SUM ). Co-
tence Query Langua- c.firstname='John'");
ge" ( JPQL) , el cual List<Customer> customers = que- mo SQL , tu
es similar a SQL y es ry.getResultList(); también puedes
también independien- ordenar los
Usted tiene que te de la base de da- resultados
// Same query but using a parameter ( ORDER BY) o
tos , Este es un len- //Query query = em.createQuery("SELECT c FROM
hacer querys no guaje rico que nos agruparlos
//Customer c WHERE c.firstname=:param"); (GROUP BY)
permite hacer querys //query.setParameter(":param", "John");
sobre una tabla,

mas bien sobre


Querying Objects, sobre objetos ...
un objeto. Para hacer querys sobre los formas el string "John" es bus-
objetos se necesita EntityMa- cado : puede ser parte del que-
nager para crear un objeto ry JPQL o puede ser pasado
Query . Luego tengo el resulta- como parámetro , en este ulti-
do del query llamando el ge- mo caso necesito usar el méto-
tResultList o genSingleResult do setParameter.
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 dos for-
mas . De cualquiera de las dos

JPQL Queries, ejemplos ...


es la primera propiedad del Acá hay un conjunto de querys
Como usted puede ver en este
objeto “customer” . Si ustedes que nosotros podemos hacer
query JPQL usa la notación
quieren buscar todos los con JPQL . Ver tabla 8
objeto . Usted tiene que hacer
query no sobre una tabla, mas “customers” que viven en U.S.
bien sobre un objeto. El carác- , ustedes pueden usar esta no-
ter “c” ( el nombre es irrele- tación , para obtener al atributo
vante ) es el alias de un objeto country del objeto address.
SELECT c FROM Customer c WHERE
“customer” y el “c.firstname” c.homeAddress.country='US';