jpa parte 2 leonardo torres altez

7
“delivery addreses” . Cada uno de esos “addresses” puede ser usado el fin de semana o por las noches. Voy a implementar esta información añadiendo “tags” a un “address”. Para hacer todo esto , voy a usar relaciones de tipo one-to-many y many-to-many de JPA. La Figura muestra el Este articulo se va construir sobre el ejem- plo del primero : Una libreta de direcciones para una empresa ficti- 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” que tienen un “first name”,un “last name” y una “date of birth”, las “companies” tienen un “name” un “contract name”, y un “number of employees”. Esto me da la oportunidad de mapear una herencia con JPA “Customers” tienen un “home address” pero Watermelon también necesita definir un Nuevos requerimientos de Watermelon ... JAVA DEVELOPER - INSTRUCTOR - LEONARDO TORRES ALTEZ Advanced Mapping with JPA Herencia con JPA ? Como dije Waterme- lon ahora tiene dos tipos de “customers” : “individuals” y “companies”. En el modelo puedo descri- bir esto usando una clase abstracta Custo- mer de la cual van a heredar las clases “Individual” y “Company”. “Customer” tiene un ID , un “telephone number” , un “email address” , y un método para validar “telephone numbers” ( ver Anota- ciones Callback) , Esto también maneja una la relación a el Address. Ambos Individual and Company añaden atri- butos específicos ( también un método para calcular la edad del “individual” ). La herencia es una no- ción de los lenguajes orientados a objetos en general y a Java en particular . Pero guar- dar esta relación jerár- quica en una estructura relacional (base de datos )es complicado . Hasta ahora la herencia había sido manejada en los EJBs de persis- tencia , Gracias a JPA , polimorfismo y heren- cia son posibles ahora en una aplicación Java EE y Java SE. Contraria- mente a las asociacio- nes donde solo hay una forma de hacerlas , con la herencia el des- arrollador tiene que escoger entre diferen- tes estrategias: Una simple tabla por clase heredada (SINGLE TABLE) es la estrategia por defecto, Esto significa que las tres clases Customer , Individual y Company usan una sola tabla ( ejemplo JPA JPA

Upload: a19987225

Post on 19-Jul-2015

2.355 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: Jpa Parte 2 Leonardo Torres Altez

“delivery addreses” . Cada uno de esos “addresses” puede ser usado el fin de semana o por las noches. Voy a implementar esta información añadiendo “tags” a un “address”. Para hacer todo esto , voy a usar relaciones de tipo one-to-many y many-to-many de JPA. La Figura muestra el

Este articulo se va construir sobre el ejem-plo del primero : Una libreta de direcciones para una empresa ficti-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”

que tienen un “first name”,un “last name” y una “date of birth”, las “companies” tienen un “name” un “contract name”, y un “number of employees”. Esto me da la oportunidad de mapear una herencia con JPA “Customers” tienen un “home address” pero Watermelon también necesita definir un

Nuevos requerimientos de Watermelon ...

JAVA DEVELOPER - INSTRUCTOR - LEONARDO TORRES ALTEZ

Advanced Mapping with JPA

Herencia con JPA ?

Como dije Waterme-lon ahora tiene dos tipos de “customers” : “individuals” y “companies”. En el modelo puedo descri-bir esto usando una clase abstracta Custo-mer de la cual van a heredar las clases “Individual” y “Company”. “Customer” tiene un ID , un “telephone number” , un “email address” , y un método para validar “telephone numbers” ( ver Anota-ciones Callback) , Esto

también maneja una la relación a el Address. Ambos Individual and Company añaden atri-butos específicos ( también un método para calcular la edad del “individual” ). La herencia es una no-ción de los lenguajes orientados a objetos en general y a Java en particular . Pero guar-dar esta relación jerár-quica en una estructura relacional (base de datos )es complicado . Hasta ahora la herencia

había sido manejada en los EJBs de persis-tencia , Gracias a JPA , polimorfismo y heren-cia son posibles ahora en una aplicación Java EE y Java SE. Contraria-mente a las asociacio-nes donde solo hay una forma de hacerlas , con la herencia el des-arrollador tiene que escoger entre diferen-tes estrategias: • Una simple tabla por clase

heredada (SINGLE TABLE) es la estrategia por defecto, Esto significa que las tres clases Customer , Individual y Company usan una sola tabla ( ejemplo

JPAJPA

Page 2: Jpa Parte 2 Leonardo Torres Altez

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 .

Lo mínimo necesario ...

Estrategia JOINED ...

para identificar que instancia de la clase concreta es guarda-da ( es un individual o un company que es guardado en la tabla t_customer? ). Sin ninguna anotación la codifica-ción por defecto va a crear una columna llamada DTYPE varchar(31) el cual su valor es por defecto el nombre de la clase concreta ( no abstracta ) En el siguiente código , Yo

decido nombrar la columna discriminadora DISC, y darle una longitud de 5 . Yo puedo hacer esto usando la anota-ción @javax.persistence.DicriminatorColumn . El contenido de esta columna puede ser custo-mizada en cada clase concreta usando la anotación @DicrimitarorValue. El valor tiene que ser único y sirve para identificar una clase con-

Estrategia JOINED ...

Para trabajar , la estrategia JOI-NED ( también la SINGLE TA-BLE) necesita información adi-cional : una co-lumna discrimida-dora . Esta colum-na , situada en la tabla principal , habilita al persis-tence provider

Todos los atributos de estas tres clases son mapeados a una sola tabla. Esta forma es la mejor en términos de performance ,desde que una sola tabla es involucra-da. De esta forma no se puede restringir que algunos atributos sean no nulos ( porque todas las subclases son mapeadas en la misma tabla )

• Una tabla por entity class ( TA-BLE_PER_CLASS) . Todas las propiedades de una clase concreta, incluyendo propiedades que se heredan son mapeadas a columnas de una tabla . En mi ejemplo yo solo tengo una tabla t_company y una t_individual . Los atributos de la clase base abstracta Customer 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

primary key. Esta opción provee soporte para relaciones polimórfi-cas y esta es la opción que yo voy a usar para mapear la herencia para el sistema address book de Water-melon.

Al escoger la estrategia de mapeo, esta tiene que ser echa una sola vez en la entidad raíz (clase Custo-mer), usando la anotación @javax.persistence.Inheritance . Esto es especificado en la clase Customer como sigue <—

Page 2 ADVANCED MAPPING WITH JPA

@Entity @Table(name = "t_customer") @Inheritance(strategy = InheritanceTy-pe.JOINED) @DiscriminatorColumn(name = "DISC", discri-minatorType = DiscriminatorType.STRING, length = 5) public abstract class Customer { @Id @GeneratedValue private Long id; @Column(length = 15) protected String telephone; @Column(name = "e_mail") protected String email; // constructors, getters, setters }

Page 3: Jpa Parte 2 Leonardo Torres Altez

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. �>

Unidad de Persistencia

datos.

Métodos CRUD... Una vez que has escogido la estrategia para mapear la herencia , tu puedes hacer los métodos CRUD de cualquiera estas clases concretas usando el EntityManager tal como entidad . En el siguiente códi-go yo creo una instancia de Company y una clase Indivi-dual que persisto en la base de

Page 3 JAVA PERSISTENCE API

public void createCustomers() { // Gets an entity manager EntityManagerFactory emf = Persistence.createEntityManagerFactory("watermelonPU"); EntityManager em = emf.createEntityManager(); EntityTransaction trans = em.getTransaction(); // Instantiates a company object Company company = new Company("Universal", "Mr Universe", 50000, "+15488454", "[email protected]"); company.setHomeAddress(new Address("World Street", "New York", "7448", "UK")); // Instantiates an individual object calendar.set(1940, 10, 9); Individual individual = new Individual("John", "Lennon", "+441909", "[email protected]", calendar.getTime()); individual.setHomeAddress(new Address("Abbey Road", "London", "SW14", "UK")); // Persists both customers trans.begin(); em.persist(company); em.persist(individual); trans.commit(); // Closes the entity manager and the factory em.close(); emf.close(); }

Page 4: Jpa Parte 2 Leonardo Torres Altez

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

Page 4 ADVANCED MAPPING WITH JPA

Page 5: Jpa Parte 2 Leonardo Torres Altez

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.

Page 5

@Entity @Table(name = "t_customer") @Inheritance(strategy = InheritanceType.JOINED) @DiscriminatorColumn(name = "DISC", discriminatorType = DiscriminatorTy-pe.STRING, length = 5) public abstract class Customer { @Id @GeneratedValue private Long id; @Column(length = 15) protected String telephone; @Column(name = "e_mail") protected String email; @OneToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, Cas-cadeType.REMOVE}) @JoinColumn(name = "home_address_fk", nullable = false) private Address homeAddress; @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinTable(name = "t_delivery_addresses", joinColumns = {@JoinColumn(name = "customer_fk")}, inverseJoinColumns = {@JoinColumn(name = "address_fk")}) 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.

One to Many ...

Page 6: Jpa Parte 2 Leonardo Torres Altez

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.

Many to Many ...

Para ayudar a clasificar los diferentes �addresses� que un �customer� puede tener Wa-termelon quiere clasificarlos ( tag ) . Un tag es una etiqueta ( un String) que puede ser añadido a una colección de addresses. �Tag� y �Address� tienen una relación bi�direccional , �many-to-

many� . En la base de datos , esta información será guarda-da exactamente de la misma forma que las relaciones �one-to-many� : usando una tercera tabla que une los pri-mary keys. En la Tabla 4 tu encontraras la anotación @ManyToMany usada en conjunción con @JoinTable y @JoinColumn.

Page 6 ADVANCED MAPPING WITH JPA

Page 7: Jpa Parte 2 Leonardo Torres Altez

One to One Relationship

Page 7 JAVA PERSISTENCE API

Tag tag1 = new Tag("24/7"); Tag tag2 = new Tag("working hours"); Tag tag3 = new Tag("week-ends"); Address address = new Address("Central Side Park", "New York", "7845", "US"); 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("SELECT a FROM Address a WHERE a.city IN ('London', 'New York') "); addresses = query.getResultList(); // Finds all the addresses that do not have a tag query = em.createQuery("SELECT a FROM Address a WHERE a.tags IS EMPTY"); addresses = query.getResultList(); // Finds all the addresses that have at least one tag query = em.createQuery("SELECT a FROM Address a WHERE a.tags IS NOT EMPTY"); addresses = query.getResultList(); // Finds all the addresses that have a "week-ends" tag query = em.createQuery("SELECT a FROM Address a WHERE :param MEMBER OF a.tags"); query.setParameter("param", new Tag("week-ends")); addresses = query.getResultList();