อะไรคือความแตกต่างระหว่างการเชื่อมโยง 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.