Jaka jest różnica między @JoinColumn a mappedBy przy użyciu asocjacji JPA @OneToMany

Jaka jest różnica między:

@Entity
public class Company {

    @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
    @JoinColumn(name = "companyIdRef", referencedColumnName = "companyId")
    private List<Branch> branches;
    ...
}

I

@Entity
public class Company {

    @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY, mappedBy = "companyIdRef")
    private List<Branch> branches;
    ...
}
Author: Vlad Mihalcea, 2012-08-13

8 answers

Adnotacja @JoinColumn wskazuje, że encja ta jestwłaścicielem relacji (to znaczy: odpowiednia tabela ma kolumnę z kluczem obcym do tabeli odniesienia), podczas gdy atrybut mappedBy wskazuje, że encja po tej stronie jest odwrotnością relacji, a właściciel rezyduje w "innym" encji. Oznacza to również, że możesz uzyskać dostęp do drugiej tabeli z klasy, do której dodałeś adnotację "mappedBy" (w pełni dwukierunkowa relacja).

W w szczególności, dla kodu w pytaniu poprawne adnotacje wyglądałyby tak:

@Entity
public class Company {
    @OneToMany(mappedBy = "company",
               orphanRemoval = true,
               fetch = FetchType.LAZY,
               cascade = CascadeType.ALL)
    private List<Branch> branches;
}

@Entity
public class Branch {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "companyId")
    private Company company;
}
 572
Author: Óscar López,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-04 21:58:56

@JoinColumn może być stosowany po obu stronach związku. pytanie dotyczyło użycia {[2] } Po Stronie @OneToMany (rzadki przypadek). Chodzi o powielanie informacji fizycznych (Nazwa kolumny) wraz z nie zoptymalizowanym zapytaniem SQL, które wytworzy dodatkowe UPDATE instrukcje .

Według dokumentacji :

Ponieważ wiele do jednego to (prawie) zawsze strona właściciela relacji dwukierunkowej w JPA Spec, Stowarzyszenie jeden do wielu jest adnotowane przez @OneToMany(mappedBy=...)

@Entity
public class Troop {
    @OneToMany(mappedBy="troop")
    public Set<Soldier> getSoldiers() {
    ...
}

@Entity
public class Soldier {
    @ManyToOne
    @JoinColumn(name="troop_fk")
    public Troop getTroop() {
    ...
} 

Troop ma dwukierunkową relację jeden do wielu z Soldier poprzez własność wojsk. Nie musisz (nie musisz) definiować żadnego fizycznego mapowania po stronie mappedBy.

Aby odwzorować dwukierunkowy jeden do wielu, z jeden do wielu jako strona posiadająca , musisz usunąć mappedBy element i ustawić wiele na jeden @JoinColumn jako insertable i updatable NA false. To rozwiązanie nie jest zoptymalizowane i będzie Utwórz dodatkowe UPDATE stwierdzenia.

@Entity
public class Troop {
    @OneToMany
    @JoinColumn(name="troop_fk") //we need to duplicate the physical information
    public Set<Soldier> getSoldiers() {
    ...
}

@Entity
public class Soldier {
    @ManyToOne
    @JoinColumn(name="troop_fk", insertable=false, updatable=false)
    public Troop getTroop() {
    ...
}
 237
Author: Mike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-26 07:59:16

Jednokierunkowe skojarzenie jeden do wielu

Jeśli używasz adnotacji @OneToMany z @JoinColumn, to masz jednokierunkowe skojarzenie, takie jak to między jednostką rodzica Post a dzieckiem PostComment na poniższym diagramie:

Jednokierunkowe skojarzenie jeden do wielu

Przy użyciu jednokierunkowego skojarzenia jeden do wielu, tylko Strona nadrzędna mapuje skojarzenie.

W tym przykładzie tylko Post byt zdefiniuje @OneToMany związek z dzieckiem PostComment entity:

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

Dwukierunkowe skojarzenie jeden do wielu

Jeśli używasz @OneToMany z zestawem atrybutów mappedBy, masz dwukierunkowe skojarzenie. W naszym przypadku zarówno encja Post ma zbiór PostComment jednostek potomnych, jak i encja potomna PostComment ma odniesienie z powrotem do encji rodzica Post, Jak pokazano na poniższym diagramie:

Dwukierunkowe skojarzenie jeden do wielu

W encji PostComment właściwość encji post jest mapowana jako następuje:

@ManyToOne(fetch = FetchType.LAZY)
private Post post;

Powodem, dla którego wyraźnie ustawiliśmy atrybut fetch na FetchType.LAZY, jest to, że domyślnie wszystkie skojarzenia @ManyToOne i @OneToOne są pobierane chętnie, co może powodować N+1 problemy z zapytaniem.

W encji Post Asocjacja comments jest odwzorowana w następujący sposób:

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

Atrybut mappedBy adnotacji @OneToMany odwołuje się do właściwości post w encji potomka PostComment i w ten sposób Hibernate wie, że dwukierunkowe skojarzenie jest kontrolowane przez Strona @ManyToOne, która odpowiada za zarządzanie wartością kolumny klucza obcego, na której opiera się ta relacja tabeli.

Dla skojarzenia dwukierunkowego, musisz również mieć dwie metody użytkowe, takie jak addChild i removeChild:

public void addComment(PostComment comment) {
    comments.add(comment);
    comment.setPost(this);
}

public void removeComment(PostComment comment) {
    comments.remove(comment);
    comment.setPost(null);
}

Te dwie metody zapewniają, że obie strony dwukierunkowego skojarzenia są zsynchronizowane. Bez synchronizacji obu końców Hibernate nie gwarantuje, że zmiany stanu asocjacji będą propagowane do bazy danych.

Który z nich wybrać?

Jednokierunkowe skojarzenie @OneToMany nie działa zbyt dobrze, więc powinieneś tego unikać.

Lepiej używać dwukierunkowego @OneToMany, który jest bardziej wydajny.

