"ฝั่งเจ้าของ" ในการแมป ORM คืออะไร


129

สิ่งที่ว่าไม่ด้านการเป็นเจ้าของหมายถึง? อะไรคือคำอธิบายพร้อมตัวอย่างการทำแผนที่ ( หนึ่งต่อหลายคนหนึ่งต่อหนึ่งหลายคนต่อหนึ่ง )

ข้อความต่อไปนี้เป็นข้อความที่ตัดตอนมาจากคำอธิบายของ@OneToOneในเอกสาร Java EE 6 คุณสามารถดูแนวคิดที่เป็นเจ้าของได้

กำหนดการเชื่อมโยงมูลค่าเดียวกับเอนทิตีอื่นที่มีหลายหลากแบบหนึ่งต่อหนึ่ง โดยปกติแล้วไม่จำเป็นต้องระบุเอนทิตีเป้าหมายที่เกี่ยวข้องอย่างชัดเจนเนื่องจากโดยปกติแล้วจะสามารถอนุมานได้จากประเภทของวัตถุที่อ้างอิง หากความสัมพันธ์เป็นแบบสองทิศทางฝ่ายที่ไม่ได้เป็นเจ้าของจะต้องใช้องค์ประกอบ mappedBy ของคำอธิบายประกอบ OneToOne เพื่อระบุฟิลด์ความสัมพันธ์หรือคุณสมบัติของฝั่งเจ้าของ



5
ฉันหลงทางจนได้อ่านสิ่งนี้javacodegeeks.com/2013/04/…
darga33

2
ตาราง DB ที่มีคอลัมน์ Foreign Key ถือเป็นฝั่งเจ้าของ ดังนั้นนิติบุคคลที่เป็นตัวแทนของตาราง DB คือเจ้าของ (ฝั่งเจ้าของ) ของความสัมพันธ์นั้น ไม่จำเป็น แต่กรณีส่วนใหญ่ฝั่งเจ้าของจะมีคำอธิบายประกอบ @JoinColumn
Diablo

คำตอบ:


203

เหตุใดความคิดเกี่ยวกับความเป็นเจ้าของจึงจำเป็น:

แนวความคิดเกี่ยวกับความเป็นเจ้าของของความสัมพันธ์แบบสองทิศทางนั้นมาจากข้อเท็จจริงที่ว่าในฐานข้อมูลเชิงสัมพันธ์ไม่มีความสัมพันธ์แบบสองทิศทางเหมือนในกรณีของวัตถุ ในฐานข้อมูลเรามีเพียงความสัมพันธ์แบบทิศทางเดียว - คีย์ต่างประเทศ

สาเหตุของชื่อ 'ฝั่งเจ้าของ' คืออะไร?

ด้านการเป็นเจ้าของของความสัมพันธ์ที่ติดตามโดย Hibernate คือด้านของความสัมพันธ์ที่เป็นเจ้าของคีย์ต่างประเทศในฐานข้อมูล

อะไรคือปัญหาที่ความคิดเกี่ยวกับการเป็นเจ้าของสามารถแก้ไขได้?

ยกตัวอย่างเอนทิตีสองรายการที่แมปโดยไม่ประกาศฝั่งเจ้าของ:

@Entity
@Table(name="PERSONS")
public class Person {
    @OneToMany
    private List<IdDocument>  idDocuments;
}

@Entity
@Table(name="ID_DOCUMENTS")
public class IdDocument {
    @ManyToOne
    private Person person;
}

จากมุมมอง OO การทำแผนที่นี้ไม่ได้กำหนดความสัมพันธ์แบบสองทิศทาง แต่เป็นสองทิศทางทิศทางที่แยกจากกัน

การแมปจะไม่เพียงสร้างตารางPERSONSและID_DOCUMENTSยังสร้างตารางการเชื่อมโยงที่สามด้วยPERSONS_ID_DOCUMENTS:

CREATE TABLE PERSONS_ID_DOCUMENTS
(
  persons_id bigint NOT NULL,
  id_documents_id bigint NOT NULL,
  CONSTRAINT fk_persons FOREIGN KEY (persons_id) REFERENCES persons (id),
  CONSTRAINT fk_docs FOREIGN KEY (id_documents_id) REFERENCES id_documents (id),
  CONSTRAINT pk UNIQUE (id_documents_id)
)

ขอให้สังเกตคีย์หลักpkในID_DOCUMENTSเท่านั้น ในกรณีนี้ไฮเบอร์เนตติดตามทั้งสองด้านของความสัมพันธ์โดยอิสระ: หากคุณเพิ่มเอกสารลงในความสัมพันธ์เอกสารPerson.idDocumentsนั้นจะแทรกระเบียนในตารางการเชื่อมโยงPERSON_ID_DOCUMENTSมันแทรกบันทึกในตารางสมาคม

