W jakim przypadku używasz adnotacji JPA @JoinTable?

Odpowiedzi:

351

EDYCJA 2017-04-29 : Jak zauważyli niektórzy komentatorzy, JoinTableprzykład nie potrzebuje mappedByatrybutu adnotacji. W rzeczywistości najnowsze wersje Hibernacji odmawiają uruchomienia, wyświetlając następujący błąd:

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

Załóżmy, że masz jednostkę o nazwie Projecti inną o nazwie, Taska każdy projekt może mieć wiele zadań.

Schemat bazy danych można zaprojektować dla tego scenariusza na dwa sposoby.

Pierwszym rozwiązaniem jest utworzenie tabeli o nazwie Projecti innej tabeli o nazwie Taski dodanie kolumny klucza obcego do tabeli zadań o nazwie project_id:

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

W ten sposób będzie można określić projekt dla każdego wiersza w tabeli zadań. Jeśli używasz tego podejścia, w klasach encji nie będziesz potrzebować tabeli łączenia:

@Entity
public class Project {

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

}

@Entity
public class Task {

   @ManyToOne
   private Project project;

}

Innym rozwiązaniem jest użycie np. Trzeciej tabeli Project_Tasksi zapisanie relacji między projektami i zadaniami w tej tabeli:

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

Project_TasksTabela nazywa się „Join Table”. Aby wdrożyć to drugie rozwiązanie w JPA, musisz użyć @JoinTableadnotacji. Na przykład, aby zaimplementować jednokierunkowe powiązanie jeden do wielu, możemy zdefiniować nasze jednostki jako takie:

Project jednostka:

@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 jednostka:

@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;
    }

}

Spowoduje to utworzenie następującej struktury bazy danych:

Schemat ER 1

@JoinTableAdnotacji pozwala również dostosować różne aspekty tabeli sprzężenia. Na przykład, gdybyśmy opisali taskswłaściwość w ten sposób:

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

Powstała baza danych stałaby się:

Schemat ER 2

Wreszcie, jeśli chcesz utworzyć schemat dla asocjacji wiele do wielu, użycie tabeli łączenia jest jedynym dostępnym rozwiązaniem.

Behrang Saeedzadeh
źródło
1
używając pierwszego podejścia, mój projekt został wypełniony zadaniami, a każde zadanie zostało wypełnione projektem nadrzędnym przed scaleniem i działa, ale wszystkie moje wpisy są powielane na podstawie liczby moich zadań. Projekt z dwoma zadaniami jest zapisywany dwukrotnie w mojej bazie danych. Czemu ?
MaikoID
AKTUALIZACJA W mojej bazie danych nie ma zduplikowanych wpisów, hibernacja wybiera się lewym
złączem
2
Uważam, że @JoinTable/@JoinColumnw tym samym polu można umieścić adnotację mappedBy. Tak więc poprawnym przykładem powinno być trzymanie mappedByw Projectśrodku i przejście @JoinColumndo Task.project (lub odwrotnie)
Adrian Shum
2
Miły! Ale mam kolejne pytanie: czy dołączyć tabeli Project_Taskspotrzebuje nameod Taska także, co staje się trzy kolumny: project_id, task_id, task_name, jak to osiągnąć?
macemers
5
Myślę, że nie powinieneś był mapować na drugim przykładzie użycia, aby zapobiec temu błędowiCaused by: org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn:
karthik m
14

Jest również bardziej przejrzysty w użyciu, @JoinTablegdy jednostka może być dzieckiem w kilku relacjach rodzic / dziecko z różnymi typami rodziców. Kontynuując przykład Behranga, wyobraź sobie, że zadanie może być dzieckiem projektu, osoby, działu, badania i procesu.

Czy tasktabela powinna mieć 5 nullablepól kluczy obcych? Myślę, że nie...

HDave
źródło
14

Jest to jedyne rozwiązanie umożliwiające mapowanie asocjacji ManyToMany: do mapowania powiązania potrzebna jest tabela łączenia między dwiema tabelami encji.

Jest również używany do asocjacji OneToMany (zwykle jednokierunkowych), gdy nie chcesz dodawać klucza obcego w tabeli wielu stron, a tym samym utrzymywać go niezależnie od jednej strony.

Wyszukaj @JoinTable w dokumentacji hibernacji, aby znaleźć wyjaśnienia i przykłady.

JB Nizet
źródło
4

To pozwala ci radzić sobie w relacjach Wiele do wielu. Przykład:

Table 1: post

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

Table 2: user

user has the following columns:

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

Join Table umożliwia tworzenie mapowań przy użyciu:

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

utworzy tabelę:

____________________
|  USER_ID| POST_ID |
|_________|_________|
|         |         |
|_________|_________|
slal
źródło
1
Pytanie: co jeśli mam już ten dodatkowy stół? JoinTable nie nadpisze istniejącego, prawda?
TheWandererr
@TheWandererr czy znalazłeś odpowiedź na swoje pytanie? Mam już stół do dołączenia
asgs
W moim przypadku jest to utworzenie zbędnej kolumny w tabeli bocznej będącej właścicielem. np. POST_ID w POST. Czy możesz zasugerować, dlaczego tak się dzieje?
SPS
0

@ManyToMany wspomnienia

Najczęściej będziesz musiał użyć @JoinTableadnotacji, aby określić mapowanie relacji wiele do wielu tabel:

  • nazwa tabeli linków i
  • dwie kolumny klucza obcego

Więc zakładając, że masz następujące tabele bazy danych:

Relacja między tabelami wiele do wielu

W Postencji można zmapować tę relację w następujący sposób:

@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<>();

@JoinTableAdnotacja jest używana do określenia nazwy tabel za pomocą nameatrybutu, a także kolumny klucza obcego która odwołuje się do posttabeli (na przykład joinColumns), a kolumna klucz obcy w post_tagtabeli połączeń, która odwołuje się Tagpodmiot za pośrednictwem inverseJoinColumnsatrybutu.

Zauważ, że atrybut kaskady @ManyToManyadnotacji jest ustawiony na PERSISTi MERGEtylko dlatego, że kaskadowanie REMOVEjest złym pomysłem, ponieważ instrukcja DELETE zostanie wydana dla innego rekordu nadrzędnego, tagw naszym przypadku, a nie dla post_tagrekordu. Więcej informacji na ten temat znajdziesz w tym artykule .

@OneToManyAsocjacje jednokierunkowe

@OneToManySkojarzenia jednokierunkowe , którym brakuje @JoinColumnodwzorowania, zachowują się jak relacje między tabelami wiele do wielu, a nie jeden do wielu.

Więc zakładając, że masz następujące mapowania encji:

@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
}

Hibernate przyjmie następujący schemat bazy danych dla powyższego mapowania encji:

Jednokierunkowe tabele asocjacyjnej bazy danych <code> @OneToMany </code> JPA

Jak już wyjaśniono, jednokierunkowe @OneToManymapowanie JPA zachowuje się jak asocjacja wiele do wielu.

Aby dostosować tabelę linków, możesz również użyć @JoinTableadnotacji:

@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<>();

A teraz zostanie wywołana tabela linków, post_comment_refa kolumny klucza obcego będą post_iddla posttabeli i post_comment_iddla post_commenttabeli.

@OneToManyPowiązania jednokierunkowe nie są wydajne, więc lepiej jest używać @OneToManyasocjacji dwukierunkowych lub tylko @ManyToOnebocznych. Zapoznaj się z tym artykułem, aby uzyskać więcej informacji na ten temat.

Vlad Mihalcea
źródło