JPA: Maestro / detalle con clave primaria compartida
Hace casi 10 años (wow!) había publicado un artículo sobre las claves compuestas en entidades de tipo Maestro / Detalle. Bueno, aquí está una super actualización. Esa vez fue hecha con JPA 1.0, ahora lo mostraré más actualizado y mejorado con con el JPA 2.0.
El ejemplo será usado del clásico: Factura / Detalle factura; donde Factura será el "maestro" y "Detalle Factura" su detalle. La tabla de Detalle deberá tener el código de la factura y un correlativo. Ambos campos serán parte de la clave primaria del detalle.
Clase completa en Factura.
Como se ve, luce totalmente normal como si fuera una Entidad común y silvestre. Además, tiene una lista (línea 44) que contendrá todas las filas del detalle de la factura.
Clase completa en :
La línea 35 nos parece algo raro, ya que es una clase incrustada
El mapeo entre el detalle y el maestro se dá en la línea 39. Es una relación muchos-a-uno
Ahora veamos qué contiene la clase
Clase completa en :
La clase luce como cualquiera con anotación
Ahora bien, necesitamos crear el detalle, y como ya tenemos el ID del Maestro, entonces simplemente le pasamos al objeto. El campo
Listo, ya tenemos dos objetos como detalle de la factura. Si lo consultamos, veamos lo que aparece:
Pero en las tablas, el resultado será este.
El "maestro":
Y del "detalle" así:
Twitter
Facebook
El ejemplo será usado del clásico: Factura / Detalle factura; donde Factura será el "maestro" y "Detalle Factura" su detalle. La tabla de Detalle deberá tener el código de la factura y un correlativo. Ambos campos serán parte de la clave primaria del detalle.
Clase "maestro"
Primero comencemos por definir la clase "Maestro":
@Entity public class Factura implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long facturaId; @Temporal(javax.persistence.TemporalType.DATE) private Date fechaFactura; @OneToMany(mappedBy = "factura") private List<FacturaDetalle> detalle; //...
Clase completa en Factura.
Como se ve, luce totalmente normal como si fuera una Entidad común y silvestre. Además, tiene una lista (línea 44) que contendrá todas las filas del detalle de la factura.
Clase "detalle"
Ahora veremos cómo luce esa clase detalle.@Entity @Table(name = "FACTURA_DETALLE") public class FacturaDetalle implements Serializable { @EmbeddedId private FacturaDetallePK facturaDetallePK; @MapsId("facturaId") @ManyToOne private Factura factura; private String descripcion; public FacturaDetalle() { } public FacturaDetalle(long facturaId, int ordenId) { facturaDetallePK = new FacturaDetallePK(facturaId, ordenId); } //...
Clase completa en :
FacturaDetalle
.
La línea 35 nos parece algo raro, ya que es una clase incrustada
@EmbeddedId
. En esta estarán los campos de la clave primaria.El mapeo entre el detalle y el maestro se dá en la línea 39. Es una relación muchos-a-uno
@ManyToOne
, y el campo relacionado entre Factura y el Detalle se dan por el atributo facturaId
. Esa relación se da en la línea 37 con la anotación @MapsId
.Ahora veamos qué contiene la clase
FacturaDetallePK
Clase Clave primaria de detalle
@Embeddable public class FacturaDetallePK implements Serializable { private long facturaId; private int orderId; //...
Clase completa en :
FacturaDetallePK
.La clase luce como cualquiera con anotación
@Embeddable
. En fin, eso es todo. No requiere mucha anotación adicional.
¿Cómo se usa?
La inserción de objetos es mucho más natural. Comencemos con crear un objeto de la clase "Maestro". Ya que después de insertarlo obtendremos el ID generado.
private void start() { LOG.info("Insertando objeto factura"); Factura factura = new Factura(); factura.setFechaFactura(new Date()); persist(factura); LOG.log(Level.INFO, "Factura insertada:{0}", factura); //...
ordenId
lo he puesto en duro solo para fines de prueba:
//... LOG.info("Insertando detalle 1"); FacturaDetalle det1 = new FacturaDetalle(factura.getFacturaId(), 1); det1.setDescripcion("PCs"); det1.setFactura(factura); persist(det1); LOG.log(Level.INFO, "detalle 1 insertada{0}", det1); LOG.info("Insertando detalle 2"); FacturaDetalle det2 = new FacturaDetalle(factura.getFacturaId(), 2); det2.setDescripcion("Monitores"); det2.setFactura(factura); persist(det2); LOG.log(Level.INFO, "detalle 2 insertada{0}", det2); //...
Listo, ya tenemos dos objetos como detalle de la factura. Si lo consultamos, veamos lo que aparece:
//... LOG.info("Obteniendo objetos"); List<Factura> facturas = em.createQuery("SELECT f FROM Factura f", Factura.class).getResultList(); facturas.stream().forEach((f) -> { LOG.log(Level.INFO, "Factura: {0}", f); });
Pero en las tablas, el resultado será este.
El "maestro":
Y del "detalle" así:
Código fuente
El código fuente completo del proyecto lo pueden obtener de aquí:
Social
Maestro / detalle con clave primaria compartida. Ejemplo usando #JPA 2.0 #JavaEE7
— Apuntes de Java (@apuntesdejava) 20 de mayo de 2016
Si te gusta, dale like..https://t.co/WoKs2hNycM
تعليقات
إرسال تعليق
Si quieres hacer una pregunta más específica, hazla en los foros que tenemos habilitados en Google Groups
Ah! solo se permiten comentarios de usuarios registrados. Si tienes OpenID, bienvenido! Puedes obtener su OpenID, aquí: http://openid.net/