JPA @OneToMany dernek kullanırken @JoinColumn ve mappedBy arasındaki fark nedir


516

Arasındaki fark nedir:

@Entity
public class Company {

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

ve

@Entity
public class Company {

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

1
Ayrıca , ilgili sorunların gerçekten iyi bir açıklaması için ORM eşleme sorusunun sahibi tarafın ne olduğuna bakın .
Dirkt

Yanıtlar:


545

Ek açıklama @JoinColumn, bu varlığın ilişkinin sahibi olduğunu belirtir (yani: karşılık gelen tabloda başvurulan tabloya yabancı anahtar içeren bir sütun vardır), ancak öznitelik mappedBybu taraftaki varlığın ilişkinin tersi olduğunu gösterir ve sahibi "diğer" varlıkta bulunur. Bu ayrıca, "mappedBy" (tam olarak çift yönlü ilişki) ile ek açıklama eklediğiniz sınıftan diğer tabloya erişebileceğiniz anlamına gelir.

Özellikle, sorudaki kod için doğru ek açıklamalar şöyle görünecektir:

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

@Entity
public class Branch {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "companyId")
    private Company company;
}

3
her iki durumda da Şube'nin Şirket kimliği olan bir alanı vardır.
Mykhaylo Adamovych

3
Şirket tablosunda başvurulan tabloya yabancı anahtar içeren bir sütun yok - Şube Şirkete başvuruda bulunuyor .. Neden "ilgili tablonun başvurulan tabloya yabancı anahtar içeren bir sütunu var" diyorsunuz? Biraz daha açıklar mısınız pls.
Mykhaylo Adamovych

13
@MykhayloAdamovych Cevabımı örnek kodla güncelledim. Uyarı kullanımı bir hata olduğunu @JoinColumniçindeCompany
Óscar López

10
@MykhayloAdamovych: Hayır, aslında tam olarak doğru değil. Eğer Branchbir mülk referansları yoktur Company, ancak altta yatan tablo yapan bir sütunu vardır, o zaman kullanabilirsiniz @JoinTableeşlemek için. Bu olağandışı bir durumdur, çünkü normalde nesnesindeki tablosuna karşılık gelen sütunu eşlersiniz, ancak olabilir ve mükemmel bir şekilde meşrudur.
Tom Anderson

4
Bu ORM'leri sevmemek için başka bir nedendir. Dokümantasyon genellikle çok tehlikeli ve kitaplarımda, bu çok fazla sihirli bölgede kıvrımlı. Bu sorunla mücadele ediyorum ve a kelimesine göre kelime kelime takip edildiğinde @OneToOne, çocuk satırları nullebeveyni referans alan FKey sütununda bir ile güncellenir .
Ashesh

225

@JoinColumnilişkinin her iki tarafında da kullanılabilir. Soru kullanarak ilgiliydi @JoinColumnüzerinde @OneToManyyan (nadiren). Ve buradaki nokta, bazı ek ifadeler üretecek optimize edilmemiş SQL sorgusu ile birlikte fiziksel bilgi çoğaltmasında (sütun adı) .UPDATE

Belgelere göre :

Çoktan bire (neredeyse) her zaman JPA spesifikasyonundaki çift yönlü ilişkinin sahibi tarafı olduğundan, bire çok ilişkisinin açıklaması@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() {
    ...
} 

TroopSoldiertroop özelliği ile çift yönlü bir-çok ilişkisine sahiptir . mappedByYanda herhangi bir fiziksel harita tanımlamanız gerekmez (zorunlu olmamalıdır) .

İki yönlü bir taneyi birden çok tarafa eşlemek için, bir-çok tarafı sahip olan taraf olarak , mappedByöğeyi kaldırmanız ve çok sayıda öğeyi bir @JoinColumnolarak insertableve updatableyanlış olarak ayarlamanız gerekir . Bu çözüm optimize edilmemiştir ve bazı ek UPDATEifadeler üretecektir .

@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() {
    ...
}

1
Birliğin ikinci pasajınızda nasıl sahibi olabileceğini anlayamıyorum, Asker hala birliğe atıfta bulunan yabancı anahtar içerdiğinden, hala sahip. (MySQL kullanıyorum, yaklaşımınızı kontrol ettim).
Akhilesh

10
Örnekleminizde ek açıklama mappedBy="troop"hangi alana atıfta bulunur?
Fractaliste

5
@Fraktaliste açıklama mappedBy="troop", Soldier sınıfındaki mülkiyet birliğini ifade eder. Yukarıdaki kodda özellik görünmüyor çünkü Mykhaylo onu atladı, ancak getTroop () alıcısı tarafından varlığını çıkarabilirsiniz. Lópezscar López'in cevabını kontrol edin , çok açık ve puan kazanacaksınız.
nicolimo86

1
Bu örnek, JPA 2 spesifikasyonunun kötüye kullanılmasıdır. Yazarın amacı çift yönlü bir ilişki oluşturmaksa, üst tarafta mappedBy ve alt tarafta JoinColumn (gerekirse) kullanmalıdır. Burada sunulan yaklaşım ile 2 tek yönlü ilişki elde ediyoruz: bağımsız olan OneToMany ve ManyToOne ama sadece şansla (daha fazla yanlış kullanımla) bu 2 ilişki aynı yabancı anahtar kullanılarak tanımlanır
aurelije

1
JPA 2.x kullanıyorsanız, aşağıdaki cevabım biraz daha temiz. Her iki yolu denemenizi ve Hibernate'in tabloları oluştururken ne yaptığını görmenizi öneririm. Yeni bir projedeyseniz, ihtiyaçlarınıza uygun olduğunu düşündüğünüz nesli seçin. Eski bir veritabanındaysanız ve yapıyı değiştirmek istemiyorsanız, şemanızla eşleşenleri seçin.
Snekse

65

Bu çok yaygın bir soru olduğundan, bu cevabın dayandığı bu makaleyi yazdım .

Tek yönlü bire çok ilişkilendirme

Açıkladığım gibi Bu makalede , @OneToManyek açıklamayı kullanırsanız , aşağıdaki diyagramda @JoinColumnüst Postvarlık ile alt öğe arasındaki gibi tek yönlü bir ilişkilendirmeniz olur PostComment:

Tek yönlü bire çok ilişkilendirme

Tek yönlü bire çok ilişkilendirme kullanırken, yalnızca üst taraf ilişkilendirmeyi eşler.

Bu örnekte, yalnızca Postvarlık @OneToMany, alt kuruluşla bir ilişki tanımlayacaktır PostComment:

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

Çift yönlü bire çok ilişkilendirme

Öznitelik seti @OneToManyile birlikte kullanırsanız, mappedByçift ​​yönlü bir ilişkilendirmeniz olur. Bizim durumumuzda, hem Postişletmenin PostCommentçocuk varlıkları koleksiyonu hem de çocukPostCommentPost aşağıdaki şemada gösterildiği gibi ana varlığa geri gönderilir :

Çift yönlü bire çok ilişkilendirme

İçinde PostComment varlık, postaşağıdaki gibi varlık özelliği eşleştirilmiş:

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

fetchÖzniteliği açıkça ayarlamamızın FetchType.LAZYnedeni, varsayılan olarak tümü @ManyToOneve@OneToOne dernekler N + 1 sorgu sorunlarına neden olabilir, hangi hevesle getirilmez. Bu konu hakkında daha fazla bilgi için bu makaleye göz atın .

İçinde Post varlık, commentsaşağıdaki gibi ilişki eşleştirilir:

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

mappedByÖznitelik @OneToManyaçıklama referanslar postçocuk mülk PostCommentvarlık ve bu şekilde, Hazırda çift yönlü ilişki ile kontrol edilir bilir @ManyToOnebu tablo ilişki dayanmaktadır Yabancı anahtar sütun değerinin yönetiminden sorumlu olan tarafı.

İki yönlü bir ilişkilendirme için, iki yardımcı yönteminizin olması gerekir. addChild ve removeChild:

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

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

Bu iki yöntem, çift yönlü ilişkinin her iki tarafının da eşitlenmemesini sağlar. Her iki ucu senkronize etmeden Hazırda Beklet, ilişkilendirme durumu değişikliklerinin veritabanına yayılacağını garanti etmez.

Çift yönlü ilişkileri JPA ve Hazırda Bekletme ile senkronize etmek için en iyi wat hakkında daha fazla bilgi için göz atın bu makaleye .

Hangisini seçmeli?

Tek yönlü@OneToMany ilişki çok iyi performans göstermediği için bundan kaçınmalısınız.

Daha verimli olan çift ​​yönlü@OneToMany kullanmanız daha iyi olur .


32

Haritalandırılan ek açıklama ideal hep bu durumda Çocuk sınıfının üye değişkeni 'şirket' ele Şirket sınıfı işaret olmalıdır, iki yönlü ilişkinin Veli tarafına (Şirket sınıfı) kullanılmalıdır (Şube sınıfı)

@JoinColumn ek açıklaması , bir varlık ilişkilendirmesine katılmak için eşlenmiş bir sütun belirtmek için kullanılır, bu ek açıklama herhangi bir sınıfta (Ebeveyn veya Çocuk) kullanılabilir, ancak ideal olarak yalnızca bir tarafta (üst sınıfta veya Alt sınıfta değil) kullanılmalıdır. her ikisinde de) burada bu durumda Şube sınıfındaki yabancı anahtarı gösteren iki yönlü ilişkinin Çocuk tarafında (Şube sınıfı) kullandım.

