ฉันควรใช้คำอธิบายประกอบแบบใด: @IdClass หรือ @EmbeddedId


129

ข้อกำหนดJPA(Java Persistence API) มี 2 วิธีในการระบุคีย์คอมโพสิตของเอนทิตี: @IdClassและ@EmbeddedId.

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

ฉันต้องการใช้วิธีเดียวในการระบุคีย์ผสมเท่านั้น อันไหนดีที่สุด? ทำไม?

คำตอบ:


87

ฉันคิดว่านั่น@EmbeddedIdอาจเป็นรายละเอียดมากกว่าเนื่องจาก@IdClassคุณไม่สามารถเข้าถึงวัตถุคีย์หลักทั้งหมดโดยใช้ตัวดำเนินการเข้าถึงฟิลด์ใด ๆ โดยใช้สิ่งที่@EmbeddedIdคุณสามารถทำได้ดังนี้:

@Embeddable class EmployeeId { name, dataOfBirth }
@Entity class Employee {
  @EmbeddedId EmployeeId employeeId;
  ...
}

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

ความแตกต่างอีกด้วย@IdClassและ@EmbeddedIdเมื่อมันมาถึงการเขียน HQL:

กับ@IdClassคุณเขียน:

เลือก e.name จาก Employee e

และ@EmbeddedIdคุณต้องเขียน:

เลือก e.employeeId.name จาก Employee e

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


