คำอธิบายประกอบไฮเบอร์เนต - การเข้าถึงฟิลด์หรือคุณสมบัติใดดีกว่ากัน


135

คำถามนี้เป็นคำถามที่ค่อนข้างที่เกี่ยวข้องกับHibernate หมายเหตุตำแหน่งคำถาม

แต่อยากรู้ว่าอันไหนดีกว่ากัน ? เข้าถึงผ่านคุณสมบัติหรือเข้าถึงผ่านช่อง? ข้อดีข้อเสียของแต่ละอย่างเป็นอย่างไร?

คำตอบ:


33

ฉันชอบ accessors เพราะฉันสามารถเพิ่มตรรกะทางธุรกิจให้กับ accessors ได้ทุกเมื่อที่ต้องการ นี่คือตัวอย่าง:

@Entity
public class Person {

  @Column("nickName")
  public String getNickName(){
     if(this.name != null) return generateFunnyNick(this.name);
     else return "John Doe";
  }
}

นอกจากนี้หากคุณโยน libs อื่นลงในส่วนผสม (เช่น lib ที่แปลง JSON บางตัวหรือ BeanMapper หรือ Dozer หรือ bean mapping / cloning lib อื่น ๆ ตามคุณสมบัติ getter / setter) คุณจะรับประกันได้ว่า lib นั้นซิงค์กับการคงอยู่ ผู้จัดการ (ทั้งคู่ใช้ getter / setter)


18
โปรดทราบว่า ORM เข้าถึงฟิลด์ / คุณสมบัติของคุณอย่างไรไม่ใช่รหัสแอปพลิเคชันของคุณ ด้วยการเข้าถึงฟิลด์ getNickName () วิธีการของคุณจะทำงานได้ตามที่คุณคาดหวัง สิ่งเดียวกันนี้ไม่เป็นความจริงหากคุณใช้ 'คุณสมบัติ' ภายนอก getters / setters นั่นคือจุดที่คุณอาจประสบปัญหาเกี่ยวกับการเข้าถึงคุณสมบัติและการโหลดขี้เกียจ ดังนั้นไม่ฉันไม่เห็นด้วยกับข้อโต้แย้งนี้โดยทั่วไป อย่างไรก็ตามครั้งล่าสุดที่ฉันตรวจสอบ Hibernate มีปัญหาในการเข้าถึงฟิลด์ @Id
Rob Bygrave

11
คำตอบนี้ไม่เกี่ยวข้องกับคำถาม คำตอบที่ดีที่สุด s โดย duffymo
Janning

10
ไม่ควรมีตรรกะทางธุรกิจใด ๆ ใน accessors นั่นไม่ใช่พฤติกรรมที่ชัดเจน
iuriisusuk

เหตุใดจึงทำเครื่องหมายคำตอบนี้ว่าถูกต้อง ไม่เป็นความจริงคุณสามารถแมปฟิลด์และจัดเตรียม setter / getter ในลักษณะเดียวกัน
Lucke

ฉันกำลังจะทดลองกับสิ่งนี้ แต่สิ่งนี้สามารถสืบทอดผ่านอินเทอร์เฟซได้หรือไม่
mmm

251

มีข้อโต้แย้งสำหรับทั้งสองอย่าง แต่ส่วนใหญ่เกิดจากข้อกำหนดบางประการของผู้ใช้ "จะเกิดอะไรขึ้นถ้าคุณต้องการเพิ่มตรรกะสำหรับ" หรือ "xxxx break encapsulation" อย่างไรก็ตามไม่มีใครแสดงความคิดเห็นเกี่ยวกับทฤษฎีนี้และให้เหตุผลที่สมเหตุสมผล

Hibernate / JPA กำลังทำอะไรอยู่เมื่อมันยังคงมีวัตถุอยู่นั่นคือสถานะของวัตถุนั้นคงอยู่ นั่นหมายถึงการจัดเก็บในลักษณะที่สามารถทำซ้ำได้ง่าย

การห่อหุ้มคืออะไร? การห่อหุ้มหมายถึงการห่อหุ้มข้อมูล (หรือสถานะ) ด้วยอินเทอร์เฟซที่แอปพลิเคชัน / ไคลเอนต์สามารถใช้เพื่อเข้าถึงข้อมูลได้อย่างปลอดภัย - ทำให้ข้อมูลสอดคล้องและถูกต้อง

คิดว่าสิ่งนี้เหมือนกับ MS Word MS Word คงรูปแบบของเอกสารไว้ในหน่วยความจำ - เอกสาร STATE นำเสนออินเทอร์เฟซที่ผู้ใช้สามารถใช้เพื่อแก้ไขเอกสาร - ชุดปุ่มเครื่องมือคำสั่งแป้นพิมพ์ ฯลฯ อย่างไรก็ตามเมื่อคุณเลือกที่จะคงอยู่ (บันทึก) เอกสารนั้นจะบันทึกสถานะภายในไม่ใช่ชุดของการกดแป้นพิมพ์และ ใช้การคลิกเมาส์เพื่อสร้างมัน

