Comment mapper une clé composite avec Hibernate?

Dans ce code, comment générer une classe Java pour la clé composite (comment composer une clé en mode veille prolongée):

create table Time ( levelStation int(15) not null, src varchar(100) not null, dst varchar(100) not null, distance int(15) not null, price int(15) not null, confPathID int(15) not null, constraint ConfPath_fk foreign key(confPathID) references ConfPath(confPathID), primary key (levelStation, confPathID) )ENGINE=InnoDB DEFAULT CHARSET=utf8; 

Pour mapper une clé composite, vous pouvez utiliser les annotations EmbeddedId ou IdClass . Je sais que cette question ne concerne pas ssortingctement JPA mais les règles définies par la spécification s’appliquent également. Alors les voici:

2.1.4 Clés primaires et identité d’entité

Une clé primaire composite doit correspondre à un seul champ persistant ou à une propriété ou à un ensemble de champs ou de propriétés tels que décrits ci-dessous. Une classe de clé primaire doit être définie pour représenter une clé primaire composite. Les clés primaires composites apparaissent généralement lors du mappage à partir de bases de données héritées lorsque la clé de firebase database est composée de plusieurs colonnes. Les annotations EmbeddedId et IdClass sont utilisées pour désigner les clés primaires composites. Voir les sections 9.1.14 et 9.1.15.

Les règles suivantes s’appliquent aux clés primaires composites:

  • La classe de clé primaire doit être publique et doit avoir un constructeur public no-arg.
  • Si l’access basé sur la propriété est utilisé, les propriétés de la classe de clé primaire doivent être publiques ou protégées.
  • La classe de clé primaire doit être serializable .
  • La classe de clé primaire doit définir des méthodes equals et hashCode . La sémantique de l’égalité des valeurs pour ces méthodes doit être cohérente avec l’égalité de la firebase database pour les types de firebase database sur lesquels la clé est mappée.
  • Une clé primaire composite doit être représentée et mappée en tant que classe incorporable (voir Section 9.1.14, «Annotation EmbeddedId») ou doit être représentée et mappée sur plusieurs champs ou propriétés de la classe d’entité (voir Section 9.1.15, «IdClass Annotation”).
  • Si la classe de clé primaire composite est mappée à plusieurs champs ou propriétés de la classe d’entité, les noms des champs de clé primaire ou des propriétés de la classe de clé primaire et ceux de la classe d’entité doivent correspondre et leurs types doivent être identiques.

Avec un IdClass

