Aquí se muestra un ejemplo de como manejar las claves compuestas en JPA, para este caso se usa de inicio las anotaciones @IdClass y @EmbeddedId.
La estructura de la tabla es la siguiente, la tabla pue`e quedar mas completa añadiendo o eliminando campos pero solo es para mostrar el uso de la llave compuesta
Aquí se puede ver la clave compuesta con los campos folio y serie.
Creamos la clase que representa la clave compuesta
public class IdFactura implements Serializable{
private int folio;
private String serie;
public IdFactura(int folio, String serie) {
super();
this.folio = folio;
this.serie = serie;
}
}
Cabe señalar que la clase IdFactura no esta escrita o no se añade alguna anotacion en particular, solo contiene las propiedades y constructores, ahora se genera la entidad Factura
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.dracof.jpa.IdFactura;
@Entity
@Table (name="Factura")
@IdClass (IdFactura.class)
public class Factura implements Serializable {
@Id
@Column
private int folio;
@Id
@Column
private String serie;
@Column
private String emisor;
@Column
@Temporal (TemporalType.DATE)
private Date fecha;
//setters & getters
}
Aquí escribimos @IdClass (IdFactura.class) para indicar que haremos uso de la clase IdFactura para representar a la llave compuesta
Se genera el cliente para realizar la inserción y la consulta
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.dracof.jpa.entities.Factura;
public class Cliente {
public static void main(String[] args) {
int folio = 1;
String serie = "B";
Cliente cliente = new Cliente();
cliente.generaFactura(folio, serie);
cliente.consultaFactura(folio, serie);
}
private void generaFactura(int folio, String serie) {
System.out.println("Insertando Factura...");
EntityManagerFactory emf = null;
EntityManager em = null;
Factura factura = new Factura();
factura.setFolio(folio);
factura.setSerie(serie);
factura.setEmisor("Empresa X");
factura.setFecha(null);
try {
emf = Persistence.createEntityManagerFactory("unitPersistence");
em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(factura);
em.getTransaction().commit();
System.out.println("Factura insertada correctamente");
} catch (Exception e) {
System.out.println("Existe un error al insertar factra");
em.getTransaction().rollback();
e.printStackTrace();
} finally {
em.close();
emf.close();
}
}
private void consultaFactura(int folio, String serie) {
System.out.println("Consultando Factura...");
EntityManagerFactory emf = null;
EntityManager em = null;
IdFactura idFactura = new IdFactura(folio, serie);
try {
emf = Persistence.createEntityManagerFactory("unitPersistence");
em = emf.createEntityManager();
Factura factura = em.find(Factura.class, idFactura);
System.out.println("Folio: " + factura.getFolio());
System.out.println("Serie: " + factura.getSerie());
System.out.println("Emisor: " + factura.getEmisor());
System.out.println("Fecha: " + factura.getFecha());
} catch (Exception e) {
e.printStackTrace();
} finally {
em.close();
emf.close();
}
}
}
Y vemos el resultado de ejecutar el cliente
Insertando Factura...
Factura insertada correctamente
Consultando Factura...
Folio: 1
Serie: B
Emisor: Empresa X
Fecha: null
Ahora se realizan algunos cambios para hacer la prueba con @EmbeddableId
Se modifica la clase IdFactura de la siguiente forma
@Embeddable
public class IdFactura implements Serializable {
@Column
private int folio;
@Column
private String serie;
public IdFactura() {
}
public IdFactura(int folio, String serie) {
super();
this.folio = folio;
this.serie = serie;
}
public int getFolio() {
return folio;
}
public String getSerie() {
return serie;
}
//en este caso omiti los setters para convertir la clase en inmutable
}
Se nota que ahora la clase IdFactura se anota con @Embeddable y los campos folio y serie ahora contienen @Column, hay que recordar que como los campos serie y folio se llaman igual que los campos de la tabla podemos omitir el parámetro name de Column
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.dracof.jpa.IdFactura;
@Entity
@Table (name="Factura")
public class Factura implements Serializable {
@EmbeddedId
private IdFactura idFactura;
@Column
private String emisor;
@Column
@Temporal (TemporalType.DATE)
private Date fecha;
public IdFactura getIdFactura() {
return idFactura;
}
public void setIdFactura(IdFactura idFactura) {
this.idFactura = idFactura;
}
public String getEmisor() {
return emisor;
}
public void setEmisor(String emisor) {
this.emisor = emisor;
}
public Date getFecha() {
return fecha;
}
public void setFecha(Date fecha) {
this.fecha = fecha;
}
}
Observamos que ahora para representar la llave compuesta dentro de Factura se usa @EmbeddedId y usamos IdFactura como una propiedad. El cliente se modifica ligeramente quedando
public class Cliente {
public static void main(String[] args) {
...
}
private void generaFactura(int folio, String serie) {
...
Factura factura = new Factura();
IdFactura idFactura = new IdFactura(folio, serie);
factura.setIdFactura(idFactura);
factura.setEmisor("Empresa X");
factura.setFecha(null);
...
}
private void consultaFactura(int folio, String serie) {
// no sufre cambios
}
}
Obteniendo el mismo resultado
Insertando Factura...
Factura insertada correctamente
Consultando Factura...
Folio: 1
Serie: B
Emisor: Empresa X
Fecha: null
Ahora realizamos el ejercicio usando solo XML, entonces borramos todas las anotaciones y la configuración del XML usando id-class y embeddable respectivamente
<entity-mappings>
<entity class="org.dracof.jpa.entities.Factura">
<table name="Factura"/>
<id-class class="org.dracof.jpa.IdFactura"/>
<attributes>
<id name="folio">
<column/>
</id>
<id name="serie">
<column/>
</id>
<basic name="emisor">
<column/>
</basic>
<basic name="fecha">
<temporal>DATE</temporal>
<column/>
</basic>
</attributes>
</entity>
</entity-mappings>
y
<entity-mappings>
<embeddable class="org.dracof.jpa.IdFactura">
<attributes>
<basic name="folio">
<column/>
</basic>
<basic name="serie">
<column/>
</basic>
</attributes>
</embeddable>
<entity class="org.dracof.jpa.entities.Factura">
<table name="Factura"/>
<attributes>
<embedded-id name="idFactura"/>
<basic name="emisor">
<column/>
</basic>
<basic name="fecha">
<temporal>DATE</temporal>
<column/>
</basic>
</attributes>
</entity>
</entity-mappings>
La estructura del proyecto quedo como sigue
Suerte.