 84
Author: Vlad Mihalcea,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-01-08 21:02:33

Adnotacja mappedBy idealnie powinna być zawsze używana w stronie nadrzędnej (klasie firmowej) relacji dwukierunkowej, w tym przypadku powinna być w klasie firmowej wskazując na zmienną członkowską "company" klasy potomnej (Branch class)

Adnotacja @ JoinColumn jest używana do określenia zmapowanej kolumny do dołączenia do asocjacji encji, ta adnotacja może być używana w dowolnej klasie (Rodzicu lub dziecku), ale powinna być używana tylko po jednej stronie (albo w Klasa rodzica lub w klasie potomnej Nie w obu) tutaj w tym przypadku użyłem go w stronie potomnej (klasie gałęzi) relacji dwukierunkowej wskazującej klucz obcy w klasie gałęzi.

Poniżej znajduje się roboczy przykład:

Klasa rodzica, firma

@Entity
public class Company {


    private int companyId;
    private String companyName;
    private List<Branch> branches;

    @Id
    @GeneratedValue
    @Column(name="COMPANY_ID")
    public int getCompanyId() {
        return companyId;
    }

    public void setCompanyId(int companyId) {
        this.companyId = companyId;
    }

    @Column(name="COMPANY_NAME")
    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    @OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy="company")
    public List<Branch> getBranches() {
        return branches;
    }

    public void setBranches(List<Branch> branches) {
        this.branches = branches;
    }


}

Klasa dziecięca, Oddział

@Entity
public class Branch {

    private int branchId;
    private String branchName;
    private Company company;

    @Id
    @GeneratedValue
    @Column(name="BRANCH_ID")
    public int getBranchId() {
        return branchId;
    }

    public void setBranchId(int branchId) {
        this.branchId = branchId;
    }

    @Column(name="BRANCH_NAME")
    public String getBranchName() {
        return branchName;
    }

