อะไรคือความแตกต่างระหว่างการเชื่อมโยง JPA และไฮเบอร์เนตแบบทิศทางเดียวและแบบสองทิศทาง


146

ความแตกต่างระหว่างการเชื่อมโยงแบบทิศทางเดียวและแบบสองทิศทางคืออะไร?

เนื่องจากตารางที่สร้างใน db นั้นเหมือนกันทั้งหมดดังนั้นความแตกต่างเพียงอย่างเดียวที่ฉันพบคือแต่ละด้านของการเชื่อมโยงแบบสองทิศทางจะมีการอ้างถึงอีกด้านหนึ่งและไม่มีทิศทางเดียว

นี่คือการเชื่อมโยงแบบทิศทางเดียว

public class User {
    private int     id;
    private String  name;
    @ManyToOne
    @JoinColumn(
            name = "groupId")
    private Group   group;
}

public class Group {
    private int     id;
    private String  name;
}

การเชื่อมโยงแบบสองทิศทาง

public class User {
    private int     id;
    private String  name;
    @ManyToOne
    @JoinColumn(
            name = "groupId")
    private Group   group;
}
public class Group {
    private int         id;
    private String      name;
    @OneToMany(mappedBy="group")
    private List<User>  users;
}

ความแตกต่างคือกลุ่มมีการอ้างอิงของผู้ใช้หรือไม่

ฉันจึงสงสัยว่านี่เป็นข้อแตกต่างเพียงอย่างเดียวหรือไม่? แนะนำตัวไหน


9
ตอนนี้กลุ่มจะทราบว่ามีผู้ใช้รายใดบ้าง ฉันไม่คิดว่านี่เป็นความแตกต่างเล็กน้อย
Satadru Biswas

6
ความสัมพันธ์แบบสองทิศทางกลายเป็นเรื่องวุ่นวายสำหรับฉันเมื่อต้องอัปเดต :)
diyoda_

คำตอบ:


157

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

โปรดทราบว่าการเข้าถึงการนำทางไม่ใช่สิ่งที่ดีเสมอไปโดยเฉพาะอย่างยิ่งสำหรับความสัมพันธ์แบบ "หนึ่งต่อหลายคน" และ "หลายต่อหลายคน" ลองนึกภาพGroupที่มีหลายพันUsers:

  • คุณจะเข้าถึงได้อย่างไร? ด้วยจำนวนมากUserคุณมักจะต้องใช้การกรองและ / หรือการแบ่งหน้าเพื่อที่คุณจะต้องดำเนินการค้นหาต่อไป (เว้นแต่คุณจะใช้การกรองคอลเลคชันซึ่งดูเหมือนแฮ็คสำหรับฉัน) นักพัฒนาบางรายอาจใช้การกรองในหน่วยความจำในกรณีเช่นนี้ซึ่งเห็นได้ชัดว่าไม่ดีต่อประสิทธิภาพ โปรดทราบว่าการมีความสัมพันธ์เช่นนี้สามารถกระตุ้นให้นักพัฒนาประเภทนี้ใช้งานได้โดยไม่ต้องคำนึงถึงผลกระทบด้านประสิทธิภาพ

  • คุณจะเพิ่มUsers ใหม่ในGroup? โชคดีที่ Hibernate มองไปที่ความสัมพันธ์ที่เป็นเจ้าของเมื่อยังคงดำเนินต่อไปดังนั้นคุณจึงสามารถตั้งค่าUser.groupได้ อย่างไรก็ตามหากคุณต้องการให้วัตถุในหน่วยความจำสอดคล้องกันคุณต้องเพิ่มUserเข้าไปในGroup.usersไฟล์. แต่มันจะทำให้ Hibernate ดึงองค์ประกอบทั้งหมดGroup.usersจากฐานข้อมูล!

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

ดูสิ่งนี้ด้วย:


สวัสดีขอบคุณดูเหมือนว่าคุณจะเป็น experter ของจำศีลเนื่องจากคุณต้องตอบคำถามของฉัน: stackoverflow.com/questions/5350770/... และตอนนี้ฉันไม่แน่ใจว่ามีความสัมพันธ์กันอย่างไรเนื่องจากฉันไม่สามารถเขียนเพิ่มเติมในความคิดเห็นได้ดังนั้นฉันจึงโพสต์ไว้ที่นี่ dpaste.de/J85mโปรดตรวจสอบหากเป็นไปได้ :)
hguser

@hguser: ในที่สุดถ้าคุณตัดสินใจที่จะสร้างความสัมพันธ์แบบสองทิศทางฉันคิดว่ามันจะดีกว่าถ้าโทรsetGroup()จากaddUser()เพื่อให้ทั้งสองฝ่ายสอดคล้องกัน
axtavt

แล้วถ้าฉันไม่เรียก setGroup () ใน group.addUser () ล่ะ?
hguser

@hguser: ความสัมพันธ์จะไม่คงอยู่ดูจุดที่ 2 ในคำตอบ คุณสามารถโทรsetGroup()โดยไม่ต้องaddUser()แต่จะส่งผลให้สถานะของวัตถุในหน่วยความจำไม่สอดคล้องกัน
axtavt

ฉันพบว่าฉันไม่สามารถทำการแมปที่ถูกต้องได้โปรดเผื่อเวลาตรวจสอบโครงการของฉันที่ github ได้ไหม มันเป็นโครงการเล็ก ๆ น้อย ๆ
hguser