La classe de la clé primaire composite pourrait ressembler à (pourrait être une classe interne statique):

 public class TimePK implements Serializable { protected Integer levelStation; protected Integer confPathID; public TimePK() {} public TimePK(Integer levelStation, Integer confPathID) { this.levelStation = levelStation; this.confPathID = confPathID; } // equals, hashCode } 

Et l’entité:

 @Entity @IdClass(TimePK.class) class Time implements Serializable { @Id private Integer levelStation; @Id private Integer confPathID; private Ssortingng src; private Ssortingng dst; private Integer distance; private Integer price; // getters, setters } 

L’annotation IdClass mappe plusieurs champs sur la table PK.

Avec EmbeddedId

La classe de la clé primaire composite pourrait ressembler à (pourrait être une classe interne statique):

 @Embeddable public class TimePK implements Serializable { protected Integer levelStation; protected Integer confPathID; public TimePK() {} public TimePK(Integer levelStation, Integer confPathID) { this.levelStation = levelStation; this.confPathID = confPathID; } // equals, hashCode } 

Et l’entité:

 @Entity class Time implements Serializable { @EmbeddedId private TimePK timePK; private Ssortingng src; private Ssortingng dst; private Integer distance; private Integer price; //... } 

L’annotation @EmbeddedId mappe une classe PK à la table PK.

Différences:

  • Du sharepoint vue du modèle physique, il n’y a pas de différences
  • @EmbeddedId communique plus clairement que la clé est une clé composite et que IMO a du sens lorsque le pk combiné est une entité significative ou réutilisée dans votre code .
  • @IdClass est utile pour spécifier que certaines combinaisons de champs sont uniques, mais que celles-ci n’ont pas de signification particulière .

Ils affectent également la manière dont vous écrivez les requêtes (les rendant plus ou moins verbeuses):

  • avec IdClass

     select t.levelStation from Time t 
  • avec EmbeddedId

     select t.timePK.levelStation from Time t 

Les références

  • Spécification JPA 1.0
    • Section 2.1.4 “Clés primaires et identité d’entité”
    • Section 9.1.14 “Annotation EmbeddedId”
    • Section 9.1.15 “Annotation IdClass”

Vous devez utiliser @EmbeddedId :

 @Entity class Time { @EmbeddedId TimeId id; Ssortingng src; Ssortingng dst; Integer distance; Integer price; } @Embeddable class TimeId implements Serializable { Integer levelStation; Integer confPathID; } 

Comme je l’ai expliqué dans cet article , en supposant que vous ayez les tables de firebase database suivantes:

entrer la description de l'image ici

Tout d’abord, vous devez créer le @Embeddable contenant l’identifiant composite:

 @Embeddable public class EmployeeId implements Serializable { @Column(name = "company_id") private Long companyId; @Column(name = "employee_number") private Long employeeNumber; public EmployeeId() { } public EmployeeId(Long companyId, Long employeeId) { this.companyId = companyId; this.employeeNumber = employeeId; } public Long getCompanyId() { return companyId; } public Long getEmployeeNumber() { return employeeNumber; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof EmployeeId)) return false; EmployeeId that = (EmployeeId) o; return Objects.equals(getCompanyId(), that.getCompanyId()) && Objects.equals(getEmployeeNumber(), that.getEmployeeNumber()); } @Override public int hashCode() { return Objects.hash(getCompanyId(), getEmployeeNumber()); } } 

Avec ceci en place, nous pouvons mapper l’entité Employee qui utilise l’identificateur composite en l’annotant avec @EmbeddedId :

 @Entity(name = "Employee") @Table(name = "employee") public class Employee { @EmbeddedId private EmployeeId id; private Ssortingng name; public EmployeeId getId() { return id; } public void setId(EmployeeId id) { this.id = id; } public Ssortingng getName() { return name; } public void setName(Ssortingng name) { this.name = name; } } 

L’entité Phone qui possède une association @ManyToOne à Employee doit référencer l’identificateur composite de la classe parent via deux mappages @JoinColumn :

 @Entity(name = "Phone") @Table(name = "phone") public class Phone { @Id @Column(name = "`number`") private Ssortingng number; @ManyToOne @JoinColumns({ @JoinColumn( name = "company_id", referencedColumnName = "company_id"), @JoinColumn( name = "employee_number", referencedColumnName = "employee_number") }) private Employee employee; public Employee getEmployee() { return employee; } public void setEmployee(Employee employee) { this.employee = employee; } public Ssortingng getNumber() { return number; } public void setNumber(Ssortingng number) { this.number = number; } } 

Pour plus de détails, consultez cet article .

On dirait que vous le faites à partir de rien. Essayez d’utiliser les outils d’ingénierie inverse disponibles, tels que les entités Netbeans de la firebase database, pour au moins automatiser les bases (comme les identifiants intégrés). Cela peut devenir un énorme casse-tête si vous avez beaucoup de tables. Je suggère d’éviter de réinventer la roue et d’utiliser autant d’outils que possible pour réduire le codage à la partie minimale et la plus importante, ce que vous avez l’intention de faire.

La classe de clé primaire doit définir des méthodes égales et hashCode

  1. Lorsque vous implémentez est égal à, vous devez utiliser instanceof pour permettre la comparaison avec les sous-classes. Si Hibernate paresseux charge un à un ou plusieurs pour une relation, vous aurez un proxy pour la classe au lieu de la classe plain. Un proxy est une sous-classe. La comparaison des noms de classe échouerait.
    Plus techniquement: vous devez suivre le principe de substitution de Liskows et ignorer la symésortinge.
  2. Le prochain piège est d’utiliser quelque chose comme name.equals (that.name) au lieu de name.equals (that.getName ()) . Le premier échouera, s’il s’agit d’un proxy.

http://www.laliluna.de/jpa-hibernate-guide/ch06s06.html

Prenons un exemple simple. Disons que deux tables nommées test et customer sont décrites comme suit:

 create table test( test_id int(11) not null auto_increment, primary key(test_id)); create table customer( customer_id int(11) not null auto_increment, name varchar(50) not null, primary key(customer_id)); 

Une autre table est là pour garder la trace des test et des customer :

 create table tests_purchased( customer_id int(11) not null, test_id int(11) not null, created_date datetime not null, primary key(customer_id, test_id)); 

Nous pouvons voir que dans la table tests_purchased la clé primaire est une clé composite, nous allons donc utiliser la ... dans le fichier de mappage hbm.xml . Donc, le PurchasedTest.hbm.xml ressemblera à ceci:

 < ?xml version="1.0"?> < !DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">            

Mais ça ne s’arrête pas là. Dans Hibernate, nous utilisons session.load ( entityClass , id_type_object ) pour rechercher et charger l’entité à l’aide de la clé primaire. Dans le cas de clés composites, l’object ID doit être une classe d’ID distincte (dans le cas ci-dessus, une classe PurchasedTestId ) qui déclare simplement les atsortingbuts de clé primaire comme cidessous :

 import java.io.Serializable; public class PurchasedTestId implements Serializable { private Long testId; private Long customerId; // an easy initializing constructor public PurchasedTestId(Long testId, Long customerId) { this.testId = testId; this.customerId = customerId; } public Long getTestId() { return testId; } public void setTestId(Long testId) { this.testId = testId; } public Long getCustomerId() { return customerId; } public void setCustomerId(Long customerId) { this.customerId = customerId; } @Override public boolean equals(Object arg0) { if(arg0 == null) return false; if(!(arg0 instanceof PurchasedTestId)) return false; PurchasedTestId arg1 = (PurchasedTestId) arg0; return (this.testId.longValue() == arg1.getTestId().longValue()) && (this.customerId.longValue() == arg1.getCustomerId().longValue()); } @Override public int hashCode() { int hsCode; hsCode = testId.hashCode(); hsCode = 19 * hsCode+ customerId.hashCode(); return hsCode; } } 

Le point important est que nous implémentons également les deux fonctions hashCode() et equals() car Hibernate les utilise.

Une autre option consiste à mapper en tant que carte d’éléments composites dans la table ConfPath.

Ce mappage bénéficierait d’un index sur (ConfPathID, levelStation).

 public class ConfPath { private Map timeForLevelStation = new HashMap(); public Time getTime(long levelStation) { return timeForLevelStation.get(levelStation); } public void putTime(long levelStation, Time newValue) { timeForLevelStation.put(levelStation, newValue); } } public class Time { Ssortingng src; Ssortingng dst; long distance; long price; public long getDistance() { return distance; } public void setDistance(long distance) { this.distance = distance; } public Ssortingng getDst() { return dst; } public void setDst(Ssortingng dst) { this.dst = dst; } public long getPrice() { return price; } public void setPrice(long price) { this.price = price; } public Ssortingng getSrc() { return src; } public void setSrc(Ssortingng src) { this.src = src; } } 

Cartographie:

                

Utiliser hbm.xml

       

Utiliser l’annotation

Classe de clé composite

 public class PK implements Serializable{ private int PRODUCT_Product_ID ; private int categories_id ; public PK(int productId, int categoryId) { this.PRODUCT_Product_ID = productId; this.categories_id = categoryId; } public int getPRODUCT_Product_ID() { return PRODUCT_Product_ID; } public void setPRODUCT_Product_ID(int PRODUCT_Product_ID) { this.PRODUCT_Product_ID = PRODUCT_Product_ID; } public int getCategories_id() { return categories_id; } public void setCategories_id(int categories_id) { this.categories_id = categories_id; } private PK() { } @Override public boolean equals(Object o) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } PK pk = (PK) o; return Objects.equals(PRODUCT_Product_ID, pk.PRODUCT_Product_ID ) && Objects.equals(categories_id, pk.categories_id ); } @Override public int hashCode() { return Objects.hash(PRODUCT_Product_ID, categories_id ); } } 

Classe d’entité

 @Entity(name = "product_category") @IdClass( PK.class ) public class ProductCategory implements Serializable { @Id private int PRODUCT_Product_ID ; @Id private int categories_id ; public ProductCategory(int productId, int categoryId) { this.PRODUCT_Product_ID = productId ; this.categories_id = categoryId; } public ProductCategory() { } public int getPRODUCT_Product_ID() { return PRODUCT_Product_ID; } public void setPRODUCT_Product_ID(int PRODUCT_Product_ID) { this.PRODUCT_Product_ID = PRODUCT_Product_ID; } public int getCategories_id() { return categories_id; } public void setCategories_id(int categories_id) { this.categories_id = categories_id; } public void setId(PK id) { this.PRODUCT_Product_ID = id.getPRODUCT_Product_ID(); this.categories_id = id.getCategories_id(); } public PK getId() { return new PK( PRODUCT_Product_ID, categories_id ); } }