    public void setBranchName(String branchName) {
        this.branchName = branchName;
    }

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="COMPANY_ID")
    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }


}
 32
Author: Soumyaansh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-05 10:23:01

Nie zgadzam się z przyjętą tu odpowiedzią Óscara Lópeza. Ta odpowiedź jest niedokładna!

Nie jest to @JoinColumn, co wskazuje, że podmiot ten jest właścicielem związku. Zamiast tego robi to adnotacja @ManyToOne (w jego przykładzie).

Adnotacje dotyczące relacji, takie jak @ManyToOne, @OneToMany i @ManyToMany powiedz JPA / Hibernate, aby utworzyło mapowanie. domyślnie odbywa się to poprzez oddzielne połączenie Stolik.


@JoinColumn

Celem @JoinColumn jest utworzenie kolumny join , jeśli jeszcze nie istnieje. Jeśli tak, to ta adnotacja może być użyta do nazwa kolumny join.


MappedBy

Celem parametru MappedBy jest polecenie JPA: nie Utwórz inną tabelę join, gdy relacja jest już mapowana by the opposite podmiot tego związku.



Remember: {[8] } jest właściwością adnotacji relacji, której celem jest wygenerowanie mechanizmu do powiązania dwóch encji, które domyślnie robią tworząc tabelę join. MappedBy zatrzymuje ten proces w jednym kierunku.

Encja nie używająca MappedBy mówi się, że jest właścicielem relacji, ponieważ mechanika mapowania jest podyktowana w jej klasie poprzez użycie jednego z trzech mapowań adnotacje względem pola klucza obcego. To nie tylko określa charakter mapowania, ale także instruuje Tworzenie tabeli join. Co więcej, opcja wyłączania tabeli join istnieje również poprzez zastosowanie adnotacji @ JoinColumn nad kluczem obcym, który utrzymuje ją wewnątrz tabeli podmiotu właściciela.

Tak w podsumowaniu: @JoinColumn albo tworzy nową kolumnę join albo zmienia nazwę istniejącej; podczas gdy parametr MappedBy współpracuje z relacją adnotacje drugiej klasy (potomnej) w celu utworzenia mapowania przez tabelę join lub przez utworzenie kolumny klucza obcego w powiązanej tabeli podmiotu właściciela.

Aby zilustrować jak działa MapppedBy, rozważ poniższy kod. Jeśli parametr MappedBy miałby zostać usunięty, wtedy Hibernate utworzyłby dwie tabele join! Dlaczego? Ponieważ w relacjach wielu do wielu istnieje symetria, a Hibernate nie ma uzasadnienia dla wyboru jednego kierunku nad drugim.

We dlatego użyj MappedBy aby powiedzieć Hibernate, wybraliśmy Inne entity do dyktowania odwzorowania relacji między dwoma podmiotami.

@Entity
public class Driver {
    @ManyToMany(mappedBy = "drivers")
    private List<Cars> cars;
}

@Entity
public class Cars {
    @ManyToMany
    private List<Drivers> drivers;
}

Dodanie @JoinColumn (name = "driverID") w klasie właściciela (patrz poniżej) uniemożliwi utworzenie tabeli join i zamiast tego utworzy kolumnę klucza obcego driverID w tabeli Cars, aby utworzyć mapowanie:

@Entity
public class Driver {
    @ManyToMany(mappedBy = "drivers")
    private List<Cars> cars;
}

@Entity
public class Cars {
    @ManyToMany
    @JoinColumn(name = "driverID")
    private List<Drivers> drivers;
}
 25
Author: IqbalHamid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-01-01 01:56:42

Chciałbym tylko dodać, że @JoinColumn nie zawsze musi być związane z fizyczną lokalizacją informacji, Jak sugeruje to ta odpowiedź. Możesz połączyć @JoinColumn z @OneToMany, nawet jeśli tabela rodzica nie ma danych tabeli wskazujących na tabelę potomną.

Jak zdefiniować jednokierunkową relację OneToMany w JPA

Jednokierunkowa Onetomania, Brak Manytoonu Odwrotnego, BRAK Tabeli Join

Wydaje się, że jest dostępny tylko w JPA 2.x+. Jest to przydatne w sytuacjach, w których chcesz, aby Klasa dziecięca zawierała tylko identyfikator rodzica, a nie pełne odniesienie.
 20
Author: Snekse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:27

JPA jest warstwowym API, różne poziomy mają swoje własne adnotacje. Najwyższym poziomem jest (1) poziom encji, który opisuje trwałe klasy, następnie masz (2) poziom relacyjnej bazy danych, który zakłada, że encje są mapowane do relacyjnej bazy danych i (3) Model java.

Przypisy poziomu 1: @Entity, @Id, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany. Możesz wprowadzić trwałość w swojej aplikacji, używając tylko tych adnotacji wysokiego poziomu. Ale potem musisz stworzyć swój bazy danych zgodnie z założeniami JPA. Te adnotacje określają model entity / relationship.

Przypisy poziomu 2: @Table, @Column, @JoinColumn, ... Wpływaj na mapowanie z encji/właściwości do relacyjnych tabel/kolumn bazy danych, jeśli nie jesteś zadowolony z domyślnych wartości JPA lub jeśli musisz zmapować do istniejącej bazy danych. Te adnotacje mogą być postrzegane jako adnotacje implementacji, określają one sposób mapowania.

Moim zdaniem najlepiej trzymać jak najwięcej do adnotacji wysokiego poziomu, a następnie wprowadzić adnotacje niższego poziomu w razie potrzeby.

Aby odpowiedzieć na pytania:@OneToMany/mappedBy jest najładniejsza, ponieważ używa tylko adnotacji z domeny encji. Na @oneToMany/@JoinColumn jest również w porządku, ale używa adnotacji implementacji tam, gdzie nie jest to bezwzględnie konieczne.

 1
Author: Bruno Ranschaert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-26 08:49:24

Pozwól mi to uprościć.
Możesz użyć @JoinColumn po obu stronach, niezależnie od mapowania.

Podzielmy to na trzy sprawy.
1) jednokierunkowe mapowanie od oddziału do firmy.
2) mapowanie dwukierunkowe od firmy do oddziału.
3) tylko jednokierunkowe mapowanie od firmy do oddziału.

Więc każdy przypadek użycia będzie należał do tych trzech kategorii. Pozwól mi więc wyjaśnić, jak używać @JoinColumni mappedBy.
1) Mapowanie jednokierunkowe od oddziału do firmy.
Użyj JoinColumn w tabeli gałęzi.
2) mapowanie dwukierunkowe od firmy do oddziału.
Użyj mappedBy w tabeli firm zgodnie z odpowiedzią @ Mychajło Adamowycz.
3) jednokierunkowe mapowanie od firmy do oddziału.
Wystarczy użyć @JoinColumn w tabeli firm.

@Entity
public class Company {

@OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
@JoinColumn(name="courseId")
private List<Branch> branches;
...
}

To mówi, że w oparciu o mapowanie klucza obcego "courseId" w tabeli branches, uzyskam listę wszystkich branches. Uwaga: nie możesz pobrać firma z oddziału w tym przypadku istnieje tylko jednokierunkowe mapowanie od firmy do oddziału.

 1
Author: Vinjamuri Satya Krishna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-17 07:53:45