34

มีความแตกต่างหลักสองประการ

การเข้าถึงด้านการเชื่อมโยง

ข้อแรกเกี่ยวข้องกับวิธีที่คุณจะเข้าถึงความสัมพันธ์ สำหรับการเชื่อมโยงแบบทิศทางเดียวคุณสามารถนำทางการเชื่อมโยงจากปลายด้านเดียวเท่านั้น

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

หากคุณมีการ@OneToManyเชื่อมโยงแบบทิศทางเดียวหมายความว่าคุณสามารถเข้าถึงความสัมพันธ์ได้จากฝั่งแม่ที่จัดการคีย์นอกเท่านั้น

สำหรับการ@OneToManyเชื่อมโยงแบบสองทิศทางคุณสามารถนำทางการเชื่อมโยงได้ทั้งสองวิธีทั้งจากระดับบนสุดหรือจากฝั่งลูก

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

ประสิทธิภาพ

ด้านที่สองเกี่ยวข้องกับประสิทธิภาพ

  1. สำหรับ@OneToMany, สมาคมทิศทางเดียวไม่ได้ดำเนินการเช่นเดียวกับคนแบบสองทิศทาง
  2. สำหรับ@OneToOne, สมาคมสองทิศทางจะทำให้ผู้ปกครองที่จะลึกซึ้งกระหายถ้า Hibernate ไม่สามารถบอกได้ว่าพร็อกซีควรจะกำหนดหรือค่าโมฆะ
  3. สำหรับ@ManyToMany, ประเภทคอลเลกชันทำให้ค่อนข้างแตกต่างเป็นSetsListsทำงานได้ดีกว่า

แต่ฝั่งผู้ปกครองมักจะเป็นฝ่ายที่มี Foreign Key เท่าที่ฉันรู้ สองประโยคนี้ดูขัดแย้งกับฉัน from the child side where the foreign key resides. from the parent side where the foreign key resides.
TOMAS

FK อยู่เคียงข้างเด็กเสมอ OneToMany แบบทิศทางเดียวจัดการ FK ที่สามารถอยู่ในตารางย่อยในตารางการเข้าร่วม ตามลิงค์สำหรับรายละเอียดเพิ่มเติม
Vlad Mihalcea

11

ในแง่ของการเข้ารหัสความสัมพันธ์แบบสองทิศทางมีความซับซ้อนมากขึ้นในการนำไปใช้เนื่องจากแอปพลิเคชันมีหน้าที่ทำให้ทั้งสองฝ่ายซิงค์กันตามข้อกำหนดของ JPA 5 (ในหน้า 42) น่าเสียดายที่ตัวอย่างที่ระบุในข้อกำหนดไม่ได้ให้รายละเอียดเพิ่มเติมดังนั้นจึงไม่ได้ให้ความคิดเกี่ยวกับระดับความซับซ้อน

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

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

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


9

ฉันไม่แน่ใจ 100% ว่านี่เป็นข้อแตกต่างเพียงอย่างเดียวแต่เป็นความแตกต่างหลัก ขอแนะนำให้มีการเชื่อมโยงแบบสองทิศทางโดยเอกสาร Hibernate:

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/best-practices.html

โดยเฉพาะ:

ต้องการการเชื่อมโยงแบบสองทิศทาง: การเชื่อมโยงแบบทิศทางเดียวจะค้นหาได้ยากกว่า ในแอปพลิเคชันขนาดใหญ่การเชื่อมโยงเกือบทั้งหมดต้องสามารถนำทางได้ทั้งสองทิศทางในแบบสอบถาม

โดยส่วนตัวฉันมีปัญหาเล็กน้อยกับคำแนะนำผ้าห่มนี้สำหรับฉันแล้วดูเหมือนว่ามีหลายกรณีที่เด็กไม่มีเหตุผลที่เป็นประโยชน์ในการรู้เกี่ยวกับผู้ปกครอง (เช่นทำไมรายการสั่งซื้อจึงต้องรู้เกี่ยวกับคำสั่งซื้อนั้น เกี่ยวข้องกับ?) แต่ฉันก็เห็นคุณค่าของเวลาเช่นกัน และเนื่องจากการกำหนดทิศทางสองทิศทางไม่ได้ทำร้ายอะไรเลยฉันจึงไม่เห็นว่าเป็นเรื่องน่ารังเกียจที่จะปฏิบัติตาม


bi-directionality อาจทำร้ายได้ในบางกรณีตามที่ axtavt ได้อธิบายไว้! ฉันจะระมัดระวังเป็นอย่างยิ่งกับทุกความสัมพันธ์ที่แมปในเอนทิตีไฮเบอร์เนต! คุณสามารถใช้เวลาไม่สิ้นสุดในการโหลดสิ่งต่างๆใน Hibernate เว้นแต่คุณจะรู้แน่ชัดว่าคุณกำลังทำอะไรอยู่ พิจารณากรณีการใช้งานอย่างรอบคอบและใช้คลาสรุ่นต่างๆสำหรับกรณีการใช้งานที่แตกต่างกัน F.ex. ในรายการสิ่งต่างๆฉันไม่ต้องการวัตถุที่เกี่ยวข้องทั้งหมดมีเพียงป้ายกำกับและรหัส ดังนั้นเอนทิตีรายการจึงแตกต่าง (และง่ายมาก) จากเอนทิตีรายละเอียดสำหรับฉัน
cslotty
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.