web-dev-qa-db-fra.com

Hibernation de la relation plusieurs-à-un sans clé primaire ni table de jointure

Problème

Je voudrais commencer par dire que je me rends compte que la structure de la base de données est atroce, mais je ne peux pas la changer pour le moment.

Cela étant dit, j'ai le besoin de créer une relation un-à-plusieurs et bidirectionnelle dans Hibernate (4.2.1) qui n'implique aucune clé primaire (seulement une clé unique du côté "parent" de la relation) et aucune joindre des tables. La clé étrangère représentant cette relation est un pointeur de "l'enfant" au "parent" (voir ci-dessous). J'ai cherché et essayé différentes configurations d'annotations différentes sans succès. Est-ce que ce que je demande est possible?

Base de données

GLOBAL_PART

CREATE TABLE "GLOBAL_PART" (    
    "GLOBAL_PART_ID" NUMBER NOT NULL,
    "RELEASES" NUMBER,
    CONSTRAINT "GLOBAL_PART_PK" PRIMARY KEY ("GLOBAL_PART_ID"),
    CONSTRAINT "GLOBAL_PART_RELEASES_UK" UNIQUE ("RELEASES")
);

PART_RELEASE

CREATE TABLE "PART_RELEASE" (
    "PART_RELEASE_ID" NUMBER NOT NULL,
    "COLLECTION_ID" NUMBER,
    CONSTRAINT "PART_RELEASE_PK" PRIMARY KEY ("PART_RELEASE_ID"),
    CONSTRAINT "GLOBAL_PART_RELEASE_FK" FOREIGN KEY ("COLLECTION_ID")
        REFERENCES "GLOBAL_PART" ("RELEASES") ON DELETE CASCADE ENABLE
);

Référence:

PART_RELEASE                 GLOBAL_PART
-------------------          ------------
PART_RELEASE_ID (PK)         GLOBAL_PART_ID (PK)
COLLECTION_ID -------------> RELEASES (UK)

Java

GlobalPart.Java

@Entity
@Table(name = "GLOBAL_PART")
@SequenceGenerator(name = "SEQUENCE_GENERATOR", sequenceName = "GLOBAL_PART_SEQ")
public class GlobalPart extends ModelBase implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "GLOBAL_PART_ID", unique = true, nullable = false)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQUENCE_GENERATOR")
    private Long globalPartId;

    @OneToMany(fetch = FetchType.EAGER)
    @JoinColumn(name = "COLLECTIONGID")
    private Set<PartRelease> releases;

    public Long getGlobalPartId() {
        return globalPartId;
    }

    public void setGlobalPartId(Long globalPartId) {
        this.globalPartId = globalPartId;
    }

    public Set<PartRelease> getReleases() {
        return releases;
    }

    public void setReleases(Set<PartRelease> releases) {
        this.releases = releases;
    }

}

PartRelease.Java

@Entity
@Table(name = "PART_RELEASE")
@SequenceGenerator(name = "SEQUENCE_GENERATOR", sequenceName = "PART_RELEASE_SEQ")
public class PartRelease extends ModelBase implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "PART_RELEASE_ID", unique = true, nullable = false)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQUENCE_GENERATOR")
    private String partReleaseId;

    @ManyToOne
    @JoinColumn(name = "RELEASES")
    private GlobalPart globalPart;

    public String getPartReleaseId() {
        return partReleaseId;
    }

    public void setPartReleaseId(String partReleaseId) {
        this.partReleaseId = partReleaseId;
    }

    public GlobalPart getGlobalPart() {
        return globalPart;
    }

    public void setGlobalPart(GlobalPart globalPart) {
        this.globalPart = globalPart;
    }

}

Toute aide serait grandement appréciée !!

16
chrismborland

Tout d'abord, dans une association bidirectionnelle, il y a toujours un côté propriétaire, qui définit le mappage, et un côté inverse, marqué par la présence de l'attribut mappedBy.

Dans une association OneToMany, le côté propriétaire est toujours le côté multiple (PartRelease dans votre cas).

De plus, une colonne de jointure, par défaut, fait référence à l'ID de l'entité qu'elle référence. Comme ce n'est pas ce que vous voulez, vous devez spécifier le nom de la colonne référencée.

Et bien sûr, la colonne RELEASES doit être mappée:

public class GlobalPart extends ModelBase implements Serializable {

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "globalPart")
    private Set<PartRelease> partReleases;

    @Column(name = "RELEASES")
    private Long releasesNumber;
}

public class PartRelease extends ModelBase implements Serializable {
    @ManyToOne
    @JoinColumn(name = "COLLECTION_ID", referencedColumnName = "RELEASES")
    private GlobalPart globalPart;

}

Les associations sont bien décrites dans la documentation . Tu devrais le lire.

33
JB Nizet

Comme expliqué dans cet article , chaque fois que vous devez mapper un @ManyToOne en utilisant une colonne de clé non primaire du côté parent, vous devez utiliser l'attribut referencedColumnName de @JoinColumn annotation.

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(
    name = "RELEASES", 
    referencedColumnName = "COLLECTIONGID"
)

J'ai utilisé FetchType.LAZY pour le @ManyToOne car par défaut, la récupération EAGER est utilisée, ce qui est très mauvais pour les performances .

2
Vlad Mihalcea