ในทางกลับกันถ้าเราโทรหาidDocument.setPerson(person)เราเปลี่ยนที่สำคัญต่างประเทศ person_id ID_DOCUMENTSบนโต๊ะ ไฮเบอร์เนตกำลังสร้างความสัมพันธ์สองทิศทางเดียว (คีย์ต่างประเทศ) บนฐานข้อมูลเพื่อใช้งานอย่างใดอย่างหนึ่งความสัมพันธ์วัตถุแบบสองทิศทาง

แนวคิดเรื่องการเป็นเจ้าของช่วยแก้ปัญหาได้อย่างไร:

หลายครั้งสิ่งที่เราต้องการเป็นเพียงที่สำคัญต่างประเทศในตารางID_DOCUMENTSต่อPERSONSและตารางการเชื่อมโยงพิเศษ

เพื่อแก้ปัญหานี้เราจำเป็นต้องกำหนดค่า Hibernate Person.idDocumentsเพื่อหยุดการติดตามการปรับเปลี่ยนความสัมพันธ์ ไฮเบอร์เนตควรติดตามอีกด้านหนึ่งของความสัมพันธ์IdDocument.personเท่านั้นและในการทำเช่นนั้นเราจึงเพิ่มmappedBy :

@OneToMany(mappedBy="person")
private List<IdDocument>  idDocuments;

mappedBy หมายถึงอะไร?

ซึ่งหมายความว่า: "การแก้ไขในด้านนี้ของความสัมพันธ์ถูกแมปไว้แล้วโดย อีกด้านหนึ่งของความสัมพันธ์ IdDocument.person ดังนั้นไม่จำเป็นต้องติดตามที่นี่แยกต่างหากในตารางพิเศษ"

มี GOTCHA ผลที่ตามมาหรือไม่?

การใช้mappedByหากเราเรียกเท่านั้นperson.getDocuments().add(document)Foreign Key ในID_DOCUMENTSจะไม่ถูกเชื่อมโยงกับเอกสารใหม่เพราะนี่ไม่ใช่ด้านการเป็นเจ้าของ / ติดตามของความสัมพันธ์!

ในการเชื่อมโยงเอกสารกับบุคคลใหม่คุณต้องโทรอย่างชัดเจนdocument.setPerson(person)เพราะนั่นคือฝั่งที่เป็นเจ้าของความสัมพันธ์

เมื่อใช้mappedByเป็นความรับผิดชอบของนักพัฒนาที่จะต้องรู้ว่าอะไรคือฝั่งที่เป็นเจ้าของและอัปเดตด้านที่ถูกต้องของความสัมพันธ์เพื่อที่จะทำให้เกิดการคงอยู่ของรีเลชันใหม่ในฐานข้อมูล


17
คำตอบที่ดีที่สุดที่ฉันพบว่าอธิบายหลักคำสอน 'mappedBy' + 'inversedBy'
Kurt Zhong

1
ขอขอบคุณที่ระบุการแมปและเหตุผลเบื้องหลังแนวคิด
Mohnish

1
ฉันไม่รู้ว่ามีการเปลี่ยนแปลงหรือไม่ แต่ใน Hibernate 5.0.9 สุดท้ายถ้าฉัน "โทรเท่านั้นperson.getDocuments().add(document)" ไฮเบอร์เนตจะอัปเดต Foreign Key ในID_DOCUMENTSไฟล์.

1
@ คาร์ลนิโคลัสสวัสดีเห็นด้วยกับคุณเรามีแอตทริบิวต์แบบเรียงซ้อนใน@OneToManyคำอธิบายประกอบที่สามารถตั้งค่าเป็น PERSIST ได้ในกรณีนี้โหมดไฮเบอร์เนตจะบันทึกเอนทิตีที่เชื่อมโยงทั้งหมดไปยังฐานข้อมูล ใครช่วยชี้แจงเรื่องนี้ได้ไหม - ทำไมผู้เขียนถึงบอกว่าไฮเบอร์เนตจะไม่ติดตามการเปลี่ยนแปลงในฝั่งที่ไม่ได้เป็นเจ้าของ - แต่ในความเป็นจริงการติดตามแบบจำศีล
Oleksandr Papchenko

3
cascade บอกให้ผู้ให้บริการบันทึกเอนทิตีลูกแม้ว่าเอนทิตีแม่จะไม่ได้เป็นเจ้าของก็ตามดังนั้นจึงแก้ไขกฎได้อย่างมีประสิทธิภาพ หากคุณมี (หรือมี) mappedBy = child.field และไม่มี cascade ลูก ๆ ของรายการจะไม่ถูกบันทึก นอกจากนี้หากคุณไม่ได้แมป By AND ไม่มี Cascade ผู้ปกครองจะเป็นเจ้าของความสัมพันธ์และถ้าคุณใส่ลูกใหม่ในรายการจากนั้นบันทึก Parent มันจะทำให้เกิดข้อยกเว้นเนื่องจาก ID เด็กใหม่ไม่พร้อมใช้งาน จะถูกบันทึกไว้ในตารางการเข้าร่วม หวังว่าคงกระจ่าง ... :)
K.Nicholas

