In welchem ​​Fall verwenden Sie die Annotation JPA @JoinTable?

Antworten:

350

EDIT 2017-04-29 : Wie von einigen Kommentatoren erwähnt, benötigt das JoinTableBeispiel das mappedByAnnotation-Attribut nicht. Tatsächlich weigern sich neuere Versionen von Hibernate, den folgenden Fehler zu drucken:

org.hibernate.AnnotationException: 
   Associations marked as mappedBy must not define database mappings 
   like @JoinTable or @JoinColumn

Stellen wir uns vor, Sie haben eine Entität mit dem Namen Projectund eine andere Entität mit dem Namen Taskund jedes Projekt kann viele Aufgaben haben.

Sie können das Datenbankschema für dieses Szenario auf zwei Arten entwerfen.

Die erste Lösung besteht darin, eine Tabelle mit dem Namen Projectund eine andere Tabelle mit dem Namen zu erstellen und Taskder Aufgabentabelle mit dem Namen project_id: eine Fremdschlüsselspalte hinzuzufügen.

Project      Task
-------      ----
id           id
name         name
             project_id

Auf diese Weise kann das Projekt für jede Zeile in der Aufgabentabelle ermittelt werden. Wenn Sie diesen Ansatz verwenden, benötigen Sie in Ihren Entitätsklassen keine Join-Tabelle:

@Entity
public class Project {

   @OneToMany(mappedBy = "project")
   private Collection<Task> tasks;

}

@Entity
public class Task {

   @ManyToOne
   private Project project;

}

Die andere Lösung besteht darin, eine dritte Tabelle zu verwenden, z. B. Project_Tasksdie Beziehung zwischen Projekten und Aufgaben in dieser Tabelle zu speichern:

Project      Task      Project_Tasks
-------      ----      -------------
id           id        project_id
name         name      task_id

Die Project_TasksTabelle wird als "Join-Tabelle" bezeichnet. Um diese zweite Lösung in JPA zu implementieren, müssen Sie die @JoinTableAnmerkung verwenden. Um beispielsweise eine unidirektionale Eins-zu-Viele-Zuordnung zu implementieren, können wir unsere Entitäten als solche definieren:

Project Entität:

@Entity
public class Project {

    @Id
    @GeneratedValue
    private Long pid;

    private String name;

    @JoinTable
    @OneToMany
    private List<Task> tasks;

    public Long getPid() {
        return pid;
    }

    public void setPid(Long pid) {
        this.pid = pid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Task> getTasks() {
        return tasks;
    }

    public void setTasks(List<Task> tasks) {
        this.tasks = tasks;
    }
}

Task Entität:

@Entity
public class Task {

    @Id
    @GeneratedValue
    private Long tid;

    private String name;

    public Long getTid() {
        return tid;
    }