การบันทึกสถานะภายในของวัตถุไม่ทำให้การห่อหุ้ม - มิฉะนั้นคุณจะไม่เข้าใจจริงๆว่าการห่อหุ้มหมายถึงอะไรและเหตุใดจึงมีอยู่ มันเหมือนกับการทำให้เป็นอนุกรมของวัตถุจริงๆ

ด้วยเหตุนี้ในกรณีส่วนใหญ่จึงเหมาะสมที่จะคงอยู่ใน FIELDS ไม่ใช่ ACCESSORS ซึ่งหมายความว่าสามารถสร้างออบเจ็กต์ใหม่จากฐานข้อมูลได้อย่างแม่นยำตรงกับวิธีการจัดเก็บ ไม่จำเป็นต้องมีการตรวจสอบความถูกต้องใด ๆ เนื่องจากสิ่งนี้ทำกับต้นฉบับเมื่อสร้างขึ้นและก่อนที่จะถูกเก็บไว้ในฐานข้อมูล (เว้นแต่พระเจ้าห้ามคุณกำลังจัดเก็บข้อมูลที่ไม่ถูกต้องในฐานข้อมูล !!!!) ในทำนองเดียวกันไม่จำเป็นต้องคำนวณค่าเนื่องจากมีการคำนวณก่อนที่จะจัดเก็บวัตถุ วัตถุควรมีลักษณะเหมือนเดิมก่อนที่จะบันทึก ในความเป็นจริงการเพิ่มสิ่งอื่น ๆ เข้าไปใน getters / setters คุณกำลังเพิ่มความเสี่ยงที่คุณจะสร้างสิ่งที่ไม่ใช่สำเนาต้นฉบับทั้งหมด

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


10
คำตอบทางวิศวกรรมซอฟต์แวร์คือการใช้ accessors ละเมิด DRY
sourcedelica

1
@Martin ฉันมีคำถามติดตามเกี่ยวกับย่อหน้าสุดท้ายของคำตอบของคุณ คุณเขียนว่า "ตัวอย่างอาจเป็นไปได้ว่าคุณต้องการหลีกเลี่ยงการคงค่าที่คำนวณไว้" คุณจะหลีกเลี่ยงค่าที่คำนวณได้โดยการเข้าถึงตามคุณสมบัติได้อย่างไร ฉันรู้ว่าคุณกำลังโต้เถียงที่จะไม่ทำเช่นนั้น แต่ฉันไม่เข้าใจตรงนี้ คุณช่วยอธิบายได้ไหม?
Geek

4
@Geek ตอนนี้ฉันอ่านย้อนหลังฉันไม่แน่ใจตัวเองทั้งหมด สองปีแล้วที่ฉันเขียนคำตอบนี้ ฉันเดาว่าตัวอย่างที่ดีกว่าอาจเป็นที่ที่คุณกำลังทำงานกับฐานข้อมูลเดิมและข้อมูลจะถูกนำเสนอในรูปแบบที่แตกต่างไปจากโมเดลอ็อบเจ็กต์ของคุณ - accessors สามารถให้การแมประหว่างทั้งสอง
Martin

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

5
@ElnurAbdurrakhimov ไปนั่นเป็นตัวอย่างที่ยอดเยี่ยม ขอบคุณ.
Martin

81

ฉันชอบการเข้าถึงฟิลด์มากกว่าเพราะวิธีนั้นฉันไม่ได้บังคับให้จัดหา getter / setter สำหรับแต่ละคุณสมบัติ