10
แม้ว่าฉันจะเห็นด้วยกับคำอธิบายที่ให้ไว้ข้างต้น แต่ฉันก็ต้องการเพิ่มกรณีการใช้งานที่ไม่ซ้ำใคร@IdClassแม้ว่าฉันจะชอบ@EmbeddedIdในสถานการณ์ส่วนใหญ่ก็ตาม(เรียนรู้สิ่งนี้จากเซสชันของ Antonio Goncalves สิ่งที่เขาแนะนำคือเราสามารถใช้@IdClassในกรณีที่มีการผสม ไม่สามารถเข้าถึงคลาสคีย์หรือมาจากโมดูลอื่นหรือรหัสเดิมซึ่งเราไม่สามารถเพิ่มคำอธิบายประกอบได้ในสถานการณ์เหล่านั้น@IdClassจะให้วิธีของเรา
Gaurav Rawat

1
ฉันคิดว่าเป็นไปได้ว่ากรณีการใช้@IdClassคำอธิบายประกอบที่กำหนดโดย @Gaurav เป็นเหตุผลที่ข้อกำหนดของ JPA แสดงทั้งสองวิธีในการสร้างคีย์ผสม .. @IdClassและ@EmbeddidId
kapad

20

มีสามกลยุทธ์ในการใช้คีย์หลักแบบผสม:

  • ทำเครื่องหมายเป็นและเพิ่มระดับนิติบุคคลของสถานที่ให้บริการตามปกติสำหรับมันที่มีเครื่องหมาย@Embeddable@Id
  • @EmbeddedIdเพิ่มในระดับนิติบุคคลของสถานที่ให้บริการตามปกติสำหรับมันที่มีเครื่องหมาย
  • เพิ่มคุณสมบัติให้กับคลาสเอนทิตีของคุณสำหรับฟิลด์ทั้งหมดทำเครื่องหมายด้วย@Idและทำเครื่องหมายคลาสเอนทิตีของคุณด้วย@IdClassการจัดหาคลาสของคลาสคีย์หลักของคุณ

การใช้@Idคลาสที่ทำเครื่องหมายว่า@Embeddableเป็นแนวทางที่เป็นธรรมชาติที่สุด @Embeddableแท็กสามารถใช้สำหรับค่าฝังที่สำคัญไม่ใช่หลักอยู่แล้ว ช่วยให้คุณปฏิบัติต่อคีย์หลักแบบผสมเป็นคุณสมบัติเดียวและอนุญาตให้ใช้@Embeddableคลาสซ้ำในตารางอื่นได้

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

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

อ้างอิง: http://www.apress.com/us/book/9781430228509


17

ฉันค้นพบอินสแตนซ์ที่ฉันต้องใช้ EmbeddedId แทน IdClass ในสถานการณ์นี้มีตารางการเข้าร่วมที่มีการกำหนดคอลัมน์เพิ่มเติม ฉันพยายามแก้ปัญหานี้โดยใช้ IdClass เพื่อแสดงคีย์ของเอนทิตีที่แสดงถึงแถวในตารางการรวมอย่างชัดเจน ฉันไม่สามารถทำให้มันทำงานในลักษณะนี้ได้ โชคดีที่ "Java Persistence With Hibernate" มีส่วนเฉพาะสำหรับหัวข้อนี้ โซลูชันหนึ่งที่เสนอนั้นคล้ายกับของฉันมาก แต่ใช้ EmbeddedId แทน ฉันจำลองวัตถุของฉันตามสิ่งที่อยู่ในหนังสือตอนนี้มันทำงานได้อย่างถูกต้อง


13

เท่าที่ฉันรู้ว่า PK คอมโพสิตของคุณมี FK หรือไม่มันใช้งานง่ายและตรงไปตรงมามากกว่า @IdClass

เมื่อ@EmbeddedIdคุณต้องกำหนดการแมปสำหรับคอลัมน์ FK ของคุณสองครั้งหนึ่งใน@Embeddedableหนึ่งและอีกครั้งสำหรับเช่น@ManyToOneที่@ManyToOneต้องอ่านอย่างเดียว ( @PrimaryKeyJoinColumn) เนื่องจากคุณไม่สามารถตั้งค่าคอลัมน์เดียวในสองตัวแปร (อาจมีความขัดแย้ง)
ดังนั้นคุณต้องตั้งค่า FK ของคุณโดยใช้ประเภทง่ายๆใน@Embeddedable.

ในไซต์อื่นที่ใช้@IdClassสถานการณ์นี้สามารถจัดการได้ง่ายกว่ามากดังแสดงในคีย์หลักผ่านความสัมพันธ์ OneToOne และ ManyToOne :

ตัวอย่างคำอธิบายประกอบรหัส JPA 2.0 ManyToOne

...
@Entity
@IdClass(PhonePK.class)
public class Phone {

    @Id
    private String type;

    @ManyToOne
    @Id
    @JoinColumn(name="OWNER_ID", referencedColumnName="EMP_ID")
    private Employee owner;
    ...
}

ตัวอย่างคลาสรหัส JPA 2.0

...
public class PhonePK {
    private String type;
    private long owner;

    public PhonePK() {}

    public PhonePK(String type, long owner) {
        this.type = type;
        this.owner = owner;
    }

    public boolean equals(Object object) {
        if (object instanceof PhonePK) {
            PhonePK pk = (PhonePK)object;
            return type.equals(pk.type) && owner == pk.owner;
        } else {
            return false;
        }
    }

    public int hashCode() {
        return type.hashCode() + owner;
    }
}

1
อย่าลืมเพิ่ม getters ในคลาส PK ของคุณ
Sonata

@ Sonata ทำไมเราถึงต้องการ getters? ฉันลองแล้วโดยไม่ต้องใช้ getters / setters และใช้งานได้ดี
xagaffar

ขอบคุณสำหรับตัวอย่างคลาส id! แม้ว่าฉันจะต้องใช้ Serializable เช่นกัน นอกจากนี้ยังอาจเพิ่ม getters และ setters โดยเฉพาะอย่างยิ่งหาก IDE ของคุณสามารถสร้างได้โดยอัตโนมัติ
Starwarswii

8

ฉันคิดว่าข้อได้เปรียบหลักคือเราสามารถใช้@GeneratedValueสำหรับ id เมื่อใช้@IdClass? ผมมั่นใจว่าเราจะไม่สามารถใช้สำหรับ@GeneratedValue@EmbeddedId


1
ไม่สามารถใช้ @GeneratedValue ใน Embeddedid ได้หรือไม่?
Kayser

1
ฉันใช้มันสำเร็จแล้วกับ EmbeddedId แต่ดูเหมือนว่าการสนับสนุนจะแตกต่างกันไปตาม DB นี่เป็นเรื่องจริงเกี่ยวกับการใช้กับ IdClass ข้อมูลจำเพาะระบุว่า: "คำอธิบายประกอบ GeneratedValue สามารถใช้ได้เฉพาะกับคีย์หลักแบบธรรมดา (เช่นไม่ใช่คอมโพสิต) เท่านั้น"
BPS

4

คีย์คอมโพสิตต้องไม่มี@Idคุณสมบัติเมื่อ@EmbeddedIdถูกใช้


1

ด้วย EmbeddedId คุณสามารถใช้ประโยค IN ใน HQL ได้ตัวอย่างเช่นFROM Entity WHERE id IN :idsโดยที่ id คือ EmbeddedId ในขณะที่การได้รับผลลัพธ์เดียวกันกับ IdClass คุณจะต้องทำสิ่งต่างๆเช่นFROM Entity WHERE idPartA = :idPartA0 AND idPartB = :idPartB0 .... OR idPartA = :idPartAN AND idPartB = :idPartBN

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