El concepto de JPA polymorphic query , es uno de los conceptos más clásicos cuando nos encontramos trabajando con JPA y herencia . En estas casuísticas es habitual tener que realizar consultas polimórficas que afecten a varias entidades y ejecuten una funcionalidad concreta en un árbol de herencia. Vamos a ver un ejemplo sencillo usando las clases Pago , PagoPayPal y PagoVisa que son clases que se encuentran relacionadas en una estructura de herencia siendo la clase Pago la padre de las otras dos .
JPA y Herencia
Para poder construir consultas polimórficas primero tendremos que diseñar una estructura jerárquica con JPA y las anotaciones orientadas a Single Table Inheritance . Vamos a verlo en código:
package com.arquitecturajava;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
@Entity
@Table(name="pagos")
@DiscriminatorColumn(name="tipo")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Pago {
@Id
private int numero;
private double importe;
public Pago(int numero, double importe) {
super();
this.numero = numero;
this.importe = importe;
}
public int getNumero() {
return numero;
}
public void setNumero(int numero) {
this.numero = numero;
}
public double getImporte() {
return importe;
}
public void setImporte(double importe) {
this.importe = importe;
}
public abstract void pagar();
} Como podemos ver esta es una clase Abstracta que almacena las propiedades y define un método pagar vacío para que otras clases hijas lo implementen.
package com.arquitecturajava;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
@Entity
@DiscriminatorValue(value="paypal")
public class PagoPayPal extends Pago {
private String cuenta;
public PagoPayPal(int numero, double importe,String cuenta) {
super(numero, importe);
this.cuenta=cuenta;
// TODO Auto-generated constructor stub
}
public String getCuenta() {
return cuenta;
}
public void setCuenta(String cuenta) {
this.cuenta = cuenta;
}
@Override
public void pagar() {
System.out.println("estoy pagando con paypal");
}
} package com.arquitecturajava;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
@Entity
@Table(name="pagos")
@DiscriminatorColumn(name="tipo")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Pago {
@Id
private int numero;
private double importe;
public Pago(int numero, double importe) {
super();
this.numero = numero;
this.importe = importe;
}
public int getNumero() {
return numero;
}
public void setNumero(int numero) {
this.numero = numero;
}
public double getImporte() {
return importe;
}
public void setImporte(double importe) {
this.importe = importe;
}
public abstract void pagar();
} Acabamos de construir las clases que directamente heredan de la clase Pago y sobreescriben el método pagar(). Es momento de construir un programa main que se encargue de ejecutar una consulta contra la base de datos y obtenernos una entidad :
package com.arquitecturajava;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.TypedQuery;
public class Principal19 {
public static void main(String[] args) {
EntityManagerFactory emf= Persistence.createEntityManagerFactory("biblioteca");
EntityManager em= emf.createEntityManager();
PagoVisa pv= new PagoVisa(2, 200, 23456789);
PagoPayPal pp= new PagoPayPal(3, 200, "cecilio");
EntityTransaction t= em.getTransaction();
t.begin();
em.persist(pv);
em.persist(pp);
TypedQuery<PagoVisa> consulta=
em.createQuery("select p from PagoVisa p",PagoVisa.class);
List<PagoVisa> lista= consulta.getResultList();
for (PagoVisa p: lista) {
System.out.println(p.getTarjeta());
p.pagar();
}
t.commit();
}
} En este caso he creado dos objetos y los he insertado en la base de datos para posteriormente realizar una consulta con el PagoVisa. Si ejecuto el código el resultado es el esperado:
JPA Polymorphic Query
Ahora bien podemos construir un código más flexible que pueda pagar tanto con PagoVisa como con PagoPayPal . Para ello cambiaremos la consulta para que sea una consulta polimórfica y pueda gestionar las dos entidades al mismo tiempo:
Veamos el código:
TypedQuery<Pago> consulta=
em.createQuery("select p from Pago p where Type(p) IN (PagoVisa,PagoPayPal)",Pago.class);
List<Pago> lista= consulta.getResultList();
for (Pago p: lista) {
System.out.println(p.getNumero());
System.out.println(p.getImporte());
p.pagar();
} En este caso estamos recorriendo con el concepto de Pago ambas entidades e imprimiendo por la consola los datos de cada pago así como el tipo de pago que se realiza finalmente.
Otros artículos relacionados
- JPA Transaction, un concepto importante
- JPA Orphan Removal y como usarlo
- Eclipse JPA y clases de dominio