การสำรวจสั้น ๆ ผ่าน Google ชี้ให้เห็นว่าการเข้าถึงข้อมูลเป็นส่วนใหญ่ (เช่นhttp://java.dzone.com/tips/12-feb-jpa-20-why-accesstype )

ฉันเชื่อว่าการเข้าถึงช่องเป็นสำนวนที่ Spring แนะนำ แต่ฉันไม่พบข้อมูลอ้างอิงเพื่อสำรองข้อมูลนั้น

มีคำถาม SO ที่เกี่ยวข้องซึ่งพยายามวัดประสิทธิภาพและได้ข้อสรุปว่า "ไม่มีความแตกต่าง"


หากคุณไม่ได้ระบุ setter getter ในเอนทิตีแล้วการใช้ฟิลด์นั้นคืออะไร ... คุณไม่สามารถใช้มันได้ในแอปพลิเคชันเนื่องจากฟิลด์เป็นแบบส่วนตัว
anshulkatta

1
ไม่ได้ให้ getter และ setter สำหรับการปฏิบัติที่ไม่ดีในสนามของคุณใช่ไหมฉันเดาว่าความคิดเห็นของฉันที่นี่ไม่ถูกต้องเสมอไปเนื่องจากฉันสมมติว่าเป็นฟิลด์สาธารณะในขณะที่เห็นได้ชัดว่ามันอาจเป็นฟิลด์ส่วนตัวที่ไม่เคยเข้าถึง
Mathijs Segers

3
@anshulkatta ฉันรู้สึกว่าฉันควรตอบคำถามของคุณจริงๆเพราะนั่นคือสิ่งที่ห่อหุ้มอยู่ ตามหลักการแล้วฟิลด์ทั้งหมดของคุณควรเป็นแบบส่วนตัวและหากเป็นไปได้ก็ไม่ควรมีตัวรับหรือตัวตั้งนั่นคือระดับการห่อหุ้มที่ดีที่สุดที่คุณสามารถหวัง พิจารณาตัวตรวจสอบรหัสผ่าน รหัสผ่าน 2 ช่องส่วนตัวแฮชและล้มเหลว ทั้งสองอย่างสามารถเป็นส่วนตัวโดยไม่มีตัวรับหรือตัวตั้งค่า พวกเขาถูกใช้โดยบูล checkPassword (รหัสผ่านสตริง) ซึ่งแฮชตรวจสอบกับ passwordHash แล้วอัปเดตล้มเหลวพยายามและส่งคืนผลลัพธ์ ไม่จำเป็นต้องใช้รหัสอื่นในการเข้าถึงทั้งสองฟิลด์
Martin

2
@anshulkatta ควรสังเกตว่าใน OOP getters และ setters เป็นรูปแบบการต่อต้านซึ่งมาจากการเขียนโปรแกรมขั้นตอนการมีคลาสกับพวกเขาทำลายหลักการห่อหุ้มและสร้างรหัสสำเร็จรูปจำนวนมากนั่นคือประเภทเดียวกันของ รหัสซ้ำแล้วซ้ำอีก ออบเจ็กต์ควรไม่เปลี่ยนรูปหากจำเป็นต้องมีการปรับเปลี่ยนคุณสมบัติควรทำโดยใช้วิธีการที่ทำบางอย่างมากกว่าเพียงแค่คืนค่าของคุณสมบัติ
Uriel Arvizu

ไม่เป็นเช่นนั้น "การต่อต้านรูปแบบ" ในกรณีนี้เป็นเรื่องของความคิดเห็นไม่ใช่ความเชื่อ ด้วยเหตุนี้การเข้าถึงข้อมูลยังคงเป็นที่ต้องการ แนวคิดที่ดีกว่าคือการหลีกเลี่ยงโซลูชัน ORM โดยสิ้นเชิง เรียนรู้วิธีการเขียน SQL ที่เหมาะสม
duffymo

39

นี่คือสถานการณ์ที่คุณต้องใช้ตัวเข้าถึงคุณสมบัติ ลองนึกภาพคุณมีคลาสนามธรรมทั่วไปที่มีความสามารถในการนำไปใช้งานมากมายเพื่อสืบทอดเป็นคลาสย่อยที่เป็นรูปธรรม 8 คลาส:

public abstract class Foo<T extends Bar> {

    T oneThing;
    T anotherThing;

    // getters and setters ommited for brevity

    // Lots and lots of implementation regarding oneThing and anotherThing here
 }

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

การเข้าถึงภาคสนามไม่ใช่ตัวเลือกในแอปพลิเคชันที่มีซูเปอร์คลาสนามธรรมทั่วไป


2
สัมผัส ฉันไม่ได้คิดอย่างนั้น ฉันพนันได้เลยว่า Hibernate จะเอา sql ที่บ้าคลั่งออกมาสำหรับสิ่งเหล่านี้
Joseph Lust

8
ปัญหานี้ฟังดูยากที่จะแก้ไขโดยกลไกโดยไม่ต้องใส่คำอธิบายประกอบคุณสมบัติ แต่ฉันไม่เคยเจอกรณีที่ฉันต้องการคลาสนามธรรมทั่วไปที่มีการใช้งานจำนวนมากที่ฉันต้องการคงอยู่ โดยปกติฉันจะสร้างลำดับชั้นของคลาสเพื่อทำให้อ็อบเจ็กต์ของฉันมีความหลากหลาย (ซึ่งทำให้เป็นการแบ่งประเภททั่วไป) ไม่ใช่เพื่อใช้โค้ดซ้ำ และ "การใช้งานจำนวนมาก" มักจะละเมิด SRP อยู่แล้วซึ่งในกรณีนี้ฉันอาจจะย้ายไปเรียนแยกต่างหาก มีตัวอย่างที่เป็นรูปธรรมที่ทำให้กรณีการใช้งานนี้ชัดเจนขึ้นหรือไม่?
Merlyn Morgan-Graham

ตัวอย่างที่เป็นรูปธรรมเพียงอย่างเดียวที่ฉันมีคือแอปพลิเคชันของฉันซึ่งฉันไม่สามารถอธิบายได้ในความคิดเห็น 500 ตัวอักษร ;-)
HDave