aşağıda çalışma örneği:

ana sınıf, Şirket

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


}

çocuk sınıfı, şube

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


}

20

Bu cevabın önerdiği gibi @JoinColumn, her zaman fiziksel bilgi konumu ile ilgili olması gerekmediğini eklemek isterim . Üst tabloda alt tabloya işaret eden tablo verisi olmasa bile ile birleştirebilirsiniz .@JoinColumn@OneToMany

JPA'da tek yönlü OneToMany ilişkisi nasıl tanımlanır

Tek Yönlü OneToMany, Ters ManyToOne Yok, Birleştirme Tablosu Yok

Sadece JPA 2.x+olsa da kullanılabilir gibi görünüyor . Alt sınıfın referans olarak değil tam olarak üst öğenin kimliğini içermesini istediğiniz durumlar için kullanışlıdır.


haklısın, JPA2'de birleştirici tablo olmadan tek yönlü OneToMany desteği tanıtıldı
aurelije

17

Lópezscar López'in kabul ettiği cevaba katılmıyorum. Bu cevap yanlış!

@JoinColumnBu varlığın ilişkinin sahibi olduğunu gösteren DEĞİLDİR . Bunun yerine,@ManyToOne ek açıklamadır (örneğinde).

İlişki gibi ek açıklamalarınızı @ManyToOne, @OneToManyve @ManyToManyJPA / Hibernate anlatmak bir eşleme oluşturun. Varsayılan olarak, bu ayrı bir Birleştirme Tablosu aracılığıyla yapılır.