142

คุณสามารถจินตนาการได้ว่าฝั่งเจ้าของคือเอนทิตีที่มีการอ้างอิงถึงอีกด้านหนึ่ง ในข้อความที่ตัดตอนมาคุณมีความสัมพันธ์แบบหนึ่งต่อกลุ่ม เนื่องจากมันเป็นสมมาตรความสัมพันธ์คุณจะพบว่าถ้าวัตถุ A สัมพันธ์กับวัตถุ B ดังนั้นในทางกลับกันก็เป็นจริง

ซึ่งหมายความว่าการบันทึกลงในอ็อบเจ็กต์ A การอ้างอิงถึงอ็อบเจ็กต์ B และการบันทึกในอ็อบเจ็กต์ B การอ้างอิงถึงอ็อบเจ็กต์ A จะซ้ำซ้อนนั่นคือเหตุผลที่คุณเลือกอ็อบเจ็กต์ที่ "เป็นเจ้าของ" อีกอันที่มีการอ้างอิงถึงอ็อบเจ็กต์

เมื่อคุณมีความสัมพันธ์แบบหนึ่งต่อกลุ่มวัตถุที่เกี่ยวข้องกับส่วน "หลาย" จะเป็นฝ่ายที่เป็นเจ้าของมิฉะนั้นคุณจะต้องจัดเก็บข้อมูลอ้างอิงจำนวนมากจากวัตถุชิ้นเดียวไปจนถึงหลาย ๆ เพื่อหลีกเลี่ยงสิ่งนั้นทุกออบเจ็กต์ในคลาสที่สองจะมีตัวชี้ไปยังวัตถุเดียวที่พวกเขาอ้างถึง (ดังนั้นจึงเป็นฝั่งที่เป็นเจ้าของ)

สำหรับความสัมพันธ์แบบกลุ่มต่อกลุ่มเนื่องจากคุณจะต้องมีตารางการแมปแยกต่างหากอย่างไรก็ตามจะไม่มีฝ่ายใดเป็นเจ้าของ

สรุปได้ว่าฝั่งเจ้าของคือเอนทิตีที่มีการอ้างอิงถึงอีกฝ่าย


6
ขอบคุณสำหรับคำชี้แจง
เพียงแค่ผู้เรียน

2
อาจช่วยให้เห็นการร้องเช่นกันคำตอบของฉันด้วยเหตุผลของชื่อ 'mappedBy' และ 'owning side' จะเกิดอะไรขึ้นถ้าเราไม่กำหนดด้านการเป็นเจ้าของ GOTCHAs หวังว่าจะช่วยได้
Angular University

5
ฉันเดาว่าคำตอบส่วนใหญ่ถูกต้อง แต่สำหรับ Hibernate อย่างน้อยแม้แต่ความสัมพันธ์แบบกลุ่มต่อกลุ่มก็มีความเป็นเจ้าของ สิ่งนี้มีผลต่อพฤติกรรมการอัปเดตเช่น ดูส่วนที่ 4 ("อัปเดตคลาสรุ่นไฮเบอร์เนต") ของบทช่วยสอนนี้: viralpatel.net/blogs/…
Pfiver

11
คำตอบนี้สร้างความสับสนมากกว่าที่จะช่วยได้ มีอะไรดีที่จะบอกว่า "คุณสามารถจินตนาการได้ว่าฝั่งเจ้าของคือเอนทิตีที่มีการอ้างอิงถึงอีกด้านหนึ่ง" เมื่ออยู่ในความสัมพันธ์แบบสองทิศทางอ็อบเจ็กต์เอนทิตีทั้งสองจะมีการอ้างอิงซึ่งกันและกัน นอกจากนี้ "สำหรับความสัมพันธ์แบบกลุ่มต่อกลุ่มเนื่องจากคุณจะต้องมีตารางการแมปแยกต่างหากอย่างไรก็ตามจะไม่มีฝ่ายใดฝ่ายหนึ่งเป็นเจ้าของ" นั้นไม่ถูกต้องธรรมดา: @ManyToManyความสัมพันธ์ก็มีฝ่ายที่เป็นเจ้าของเช่นกัน ในทำนองเดียวกัน@OneToManyความสัมพันธ์สามารถใช้ตารางการเข้าร่วมและคุณยังต้องระบุฝั่งที่เป็นเจ้าของ
DavidS

5
โดยพื้นฐานแล้วนี่เป็นคำตอบที่น่ารักและให้ความรู้สึกดีที่มีการโหวตโหวตเพราะเข้าใจง่ายกว่าความจริง
DavidS
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.