3
คุณสามารถใช้abstract T getOneThing()และabstract void setOneThing(T thing)และใช้การเข้าถึงฟิลด์
Arturo Volpe

33

ฉันมักจะชอบและใช้ตัวเข้าถึงคุณสมบัติ:

  • ฉันสามารถเพิ่มตรรกะได้หากจำเป็น (ตามที่กล่าวไว้ในคำตอบที่ยอมรับ)
  • มันช่วยให้ฉันโทรได้foo.getId() โดยไม่ต้องเริ่มต้นพร็อกซี (สำคัญเมื่อใช้ไฮเบอร์เนตจนกว่าHHH-3718จะได้รับการแก้ไข)

ข้อเสียเปรียบ:

  • มันทำให้โค้ดอ่านได้น้อยลงตัวอย่างเช่นคุณต้องเรียกดูทั้งคลาสเพื่อดูว่ามีอยู่แถว@Transientนั้นหรือไม่

แต่ถ้าคุณใช้ผู้ให้บริการ JPA ที่ไม่มี "พร็อกซี" แสดงว่าคุณไม่มีปัญหาหรือที่เรียกว่า
Neil Stockton

13

นั่นขึ้นอยู่กับกรณีเฉพาะจริงๆ - ทั้งสองตัวเลือกมีให้สำหรับเหตุผล IMO เดือดถึงสามกรณี:

  1. setter มีตรรกะบางอย่างที่ไม่ควรดำเนินการในขณะโหลดอินสแตนซ์จากฐานข้อมูล ตัวอย่างเช่นการตรวจสอบค่าบางอย่างเกิดขึ้นใน setter อย่างไรก็ตามข้อมูลที่มาจาก db ควรจะถูกต้อง (มิฉะนั้นจะไม่ไปที่นั่น (:) ในกรณีนี้การเข้าถึงฟิลด์เหมาะสมที่สุด
  2. setter มีตรรกะบางอย่างที่ควรเรียกใช้เสมอแม้ในระหว่างการโหลดอินสแตนซ์จากฐานข้อมูล ตัวอย่างเช่นคุณสมบัติที่กำลังเริ่มต้นถูกใช้ในการคำนวณของฟิลด์ที่คำนวณบางฟิลด์ (เช่นคุณสมบัติ - จำนวนเงินคุณสมบัติที่คำนวณได้ - คุณสมบัติทางการเงินทั้งหมดของอินสแตนซ์เดียวกัน) ในกรณีนี้จำเป็นต้องมีการเข้าถึงคุณสมบัติ
  3. ไม่มีกรณีใด ๆ ข้างต้น - จากนั้นทั้งสองตัวเลือกสามารถใช้ได้เพียงแค่สอดคล้องกัน (ei ถ้าการเข้าถึงฟิลด์เป็นตัวเลือกในสถานการณ์นี้จากนั้นใช้ตลอดเวลาในสถานการณ์ที่คล้ายคลึงกัน)

ฉันเพิ่งเริ่มใช้ Hibernate และกำลังดิ้นรนกับคำถามเดิม ๆ ฉันคิดว่าโพสต์นี้ให้คำตอบที่ชัดเจนที่สุด ขอบคุณ.
Sam Levin

12

ฉันขอแนะนำอย่างยิ่งให้การเข้าถึงฟิลด์และไม่ใช่คำอธิบายประกอบบน getters (การเข้าถึงคุณสมบัติ) หากคุณต้องการทำอะไรเพิ่มเติมใน setters มากกว่าแค่การตั้งค่า (เช่นการเข้ารหัสหรือการคำนวณ)

ปัญหาเกี่ยวกับการเข้าถึงคุณสมบัติคือ setters จะถูกเรียกด้วยเมื่อโหลดอ็อบเจ็กต์ สิ่งนี้ได้ผลดีสำหรับฉันเป็นเวลาหลายเดือนจนกระทั่งเราต้องการแนะนำการเข้ารหัส ในกรณีการใช้งานของเราเราต้องการเข้ารหัสฟิลด์ใน setter และถอดรหัสใน getter ตอนนี้ปัญหาเกี่ยวกับการเข้าถึงคุณสมบัติคือเมื่อ Hibernate โหลดอ็อบเจ็กต์มันก็กำลังเรียก setter เพื่อเติมข้อมูลในฟิลด์และกำลังเข้ารหัสค่าที่เข้ารหัสอีกครั้ง โพสต์นี้ยังกล่าวถึงสิ่งนี้ด้วย: Java Hibernate: ลักษณะการทำงานของชุดคุณสมบัติที่แตกต่างกันขึ้นอยู่กับว่าใครเรียกมัน

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


ใช่ - ฉันพบว่าหากคุณใช้การเข้าถึงคุณสมบัติคุณไม่สามารถทำอะไรในตัวตั้งค่าของคุณได้เลยนอกจากตั้งค่าฟิลด์
HDave

2
+1 ให้ห่างจาก getters / setters ฉันใช้projectlombok.org และซ่อนไม่ให้นักพัฒนา
bhantol

12

ฉันชอบใช้การเข้าถึงช่องด้วยเหตุผลต่อไปนี้:

  1. การเข้าถึงคุณสมบัติอาจนำไปสู่ข้อบกพร่องที่น่ารังเกียจเมื่อใช้ equals / hashCode และการอ้างอิงฟิลด์โดยตรง (ซึ่งตรงข้ามกับ getters) เนื่องจากพร็อกซีจะเริ่มต้นเมื่อเข้าถึง getters เท่านั้นและการเข้าถึงโดยตรงในฟิลด์จะคืนค่า null

  2. เข้าถึงสถานที่ให้บริการคุณจะต้องใส่คำอธิบายประกอบทุกวิธียูทิลิตี้ (เช่น addChild / RemoveChild) @Transientเช่น

  3. ด้วยการเข้าถึงฟิลด์เราสามารถซ่อนฟิลด์ @Version ได้โดยไม่เปิดเผย getter เลย ผู้เริ่มต้นสามารถนำไปสู่การเพิ่มตัวตั้งค่าได้เช่นกันและversionไม่ควรตั้งค่าฟิลด์ด้วยตนเอง (ซึ่งอาจนำไปสู่ปัญหาที่น่ารังเกียจได้) การเพิ่มเวอร์ชันทั้งหมดควรทริกเกอร์ผ่านOPTIMISTIC_FORCE_INCREMENTหรือPESSIMISTIC_FORCE_INCREMENT การล็อกแบบชัดแจ้ง


1.กลยุทธ์การเข้าถึงภาคสนามป้องกันสิ่งนี้ได้อย่างไร? นี่ดูเหมือนจะเป็นข้อผิดพลาดทั่วไปกับพร็อกซีโดยไม่คำนึงถึงรูปแบบการเข้าถึง 2.คุณแน่ใจหรือไม่ว่าควรเป็นยูทิลิตี้ getters เท่านั้น? (แต่ก็เป็นข้อโต้แย้งที่ดีอยู่ดี) 3.การเปิดเผยตัวเข้าถึงสำหรับversionฟิลด์มักมีประโยชน์ในสถานการณ์ที่ใช้ DTO แทนเอนทิตีแยก
Dragan Bozanovic

1. เนื่องจากเมื่อ Proxy เริ่มต้น 2. แน่นอน 100% 3. นั่นคือจุดที่ถูกต้อง
Vlad Mihalcea

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

ไม่การเข้าถึงฟิลด์หมายความว่าไฮเบอร์เนตใช้ฟิลด์ผ่านการสะท้อนเพื่ออ่านแอตทริบิวต์เอนทิตี สำหรับรายละเอียดเพิ่มเติม, ตรวจสอบการเข้าถึงประเภทส่วนในคู่มือการใช้ Hibernate
Vlad Mihalcea

9

ให้ฉันพยายามสรุปเหตุผลที่สำคัญที่สุดในการเลือกการเข้าถึงตามฟิลด์ หากคุณต้องการเจาะลึกมากขึ้นโปรดอ่านบทความนี้ในบล็อกของฉัน: กลยุทธ์การเข้าถึงใน JPA และ Hibernate - การเข้าถึงฟิลด์หรือคุณสมบัติใดดีกว่ากัน

การเข้าถึงตามฟิลด์เป็นตัวเลือกที่ดีกว่า นี่คือ 5 เหตุผลสำหรับมัน:

เหตุผลที่ 1: อ่านโค้ดได้ดีขึ้น

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

เหตุผลที่ 2: ละเว้นเมธอด getter หรือ setter ที่แอปพลิเคชันของคุณไม่ควรเรียก

ข้อดีอีกอย่างของการเข้าถึงตามฟิลด์คือผู้ให้บริการการคงอยู่ของคุณเช่น Hibernate หรือ EclipseLink ไม่ได้ใช้เมธอด getter และ setter ของแอตทริบิวต์เอนทิตีของคุณ นั่นหมายความว่าคุณไม่จำเป็นต้องระบุวิธีการใด ๆ ที่ไม่ควรใช้กับรหัสธุรกิจของคุณ ส่วนใหญ่มักเป็นกรณีของเมธอด setter ของแอตทริบิวต์คีย์หลักที่สร้างขึ้นหรือคอลัมน์เวอร์ชัน ผู้ให้บริการคงอยู่ของคุณจะจัดการค่าของแอตทริบิวต์เหล่านี้และคุณไม่ควรตั้งค่าโดยใช้โปรแกรม

เหตุผลที่ 3: การใช้วิธี getter และ setter ที่ยืดหยุ่น

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

คุณสามารถยกตัวอย่างเช่นการใช้งานที่ห่อสมาคมเสริมหรือแอตทริบิวต์เป็น OptionalJava

เหตุผลที่ 4: ไม่จำเป็นต้องทำเครื่องหมายวิธียูทิลิตี้เป็น @Transient

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

เหตุผลที่ 5: หลีกเลี่ยงข้อบกพร่องเมื่อทำงานกับพร็อกซี

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

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


7

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

นี่เป็นตัวอย่างที่ดีว่าจะทำให้คุณไหม้ได้ที่ไหน: คุณอาจต้องการคำอธิบายประกอบสำหรับตัวตรวจสอบไฮเบอร์เนตและความคงอยู่ในที่เดียวกัน (ไม่ว่าจะเป็นฟิลด์หรือคุณสมบัติ) หากคุณต้องการทดสอบการตรวจสอบความถูกต้องไฮเบอร์เนตของคุณซึ่งมีคำอธิบายประกอบอยู่ในฟิลด์คุณไม่สามารถใช้การเยาะเย้ยเอนทิตีของคุณเพื่อแยกการทดสอบหน่วยของคุณเป็นเพียงเครื่องมือตรวจสอบความถูกต้อง อุ๊ย.


2
นั่นเป็นเหตุผลที่คุณใส่ validator-annotations บน accessors และ persistence-annotations ในช่อง
fishbone

6

ฉันเชื่อว่าการเข้าถึงพร็อพเพอร์ตี้กับการเข้าถึงฟิลด์นั้นแตกต่างกันอย่างมากเมื่อเทียบกับการเริ่มต้นแบบขี้เกียจ

พิจารณาการแมปต่อไปนี้สำหรับถั่วพื้นฐาน 2 ชนิด:

<hibernate-mapping package="org.nkl.model" default-access="field">
  <class name="FieldBean" table="FIELD_BEAN">
    <id name="id">
      <generator class="sequence" />
    </id>
    <property name="message" />
  </class>
</hibernate-mapping>

<hibernate-mapping package="org.nkl.model" default-access="property">
  <class name="PropBean" table="PROP_BEAN">
    <id name="id">
      <generator class="sequence" />
    </id>
    <property name="message" />
  </class>
</hibernate-mapping>

และการทดสอบหน่วยต่อไปนี้:

@Test
public void testFieldBean() {
    Session session = sessionFactory.openSession();
    Transaction tx = session.beginTransaction();
    FieldBean fb = new FieldBean("field");
    Long id = (Long) session.save(fb);
    tx.commit();
    session.close();

    session = sessionFactory.openSession();
    tx = session.beginTransaction();
    fb = (FieldBean) session.load(FieldBean.class, id);
    System.out.println(fb.getId());
    tx.commit();
    session.close();
}

@Test
public void testPropBean() {
    Session session = sessionFactory.openSession();
    Transaction tx = session.beginTransaction();
    PropBean pb = new PropBean("prop");
    Long id = (Long) session.save(pb);
    tx.commit();
    session.close();

    session = sessionFactory.openSession();
    tx = session.beginTransaction();
    pb = (PropBean) session.load(PropBean.class, id);
    System.out.println(pb.getId());
    tx.commit();
    session.close();
}

คุณจะเห็นความแตกต่างเล็กน้อยในการเลือกที่ต้องการ:

Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    insert 
    into
        FIELD_BEAN
        (message, id) 
    values
        (?, ?)
Hibernate: 
    select
        fieldbean0_.id as id1_0_,
        fieldbean0_.message as message1_0_ 
    from
        FIELD_BEAN fieldbean0_ 
    where
        fieldbean0_.id=?
0
Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    insert 
    into
        PROP_BEAN
        (message, id) 
    values
        (?, ?)
1

นั่นคือการโทรfb.getId()ต้องมีการเลือกในขณะที่pb.getId()ไม่ได้


นี่มันตลก! :) แต่มันเป็นพฤติกรรมเฉพาะการใช้งานฉันแน่ใจ I
Vladimir Dyuzhev