@JoinColumn

Amacı, @JoinColumnhenüz yoksa bir birleştirme sütunu oluşturmaktır . Varsa, bu ek açıklama birleştirme sütununu adlandırmak için kullanılabilir .


MappedBy

MappedByParametrenin amacı JPA'ya talimat vermektir: İlişki zaten bu ilişkinin karşıt varlığı tarafından eşleştirildiği için başka bir birleştirme tablosu OLUŞTURMAYIN .



Unutmayın: MappedByamacı, birleştirme tablosu oluşturarak varsayılan olarak yaptıkları iki varlığı ilişkilendirmek için bir mekanizma oluşturmak olan ilişki ek açıklamalarının bir özelliğidir. MappedBybu işlemi bir yönde durdurur.

Kullanılmayan varlığın ilişkinin sahibiMappedBy olduğu söylenir, çünkü eşlemenin mekaniği, yabancı anahtar alanına karşı üç haritalama notundan biri kullanılarak kendi sınıfında dikte edilir. Bu yalnızca eşlemenin niteliğini belirtmekle kalmaz, aynı zamanda bir birleştirme tablosunun oluşturulmasını da bildirir. Ayrıca, birleştirme tablosunu bastırma seçeneği de, yabancı anahtar üzerine @JoinColumn ek açıklaması uygulayarak onu sahibi varlık tablosunun içinde tutar.

