web-dev-qa-db-fra.com

Hibernate: Où sont insérables = faux, actualisables = faux dans les constellations de clés primaires composites impliquant des clés étrangères?

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):

  1. Dans l'annotation @Column de la classe PK composite (classes @Embeddable uniquement) ou
  2. Dans l'association de classe d'entité @ annotation JoinColumn/s ou
  3. Dans la classe d'entité ' redondante annotation @Column de la propriété PK (classes @IdClass uniquement)

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"?

32
Kawu

Permettez-moi de répondre étape par étape.

1. Quand avez-vous besoin de `insertable = false, updatable = false`?

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.

2. Pourquoi hibernate se plaint-il de votre cartographie?

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.

3. Comment le réparer dans votre cas?

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.

68
ManuPK

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.

0
Darshan