ใช่ฉันเดาว่านี่เป็นเพราะความจริงที่ว่ามีเพียงคลาสถาวรเท่านั้นที่เป็นเครื่องมือ อย่างไรก็ตามเป็นเรื่องเล็กน้อยเนื่องจากฟิลด์ id มักเป็นฟิลด์เดียวที่ไม่มีมูลค่าทางธุรกิจและไม่ต้องการตัวเข้าถึงใด ๆ
Maurice Perry

3

โดยค่าเริ่มต้นผู้ให้บริการ JPA จะเข้าถึงค่าของฟิลด์เอนทิตีและแม็พฟิลด์เหล่านั้นกับคอลัมน์ฐานข้อมูลโดยใช้วิธีการเข้าถึงคุณสมบัติ JavaBean (getter) และ mutator (setter) ของเอนทิตี ด้วยเหตุนี้ชื่อและประเภทของฟิลด์ส่วนตัวในเอนทิตีจึงไม่สำคัญกับ JPA แต่ JPA จะดูเฉพาะชื่อและประเภทการส่งคืนของตัวเข้าถึงคุณสมบัติ JavaBean คุณสามารถแก้ไขสิ่งนี้ได้โดยใช้@javax.persistence.Accessคำอธิบายประกอบซึ่งช่วยให้คุณระบุวิธีการเข้าถึงอย่างชัดเจนที่ผู้ให้บริการ JPA ควรใช้

