Lors de l'implémentation de clés primaires composites dans Hibernate ou d'autres ORM, il y a jusqu'à trois endroits où placer les insertable = false, updatable = false dans les constellations de clés primaires composites qui utilisent des relations d'identification (FK qui font partie du PK):
Le troisième est le seul moyen de le faire avec @IdClass et JPA 1.0 AFAIK. Voir http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#Primary_Keys_through_OneToOne_Relationships . Je ne considérerai que les cas 1. et 2.
Q: De quelle manière est l'endroit préféré pour mettre "insertable = false, updatable = false" en général?
J'ai rencontré des problèmes avec Hibernate concernant cette question. Par exemple, Hibernate 3.5.x se plaindra de la table Zips
CREATE TABLE Zips
(
country_code CHAR(2),
code VARCHAR(10),
PRIMARY KEY (country_code, code),
FOREIGN KEY (country_code) REFERENCES Countries (iso_code)
)
avec:
org.hibernate.MappingException: Repeated column in mapping for entity: com.kawoolutions.bbstats.model.Zip column: country_code (should be mapped with insert="false" update="false")
org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.Java:676)
org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.Java:698)
...
Comme vous pouvez le voir, la colonne country_code est à la fois PK et FK. Voici ses classes:
Classe d'entité:
@Entity
@Table(name = "Zips")
public class Zip implements Serializable
{
@EmbeddedId
private ZipId id;
@ManyToOne
@JoinColumn(name = "country_code", referencedColumnName = "iso_code")
private Country country = null;
...
}
Classe PK composite:
@Embeddable
public class ZipId implements Serializable
{
@Column(name = "country_code", insertable = false, updatable = false)
private String countryCode;
@Column(name = "code")
private String code;
...
}
Lorsque vous placez insertable = false, updatable = false dans @JoinColumn de l'association de classe d'entité, toutes les exceptions disparaissent et tout fonctionne correctement. Cependant, je ne vois pas pourquoi le code ci-dessus ne devrait pas fonctionner. Il se peut qu'Hibernate ait des problèmes avec cela. Le bogue décrit est-il un Hibernate, car il ne semble pas évaluer @Column "insertable = false, updatable = false"?
En substance, quelle est la méthode JPA standard, la meilleure pratique ou la préférence où mettre "insertable = false, updatable = false"?
Permettez-moi de répondre étape par étape.
Regardons la cartographie ci-dessous,
public class Zip {
@ManyToOne
@JoinColumn(name = "country_code", referencedColumnName = "iso_code")
private Country country = null
@Column(name = "country_code")
private String countryCode;
}
Ici, nous faisons référence à la même colonne dans le tableau en utilisant deux propriétés différentes. Dans le code ci-dessous,
Zip z = new Zip();
z.setCountry(getCountry("US"));
z.setCountryCode("IN");
saveZip(z);
Que fera hibernation ici ??
Pour éviter ce type d'incohérence, hibernate vous demande de spécifier le point de mise à jour des navires de relation. Ce qui signifie vous pouvez vous référer à la même colonne dans le tableau n
nombre de fois mais une seule d'entre elles peut être utilisée pour la mise à jour et toutes les autres seront en lecture seule.
Dans votre classe Zip
, vous faites référence à la classe d'ID incorporé ZipId
qui contient à nouveau le code du pays. Comme dans le scénario ci-dessus, vous avez maintenant la possibilité de mettre à jour le counry_code
colonne à deux endroits. Par conséquent, l'erreur donnée par hibernate est appropriée.
Non. Idéalement, vous voulez que votre classe ZipId
génère l'identifiant, vous ne devez donc pas ajouter insertable = false, updatable = false
au countryCode dans le ZipId
. Donc, le correctif est comme ci-dessous, modifiez le mappage country
dans votre classe Zip
comme ci-dessous,
@ManyToOne
@JoinColumn(name = "country_code", referencedColumnName = "iso_code",
insertable = false, updatable = false)
private Country country;
J'espère que cela aide votre compréhension.
Vous pouvez également résoudre ce problème en utilisant @PrimaryKeyJoinColumn
annotation. L'annotation PrimaryKeyJoinColumn spécifie une colonne de clé primaire qui est utilisée comme clé étrangère pour se joindre à une autre table.
L'annotation PrimaryKeyJoinColumn est utilisée pour joindre la table primaire d'une sous-classe d'entité dans la stratégie de mappage JOINED à la table primaire de sa superclasse; il est utilisé dans une annotation SecondaryTable pour joindre une table secondaire à une table primaire; et il peut être utilisé dans un mappage OneToOne dans lequel la clé primaire de l'entité de référence est utilisée comme clé étrangère pour l'entité référencée. Si aucune annotation PrimaryKeyJoinColumn n'est spécifiée pour une sous-classe dans la stratégie de mappage JOINED, les colonnes de clé étrangère sont supposées avoir les mêmes noms que les colonnes de clé primaire de la table primaire de la superclasse.