    public void setTid(Long tid) {
        this.tid = tid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

Dadurch wird die folgende Datenbankstruktur erstellt:

ER-Diagramm 1

Mit der @JoinTableAnmerkung können Sie auch verschiedene Aspekte der Verknüpfungstabelle anpassen. Hatten wir die tasksEigenschaft beispielsweise folgendermaßen kommentiert :

@JoinTable(
        name = "MY_JT",
        joinColumns = @JoinColumn(
                name = "PROJ_ID",
                referencedColumnName = "PID"
        ),
        inverseJoinColumns = @JoinColumn(
                name = "TASK_ID",
                referencedColumnName = "TID"
        )
)
@OneToMany
private List<Task> tasks;

Die resultierende Datenbank wäre geworden:

ER-Diagramm 2

Wenn Sie ein Schema für eine Viele-zu-Viele-Zuordnung erstellen möchten, ist die Verwendung einer Verknüpfungstabelle die einzige verfügbare Lösung.

Behrang Saeedzadeh
quelle
1
Mit dem ersten Ansatz habe ich mein Projekt mit meinen Aufgaben und jede Aufgabe mit dem übergeordneten Projekt vor dem Zusammenführen gefüllt und funktioniert, aber alle meine Einträge werden basierend auf der Anzahl meiner Aufgaben dupliziert. Ein Projekt mit zwei Aufgaben wird zweimal in meiner Datenbank gespeichert. Warum ?
MaikoID
UPDATE Es gibt keine doppelten Einträge in meiner Datenbank, der Ruhezustand wählt mit dem linken äußeren Join und ich weiß nicht warum ..
MaikoID
2
Ich glaube, @JoinTable/@JoinColumnkann auf dem gleichen Feld mit kommentiert werden mappedBy. Das richtige Beispiel sollte also sein, das mappedByIn beizubehalten Projectund das @JoinColumnzu Task.project (oder umgekehrt) zu verschieben
Adrian Shum
2
Nett! Aber ich eine weitere Frage: wenn die Tabelle verbindet Project_Tasksdie Bedürfnisse namevon Taskals auch, was drei Spalten wird: project_id, task_id, task_name, wie dies zu erreichen?
Macemers
5
Ich denke, Sie sollten nicht auf Ihr zweites Verwendungsbeispiel abgebildet haben, um diesen Fehler zu verhindernCaused by: org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn:
karthik m
14

Es ist auch sauberer zu verwenden, @JoinTablewenn eine Entität das Kind in mehreren Eltern-Kind-Beziehungen mit verschiedenen Elterntypen sein kann. Stellen Sie sich vor, eine Aufgabe kann das Kind von Projekt, Person, Abteilung, Studie und Prozess sein, um Behrangs Beispiel zu verfolgen.

Sollte die taskTabelle 5 nullableFremdschlüsselfelder enthalten? Ich denke nicht...

HDave
quelle
14

Dies ist die einzige Lösung zum Zuordnen einer ManyToMany-Zuordnung: Sie benötigen eine Verknüpfungstabelle zwischen den beiden Entitätstabellen, um die Zuordnung zuzuordnen.

Es wird auch für OneToMany-Zuordnungen (normalerweise unidirektional) verwendet, wenn Sie keinen Fremdschlüssel in die Tabelle der vielen Seiten einfügen und ihn somit unabhängig von der einen Seite halten möchten.

Suchen Sie in der Dokumentation zum Ruhezustand nach @JoinTable, um Erklärungen und Beispiele zu erhalten.

JB Nizet
quelle
4

Damit können Sie mit vielen zu vielen Beziehungen umgehen. Beispiel:

Table 1: post

post has following columns
____________________
|  ID     |  DATE   |
|_________|_________|
|         |         |
|_________|_________|

Table 2: user

user has the following columns:

____________________
|     ID  |NAME     |
|_________|_________|
|         |         |
|_________|_________|

Mit Join Table können Sie eine Zuordnung erstellen, indem Sie:

@JoinTable(
  name="USER_POST",
  joinColumns=@JoinColumn(name="USER_ID", referencedColumnName="ID"),
  inverseJoinColumns=@JoinColumn(name="POST_ID", referencedColumnName="ID"))

erstellt eine Tabelle:

____________________
|  USER_ID| POST_ID |
|_________|_________|
|         |         |
|_________|_________|
Slal
quelle
1
Frage: Was ist, wenn ich bereits diese zusätzliche Tabelle habe? Die JoinTable überschreibt nicht die vorhandene, oder?
TheWandererr
@TheWandererr hast du die Antwort auf deine Frage herausgefunden? Ich habe bereits eine Join-Tabelle
Asgs
In meinem Fall wird eine redundante Spalte in der besitzenden Beistelltabelle erstellt. für zB. POST_ID im POST. Können Sie vorschlagen, warum es passiert?
SPS
0

@ManyToMany Verbände

In den meisten Fällen müssen Sie @JoinTableAnmerkungen verwenden, um die Zuordnung einer Viele-zu-Viele-Tabellenbeziehung anzugeben:

  • den Namen der Verknüpfungstabelle und
  • die beiden Fremdschlüsselspalten

Angenommen, Sie haben die folgenden Datenbanktabellen:

Viele-zu-viele-Tabellenbeziehung

In der PostEntität würden Sie diese Beziehung folgendermaßen zuordnen:

@ManyToMany(cascade = {
    CascadeType.PERSIST,
    CascadeType.MERGE
})
@JoinTable(
    name = "post_tag",
    joinColumns = @JoinColumn(name = "post_id"),
    inverseJoinColumns = @JoinColumn(name = "tag_id")
)
private List<Tag> tags = new ArrayList<>();

Die @JoinTableAnmerkung wird verwendet, um den Tabellennamen über das nameAttribut sowie die Fremdschlüsselspalte anzugeben, die auf die Tabelle verweist post(z. B. joinColumns), und die Fremdschlüsselspalte in der post_tagVerknüpfungstabelle, die Tagüber das inverseJoinColumnsAttribut auf die Entität verweist .

Beachten Sie, dass das Kaskadenattribut der @ManyToManyAnnotation auf PERSISTund MERGEnur gesetzt ist, weil Kaskadierung REMOVEeine schlechte Idee ist, da die DELETE-Anweisung für den anderen übergeordneten Datensatz ausgegeben wird, tagin unserem Fall nicht für den post_tagDatensatz. Weitere Informationen zu diesem Thema finden Sie in diesem Artikel .

Unidirektionale @OneToManyAssoziationen

Die unidirektionalen @OneToManyAssoziationen, denen eine @JoinColumnZuordnung fehlt , verhalten sich eher wie viele-zu-viele-Tabellenbeziehungen als wie eins-zu-viele.

Angenommen, Sie haben die folgenden Entitätszuordnungen:

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    @OneToMany(
        cascade = CascadeType.ALL,
        orphanRemoval = true
    )
    private List<PostComment> comments = new ArrayList<>();

    //Constructors, getters and setters removed for brevity
}

@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {

    @Id
    @GeneratedValue
    private Long id;

    private String review;

    //Constructors, getters and setters removed for brevity
}

Der Ruhezustand übernimmt das folgende Datenbankschema für die obige Entitätszuordnung:

Unidirektionale <code> @OneToMany </ code> JPA-Zuordnungsdatenbanktabellen

Wie bereits erläutert, @OneToManyverhält sich die unidirektionale JPA-Zuordnung wie eine Viele-zu-Viele-Zuordnung.

Zum Anpassen der Verknüpfungstabelle können Sie auch die @JoinTableAnmerkung verwenden:

@OneToMany(
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
@JoinTable(
    name = "post_comment_ref",
    joinColumns = @JoinColumn(name = "post_id"),
    inverseJoinColumns = @JoinColumn(name = "post_comment_id")
)
private List<PostComment> comments = new ArrayList<>();

Und jetzt wird die Verknüpfungstabelle aufgerufen post_comment_refund die Fremdschlüsselspalten werden post_idfür die postTabelle und post_comment_idfür die post_commentTabelle angezeigt.

Unidirektionale @OneToManyAssoziationen sind nicht effizient, daher ist es besser, bidirektionale @OneToManyAssoziationen oder nur die @ManyToOneSeite zu verwenden. In diesem Artikel finden Sie weitere Informationen zu diesem Thema.

Vlad Mihalcea
quelle