@Entity
@Access(AccessType.FIELD)
public class SomeEntity implements Serializable
{
...
}

ตัวเลือกที่ใช้ได้สำหรับ AccessType enum คือ PROPERTY (ค่าเริ่มต้น) และ FIELD ด้วย PROPERTY ผู้ให้บริการรับและตั้งค่าฟิลด์โดยใช้วิธีคุณสมบัติ JavaBean FIELD ทำให้ผู้ให้บริการรับและตั้งค่าฟิลด์โดยใช้ฟิลด์อินสแตนซ์ ตามแนวทางปฏิบัติที่ดีที่สุดคุณควรยึดติดกับค่าเริ่มต้นและใช้คุณสมบัติ JavaBean เว้นแต่คุณจะมีเหตุผลที่น่าสนใจที่จะทำอย่างอื่น

คุณสามารถใส่คำอธิบายประกอบคุณสมบัติเหล่านี้ในฟิลด์ส่วนตัวหรือวิธีการเข้าถึงสาธารณะ หากคุณใช้AccessType.PROPERTY(ค่าเริ่มต้น) และใส่คำอธิบายประกอบฟิลด์ส่วนตัวแทนตัวเข้าถึง JavaBean ชื่อฟิลด์ต้องตรงกับชื่อคุณสมบัติ JavaBean อย่างไรก็ตามชื่อไม่จำเป็นต้องตรงกันหากคุณใส่คำอธิบายประกอบตัวเข้าถึง JavaBean ในทำนองเดียวกันหากคุณใช้AccessType.FIELDและใส่คำอธิบายประกอบตัวเข้าถึง JavaBean แทนฟิลด์ชื่อฟิลด์ต้องตรงกับชื่อคุณสมบัติ JavaBean ด้วย ในกรณีนี้พวกเขาไม่จำเป็นต้องจับคู่หากคุณใส่คำอธิบายประกอบในช่อง มันเป็นเรื่องที่ดีที่สุดที่จะเพียงแค่มีความสอดคล้องและอธิบาย accessors JavaBean สำหรับและฟิลด์สำหรับAccessType.PROPERTY AccessType.FIELD

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