Özet olarak: @JoinColumnya yeni bir birleştirme sütunu oluşturur ya da mevcut bir sütunu yeniden adlandırır; iken MappedByparametre aracılığıyla ya bir eşleme oluşturmak için diğer (çocuk) sınıfının ilişki ek açıklamaları işbirliği halinde çalışmaktadır tablo veya sahibi varlığın ilişkili tablodaki yabancı anahtar sütunu oluşturarak katılmak.

Nasıl MapppedByçalıştığını göstermek için aşağıdaki kodu göz önünde bulundurun. Eğer MappedByparametre Silinecek vardı sonra hazırda aslında yaratacak İKİ tabloları katılmak! Neden? Çünkü çoktan çoğa ilişkilerinde bir simetri vardır ve Hazırda Bekletme'nin bir yönü diğerinden seçmek için bir mantığı yoktur.

Bu nedenle MappedBy, Hazırda Beklet'e söylemek için kullanırız , iki varlık arasındaki ilişkinin eşlenmesini dikte etmek için diğer varlığı seçtik .

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

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

Sahip sınıfına @JoinColumn (name = "driverID") eklenmesi (aşağıya bakın), bir birleştirme tablosunun oluşturulmasını önler ve bunun yerine bir eşleme oluşturmak için Cars tablosunda bir driverID yabancı anahtar sütunu oluşturur:

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

@Entity
public class Cars {
    @ManyToMany
    @JoinColumn(name = "driverID")
    private List<Drivers> drivers;
}

1

JPA katmanlı bir API'dir, farklı düzeylerin kendi ek açıklamaları vardır. En yüksek seviye, kalıcı sınıfları tanımlayan (1) Varlık düzeyidir, o zaman varlıkların bir ilişkisel veritabanına ve (3) java modeline eşlendiğini varsayan (2) ilişkisel veritabanı düzeyine sahip olursunuz.

Seviye 1 açıklamalar: @Entity, @Id, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany. Yalnızca bu üst düzey ek açıklamaları kullanarak uygulamanızda kalıcılık sağlayabilirsiniz. Ama sonra veritabanınızı JPA'nın varsayımlarına göre oluşturmanız gerekir. Bu ek açıklamalar varlık / ilişki modelini belirtir.

Seviye 2 açıklamalar: @Table, @Column, @JoinColumn, ... ilişkisel veritabanı tablolarına kişiler / özelliklerinden eşleme Influence / sen JPA varsayılan memnun olmayan ya da varolan bir veritabanına haritasına gerekirse eğer sütunlar. Bu ek açıklamalar uygulama ek açıklamaları olarak görülebilir, eşleştirmenin nasıl yapılması gerektiğini belirtir.

Benim düşünceme göre, mümkün olduğunca yüksek düzeyli ek açıklamalara sadık kalmak ve sonra daha düşük düzeyli ek açıklamaları tanıtmak en iyisidir.

Soruları cevaplamak için: @OneToMany/ mappedByyalnızca en iyisidir çünkü yalnızca varlık alanından ek açıklamaları kullanır. @oneToMany/ @JoinColumnDa iyi ama bu kesinlikle gerekli olmayan bir uygulama ek açıklama kullanır.


1

Basitleştireyim. Eşlemeden bağımsız olarak her iki tarafta @JoinColumn
kullanabilirsiniz .

Bunu üç duruma ayıralım.
1) Şube'den Şirkete tek yönlü haritalama.
2) Şirketten Şubeye iki yönlü haritalama.
3) Sadece Şirketten Şube'ye tek yönlü haritalama.

Yani herhangi bir kullanım durumu bu üç kategoriye girecektir. @JoinColumn ve mappedBy'nin nasıl kullanılacağını açıklayayım .
1) Şube'den Şirkete tek yönlü haritalama. Branch tablosunda JoinColumn
kullanın . 2) Şirketten Şubeye iki yönlü haritalama. @Mykhaylo Adamovych'in cevabında açıklandığı gibi Şirket tablosunda mappedBy kullanın . 3) Şirketten Şubeye tek yönlü haritalama. Şirket tablosunda @JoinColumn kullanın .



@Entity
public class Company {

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

Bu şube tablosunda yabancı anahtar "courseId" eşleme dayalı, bana tüm şubelerin listesini almak diyor. NOT: Bu durumda şirketi şubeden getiremezsiniz, yalnızca şirketten şubeye tek yönlü haritalama vardır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.