Claves primarias compuestas en JPA
Introducción
En este tutorial, aprenderemos sobre las Claves Primarias compuestas y las anotaciones correspondientes en JPA.
Claves primarias compuestas
Una clave primaria compuesta, también llamada clave compuesta, es una combinación de dos o más columnas para formar una clave primaria para una tabla.
En JPA, tenemos dos opciones para definir las claves compuestas: Las anotaciones @IdClass y @EmbeddedId.
Para definir las claves primarias compuestas, debemos seguir algunas reglas:
- La clase de clave primaria compuesta debe ser pública
- Debe tener un constructor no-arg
- Debe definir los métodos equals() y hashCode ()
- Debe ser Serializable
La anotación IdClass
digamos que tenemos una tabla llamada Cuenta y tiene dos columnas – Número de cuenta, tipo de cuenta – que forman la clave compuesta. Ahora tenemos que mapearlo en JPA.
De acuerdo con la especificación JPA, vamos a crear una clase accountId con estos campos de clave primaria:
public class AccountId implements Serializable { private String accountNumber; private String accountType; // default constructor public AccountId(String accountNumber, String accountType) { this.accountNumber = accountNumber; this.accountType = accountType; } // equals() and hashCode()}
A continuación, asociemos la clase accountId con la Cuenta de entidad.
Para hacer eso, necesitamos anotar la entidad con la anotación @IdClass. También debemos declarar los campos de la clase accountId en la Cuenta de entidad y anotarlos con @Id:
@Entity@IdClass(AccountId.class)public class Account { @Id private String accountNumber; @Id private String accountType; // other fields, getters and setters}
La anotación EmbeddedId
@EmbeddedId es una alternativa a la anotación @IdClass.
Consideremos otro ejemplo en el que tenemos que conservar cierta información de un libro con el título y el idioma como campos clave principales.
En este caso, la clase de clave primaria, BookID, debe estar anotada con @Embeddable:
@Embeddablepublic class BookId implements Serializable { private String title; private String language; // default constructor public BookId(String title, String language) { this.title = title; this.language = language; } // getters, equals() and hashCode() methods}
Luego, necesitamos incrustar esta clase en la entidad del libro usando @EmbeddedId:
@Entitypublic class Book { @EmbeddedId private BookId bookId; // constructors, other fields, getters and setters}
@IdClass vs @ EmbeddedId
Como acabamos de ver, la diferencia en la superficie entre estos dos es que con @IdClass, tuvimos que especificar las columnas dos veces, una en accountId y otra en Account. Pero, con @EmbeddedId no lo hicimos.
Sin embargo, hay algunas otras compensaciones.
Por ejemplo, estas diferentes estructuras afectan a las consultas JPQL que escribimos.
Por ejemplo, con @IdClass, la consulta es un poco más simple:
SELECT account.accountNumber FROM Account account
Con @EmbeddedId, tenemos que hacer un recorrido adicional:
SELECT book.bookId.title FROM Book book
Además, @IdClass puede ser bastante útil en lugares donde estamos usando una clase de clave compuesta que no podemos modificar.
Finalmente, si vamos a acceder a partes de la clave compuesta individualmente, podemos hacer uso de @IdClass, pero en lugares donde usamos con frecuencia el identificador completo como objeto, se prefiere @EmbeddedId.
Conclusión
En este artículo rápido, exploramos las claves primarias compuestas en JPA.
Como siempre, el código completo de este artículo se puede encontrar en Github.