2

เรามีหรือยัง

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


2

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

ดังนั้นฉันชอบที่จะมีคอลเลกชันเป็นฟิลด์ที่ได้รับการป้องกันซึ่งเริ่มต้นเพื่อการใช้งานที่ว่างเปล่าในตัวสร้างเริ่มต้นและแสดงเฉพาะ getters ของพวกเขา จากนั้นการดำเนินงานการจัดการเพียงชอบclear(), remove(), removeAll()ฯลฯ เป็นไปได้ว่าจะไม่ทำให้ Hibernate ไม่รู้ของการเปลี่ยนแปลง


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

2

ฉันชอบฟิลด์มากกว่า แต่ฉันเจอสถานการณ์หนึ่งที่ดูเหมือนจะบังคับให้ฉันต้องวางคำอธิบายประกอบบน getters

ด้วยการใช้งาน Hibernate JPA @Embeddedดูเหมือนจะไม่ทำงานในฟิลด์ ดังนั้นจึงต้องดำเนินการต่อไป และเมื่อคุณใส่สิ่งนั้นลงบน getter แล้ว@Columnคำอธิบายประกอบต่างๆก็ต้องไปอยู่บนgetter ด้วย (ฉันคิดว่า Hibernate ไม่ต้องการสนามผสมและตัวรับที่นี่) และเมื่อคุณใส่@Columngetters ในคลาสเดียวแล้วมันก็สมเหตุสมผลที่จะทำเช่นนั้นตลอดไป


2

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

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

เวอร์ชันไฮเบอร์เนตที่ใช้ในแอพนี้:

<!-- hibernate -->
<hibernate-core.version>3.3.2.GA</hibernate-core.version>
<hibernate-annotations.version>3.4.0.GA</hibernate-annotations.version>
<hibernate-commons-annotations.version>3.1.0.GA</hibernate-commons-annotations.version>
<hibernate-entitymanager.version>3.4.0.GA</hibernate-entitymanager.version>

2

คุณควรเลือกการเข้าถึงผ่านฟิลด์มากกว่าการเข้าถึงผ่านคุณสมบัติ ด้วยช่องคุณสามารถ จำกัด ข้อมูลที่ส่งและรับได้ ด้วยคุณสมบัติคุณสามารถส่งข้อมูลได้มากขึ้นในฐานะโฮสต์และตั้งค่านิกาย G (ซึ่งโรงงานตั้งค่าคุณสมบัติส่วนใหญ่ทั้งหมด)


1

ฉันมีคำถามเดียวกันเกี่ยวกับ accesstype ในจำศีลและพบคำตอบบางอย่างที่นี่



1

เราสร้างเอนทิตี bean และใช้คำอธิบายประกอบ getter ปัญหาที่เราพบคือเอนทิตีบางรายการมีกฎที่ซับซ้อนสำหรับคุณสมบัติบางอย่างเกี่ยวกับเวลาที่สามารถอัปเดตได้ วิธีแก้ปัญหาคือต้องมีตรรกะทางธุรกิจบางอย่างในตัวเซ็ตเตอร์แต่ละตัวที่กำหนดว่าค่าจริงเปลี่ยนแปลงหรือไม่และถ้าเป็นเช่นนั้นควรอนุญาตให้เปลี่ยนแปลงหรือไม่ แน่นอนว่าไฮเบอร์เนตสามารถตั้งค่าคุณสมบัติได้เสมอดังนั้นเราจึงลงเอยด้วยเซ็ตเตอร์สองกลุ่ม ค่อนข้างน่าเกลียด.

เมื่ออ่านโพสต์ก่อนหน้านี้ฉันยังเห็นว่าการอ้างอิงคุณสมบัติจากภายในเอนทิตีอาจทำให้เกิดปัญหากับคอลเลกชันที่ไม่โหลด

สิ่งที่สำคัญที่สุดคือฉันจะเอนเอียงไปที่การใส่คำอธิบายประกอบฟิลด์ในอนาคต


0

โดยปกติถั่วเป็น POJO ดังนั้นพวกเขาจึงมี accessors อยู่แล้ว

ดังนั้นคำถามจึงไม่ใช่ "อันไหนดีกว่า" แต่เป็นเพียง "เมื่อใดจึงจะใช้การเข้าถึงภาคสนาม" และคำตอบก็คือ "เมื่อคุณไม่ต้องการตัวตั้งรับ / ผู้ลงสนาม!"


4
ปัญหาคือคุณไม่สามารถผสมผสานการเข้าถึงฟิลด์และการเข้าถึงคุณสมบัติใน POJO - คุณต้องเลือกอย่างใดอย่างหนึ่ง
Martin OConnor

จริงๆ? ฉันคงลืมมันไปแล้ว อย่างไรก็ตามฉันใช้ POJO เป็นตัวเข้าถึง d เสมอ
Vladimir Dyuzhev

โปรดทราบว่าด้วย JPA 2.0 (ซึ่งไม่ได้เกิดขึ้นเมื่อถามคำถามนี้) ตอนนี้คุณสามารถผสมประเภทการเข้าถึงโดยใช้คำอธิบายประกอบ @AccessType
mtpettyp

0

ฉันคิดถึงสิ่งนี้และฉันเลือกวิธีการเข้าถึง

ทำไม?

เนื่องจากฟิลด์และแอคเซสซอร์เมทเหมือนกัน แต่ถ้าในภายหลังฉันต้องการตรรกะบางอย่างในฟิลด์โหลดฉันบันทึกการย้ายคำอธิบายประกอบทั้งหมดที่วางในฟิลด์

ความนับถือ

กรูฮาร์ต


0

เพื่อให้ชั้นเรียนของคุณสะอาดขึ้นให้ใส่คำอธิบายประกอบลงในช่องจากนั้นใช้ @Access (AccessType.PROPERTY)


0

ทั้งสอง:

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

https://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#entity-mapping


0

AccessType.PROPERTY: การใช้งานความคงอยู่ของ EJB จะโหลดสถานะในคลาสของคุณผ่านเมธอด "setter" ของ JavaBean และดึงสถานะจากคลาสของคุณโดยใช้เมธอด JavaBean "getter" นี่คือค่าเริ่มต้น

AccessType.FIELD: สถานะถูกโหลดและดึงข้อมูลโดยตรงจากฟิลด์คลาสของคุณ คุณไม่จำเป็นต้องเขียน JavaBean "getters" และ "setters"

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