1. JAVA DEVELOPER - INSTRUCTOR - LEONARDO TORRES ALTEZ
Advanced Mapping with JPA
Nuevos requerimientos de Watermelon ...
que tienen un “first
JPA
name”,un “last name” y “delivery addreses” .
una “date of birth”, las Cada uno de esos
“companies” tienen un “addresses” puede ser
“name” un “contract usado el fin de semana
name”, y un “number o por las noches. Voy
of employees”. Esto me a implementar esta
da la oportunidad de información añadiendo
mapear una herencia “tags” a un “address”.
Este articulo se va
con JPA Para hacer todo esto ,
construir sobre el ejem-
“Customers” tienen un voy a usar relaciones
plo del primero : Una
“home address” pero de tipo one-to-many y
libreta de direcciones
Watermelon también many-to-many de JPA.
para una empresa ficti-
necesita definir un La Figura muestra el
cia de música llamada
Watermelon. Para em-
pezar , vamos a imagi-
nar que Watermelon
tiene algunos nuevos
requerimientos , ahora
hay dos tipos de
“customers”:
“individuals” y
“companies”.
“individuals” son un
tipo de “customers”
Herencia con JPA ?
había sido manejada
Como dije Waterme- también maneja una la
en los EJBs de persis-
lon ahora tiene dos relación a el Address.
tencia , Gracias a JPA ,
tipos de “customers” : Ambos Individual and
polimorfismo y heren-
“individuals” y Company añaden atri-
cia son posibles ahora
“companies”. En el butos específicos
en una aplicación Java
modelo puedo descri- ( también un método
EE y Java SE. Contraria-
bir esto usando una para calcular la edad
mente a las asociacio-
clase abstracta Custo- del “individual” ).
nes donde solo hay
mer de la cual van a
La herencia es una no- una forma de hacerlas ,
heredar las clases
con la herencia el des-
“Individual” y ción de los lenguajes
arrollador tiene que
“Company”. orientados a objetos en
escoger entre diferen-
“Customer” tiene un general y a Java en
tes estrategias:
ID , un “telephone particular . Pero guar-
number” , un “email dar esta relación jerár-
•
address” , y un método quica en una estructura Una simple tabla por clase
para validar “telephone relacional (base de heredada (SINGLE TABLE)
es la estrategia por defecto,
numbers” ( ver Anota- datos )es complicado . Esto significa que las tres
clases Customer , Individual
ciones Callback) , Esto Hasta ahora la herencia y Company usan una sola
tabla ( ejemplo
2. ADVANCED MAPPING WITH JPA Page 2
Lo mínimo necesario ...
primary key. Esta opción provee
Todos los atributos de estas tres soporte para relaciones polimórfi-
clases son mapeados a una sola cas y esta es la opción que yo voy
tabla. Esta forma es la mejor en a usar para mapear la herencia para
términos de performance ,desde el sistema address book de Water-
que una sola tabla es involucra- melon.
da. De esta forma no se puede
restringir que algunos atributos
sean no nulos ( porque todas las
Al escoger la estrategia de
subclases son mapeadas en la
mapeo, esta tiene que ser
misma tabla )
echa una sola vez en la
• Una tabla por entity class ( TA-
entidad raíz (clase Custo-
BLE_PER_CLASS) . Todas las
mer), usando la anotación
propiedades de una clase concreta,
incluyendo propiedades que se
@javax.persistence.Inheritan
heredan son mapeadas a columnas
de una tabla . En mi ejemplo yo
ce . Esto es especificado en
solo tengo una tabla t_company y
la clase Customer como
una t_individual . Los atributos de
la clase base abstracta Customer
sigue
serán copiados en estas dos tablas.
• <—
Una tabla por clase (JOINED). En
esta estrategia la clase raíz de la
jerarquía (Customer) tanto como
cada subclase es representada en
una tabla separada . En el ejemplo
de Watermelon tendré una tabla
base común t_customer con un ID ,
telephone number , y un email
adress. Las tablas de subclases
( t_company , t_individual ) serán
unidas con la principal mediante su
Estrategia JOINED ...
@Entity
@Table(name = quot;t_customerquot;)
@Inheritance(strategy = InheritanceTy-
Para trabajar , la para identificar que instancia decido nombrar la columna
pe.JOINED)
estrategia JOI- de la clase concreta es guarda- discriminadora DISC, y darle
@DiscriminatorColumn(name = quot;DISCquot;, discri-
minatorType = DiscriminatorType.STRING,
NED ( también la da ( es un individual o un una longitud de 5 . Yo puedo
length = 5)
SINGLE TA- company que es guardado en hacer esto usando la anota-
public abstract class Customer {
BLE) necesita la tabla t_customer? ). Sin ción
@Id información adi- ninguna anotación la codifica- @javax.persistence.Dicrimina
@GeneratedValue
cional : una co- ción por defecto va a crear torColumn . El contenido de
private Long id;
lumna discrimida- una columna llamada DTYPE esta columna puede ser custo-
@Column(length = 15)
dora . Esta colum- varchar(31) el cual su valor es mizada en cada clase concreta
protected String telephone;
@Column(name = quot;e_mailquot;) na , situada en la por defecto el nombre de la usando la anotación
protected String email;
tabla principal , clase concreta ( no abstracta ) @DicrimitarorValue. El valor
// constructors, getters, setters
habilita al persis- tiene que ser único y sirve
}
tence provider En el siguiente código , Yo para identificar una clase con-
Estrategia JOINED ...
creta . La codifi-
cación por defec-
to lo hará por
defecto al nom-
bre de la clase,
Pero he decidido
cambiar esto a
“indiv” y
“comp” . Ver
tabla 1 .
3. JAVA PERSISTENCE API
Page 3
Unidad de Persistencia
Vea que cada una de las tres
entidades es mapeada a su
propia tabla ( usando la anota-
ción @Table ) .Como expli-
que en el articulo previo , el
EntityManager y las propieda-
des seteadas en la unidad de
persistencia automáticamente
creara estas tablas. El resulta-
do esta estrategia JOINED
son los DDLs y los contraints
de integridad entre las tres
tablas, como se muestra en la
Tabla 2. —>
Métodos CRUD...
Una vez que has escogido la datos.
estrategia para mapear la
herencia , tu puedes hacer los public void createCustomers() {
// Gets an entity manager
métodos CRUD de cualquiera
EntityManagerFactory emf = Persistence.createEntityManagerFactory
estas clases concretas usando (quot;watermelonPUquot;);
el EntityManager tal como EntityManager em = emf.createEntityManager();
entidad . En el siguiente códi- EntityTransaction trans = em.getTransaction();
go yo creo una instancia de
// Instantiates a company object
Company y una clase Indivi- Company company = new Company(quot;Universalquot;, quot;Mr Universequot;, 50000,
dual que persisto en la base de quot;+15488454quot;, quot;info@universal.comquot;);
company.setHomeAddress(new Address(quot;World Streetquot;, quot;New Yorkquot;, quot;7448quot;,
quot;UKquot;));
// Instantiates an individual object
calendar.set(1940, 10, 9);
Individual individual = new Individual(quot;Johnquot;, quot;Lennonquot;, quot;+441909quot;,
quot;john@lenon.comquot;, calendar.getTime());
individual.setHomeAddress(new Address(quot;Abbey Roadquot;, quot;Londonquot;, quot;SW14quot;,
quot;UKquot;));
// Persists both customers
trans.begin();
em.persist(company);
em.persist(individual);
trans.commit();
// Closes the entity manager and the factory
em.close();
emf.close();
}
4. ADVANCED MAPPING WITH JPA Page 4
Hacer queries sobre un modelo jerárquico también es posible con JPQL . Yo puedo hacer queries
en las clases concretas tanto como en la clase principal abstracta
Y yo puedo usar los atributos de la clase principal en los queries. Por ejemplo, el mail address
esta definido en la clase Customer , puedo usar este atributo en los queries envolviendo la clase
customer también en la clase Individual y Company
De acuerdo : no puedo usar los atributos de la clase hija para hacer queries en la clase padre.Por
ejemplo , el atributo first name es justo definido en Individual . Yo no puedo usar este para hacer
queries de customers y companies.
En el adress book , las companies tienen un numero de employees . JPQL tiene un set de funcio-
nes aritméticas que tu puede usar en un query.
Yo también puedo usar restricciones en estos números aplicando funciones : menor que , mayor
que , igual a . También obtener un rango de empleados
5. Page 5
One to Many ...
Ahora que ya esta mapeada la herencia. Voy a fijarme en el delivery addresses. En la Figura 1 una clase “Customer” tiene
un “home address” ( mapeado en el articulo previo ) y cero o muchos delivery addresses. Esto es representado por una
relación unidireccional , “one-to-many” entre “Customer” y “Address”.
Hay dos diferentes estrategias que tu puedes usar para tener una relación “one-to-many” en una base de datos relacional .
Lo primero es colocar un foreing key en la tabla representando “many” ( t_address) , apuntando a el primary key de la
tabla representando el “one” ( t_customer ) . Lo segundo es usar una tercera tabla ( join_table ) para servir como un link
entre estas dos, usando esta estrategia las relaciones “one-to-many” en JPA usan una tabla join. ( una tercera tabla )
El siguiente código muestra como manejar una relaciones de uno a muchos. Otra vez puedo dejar la codificación por de-
fecto y a JPA mapear la relación por mi , pero decido usar anotaciones para customizar el mapeo.
@Entity
@Table(name = quot;t_customerquot;)
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = quot;DISCquot;, discriminatorType = DiscriminatorTy-
pe.STRING, length = 5)
public abstract class Customer {
@Id
@GeneratedValue
private Long id;
@Column(length = 15)
protected String telephone;
@Column(name = quot;e_mailquot;)
protected String email;
@OneToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, Cas-
cadeType.REMOVE})
@JoinColumn(name = quot;home_address_fkquot;, nullable = false)
private Address homeAddress;
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(name = quot;t_delivery_addressesquot;,
joinColumns = {@JoinColumn(name = quot;customer_fkquot;)},
inverseJoinColumns = {@JoinColumn(name = quot;address_fkquot;)})
private List deliveryAddresses = new ArrayList();
public void addDeliveryAddress(Address deliveryAddress) {
deliveryAddresses.add(deliveryAddress);
}
// constructors, getters, setters
}
La anotación @javax.persistence.OneToMany es usada para relaciones “one-to-many” tiene los mismos atributos como
@OneToOne ( ver el articulo previo) .Lo que yo estoy haciendo en el código que sigue es notificar a JPA el “lazy load” de
el atributo “delivery address” ( atributo “fetch” ) y “cascade all ” de todos los eventos ( persist , remove , ..etc ) . Como
mencione JPA mapea una relación “one-to-many” usando una join table. Para customizar el nombre y columnas de esta
join table , Yo puedo usar la anotación @javax.persistence.JoinTable . De acuerdo a el código , la tabla join se llamara
(t_delivery_address ) . Para
cambiar los nombres de los
foreing key , yo tengo que
usar la anotación
@JoinColumn.
Con todas estas anotacio-
nes , JPA va a crear y ade-
cuar las tablas y los integrity
constraints. La tabla 3 mues-
tra los DDLs de las tres ta-
blas involucradas en la rela-
ción.
6. ADVANCED MAPPING WITH JPA Page 6
Many to Many ...
many” . En la base de datos ,
Para ayudar a clasificar los esta información será guarda-
diferentes “addresses” que un da exactamente de la misma
“customer” puede tener Wa- forma que las relaciones
termelon quiere clasificarlos “one-to-many” : usando una
( tag ) . Un tag es una etiqueta tercera tabla que une los pri-
( un String) que puede ser mary keys. En la Tabla 4 tu
añadido a una colección de encontraras la anotación
addresses. “Tag” y “Address” @ManyToMany usada en
tienen una relación bi– conjunción con @JoinTable y
direccional , “many-to- @JoinColumn.
El identificador ( @id ) de la clase “Tag” es un atributo de tipo String. El valor es seteado
manualmente, por eso es que no hay una anotación @GeneratedValue. Ambas clases
usan la anotación @ManyToMany significa que la relación es bidireccional . Cualquiera
de estas clases podría definir los atributos de la tabla join ( @JoinTable) pero he decidido
que la clase Address lo haga. La tabla join entre los “addresses” y “tags” es llamada
t_address_tag .
@OneToMany y @ManyToMany lidian con colecciones de objetos. Para hacer querys
sobre colecciones , JPQL tiene un conjunto de keywords como EMPTY , que verifica si
una colección esta vacía o no , o MEMBER OF que verifica si un objeto es un miembro
de la colección . El código siguiente nos muestra como un objeto “address” y sus “tags”
están relacionados. Tanto como definir algunos querys.
7. JAVA PERSISTENCE API
Page 7
One to One Relationship
Tag tag1 = new Tag(quot;24/7quot;);
Tag tag2 = new Tag(quot;working hoursquot;);
Tag tag3 = new Tag(quot;week-endsquot;);
Address address = new Address(quot;Central Side Parkquot;, quot;New Yorkquot;, quot;7845quot;, quot;USquot;);
address.addTag(tag1);
address.addTag(tag2);
address.addTag(tag3);
// Perists the address and its tags
trans.begin();
em.persist(address);
trans.commit();
Query query;
List<Address> addresses;
// Finds all the addresses either in London or New York
query = em.createQuery(quot;SELECT a FROM Address a WHERE a.city IN ('London', 'New York') quot;);
addresses = query.getResultList();
// Finds all the addresses that do not have a tag
query = em.createQuery(quot;SELECT a FROM Address a WHERE a.tags IS EMPTYquot;);
addresses = query.getResultList();
// Finds all the addresses that have at least one tag
query = em.createQuery(quot;SELECT a FROM Address a WHERE a.tags IS NOT EMPTYquot;);
addresses = query.getResultList();
// Finds all the addresses that have a quot;week-endsquot; tag
query = em.createQuery(quot;SELECT a FROM Address a WHERE :param MEMBER OF a.tagsquot;);
query.setParameter(quot;paramquot;, new Tag(quot;week-endsquot;));
addresses = query.getResultList();