Tôi nên sử dụng chú thích nào: @IdClass hoặc @EmbeddedId


128

Đặc tả JPA(API liên tục Java) có 2 cách khác nhau để chỉ định các khóa tổng hợp thực thể: @IdClass@EmbeddedId.

Tôi đang sử dụng cả hai chú thích trên các thực thể được ánh xạ của mình, nhưng hóa ra nó là một mớ hỗn độn lớn đối với những người không quen thuộc lắm JPA.

Tôi muốn chỉ áp dụng một cách để xác định khóa tổng hợp. Cái nào thực sự tốt nhất? Tại sao?

Câu trả lời:


86

Tôi cho rằng điều đó @EmbeddedIdcó thể dài dòng hơn bởi vì @IdClassbạn không thể truy cập toàn bộ đối tượng khóa chính bằng bất kỳ toán tử truy cập trường nào. Sử dụng @EmbeddedIdbạn có thể làm như thế này:

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

Điều này đưa ra một khái niệm rõ ràng về các trường tạo ra khóa tổng hợp bởi vì tất cả chúng được tổng hợp trong một lớp được truy cập qua máng một toán tử truy cập trường.

Một điểm khác biệt với @IdClass@EmbeddedIdlà khi viết HQL:

Với @IdClassbạn viết:

chọn e.name từ Nhân viên e

và với @EmbeddedIdbạn phải viết:

chọn e.employeeId.name từ Nhân viên e

Bạn phải viết thêm văn bản cho cùng một truy vấn. Một số người có thể lập luận rằng điều này khác với một ngôn ngữ tự nhiên hơn như ngôn ngữ được quảng bá IdClass. Nhưng hầu hết các lần hiểu ngay từ truy vấn rằng một trường nhất định là một phần của khóa tổng hợp là trợ giúp vô giá.


10
Mặc dù tôi đồng ý với lời giải thích được đưa ra ở trên, tôi cũng muốn thêm một trường hợp sử dụng duy nhất cho @IdClassdù tôi thích @EmbeddedIdtrong hầu hết các tình huống (Phải biết điều này từ một phiên của Antonio Goncalves. Ông đề nghị chúng ta có thể sử dụng @IdClasstrường hợp nào trong trường hợp tổng hợp lớp khóa không thể truy cập hoặc đến từ một mô-đun hoặc mã kế thừa khác mà chúng tôi không thể thêm chú thích. Trong các kịch bản đó @IdClasssẽ cho chúng tôi một cách của chúng tôi.
Gaurav Rawat

1
Tôi nghĩ rằng có thể các trường hợp sử dụng @IdClasschú thích do @Gaurav đưa ra là lý do mà đặc tả JPA liệt kê cả hai phương pháp tạo khóa tổng hợp .. @IdClass@EmbeddidId
kapad

20

Có ba chiến lược để sử dụng khóa chính ghép:

  • Đánh dấu nó là @Embeddablevà thêm vào lớp thực thể của bạn một thuộc tính bình thường cho nó, được đánh dấu bằng @Id.
  • Thêm vào lớp thực thể của bạn một thuộc tính bình thường cho nó, được đánh dấu bằng @EmbeddedId.
  • Thêm thuộc tính vào lớp thực thể của bạn cho tất cả các trường của nó, đánh dấu chúng @Idvà đánh dấu lớp thực thể của bạn @IdClass, cung cấp lớp của lớp khóa chính của bạn.

Việc sử dụng @Idvới một lớp được đánh dấu @Embeddablelà cách tiếp cận tự nhiên nhất. Các @Embeddablethẻ có thể được sử dụng cho các giá trị nhúng chính phi chính anyway. Nó cho phép bạn coi khóa chính ghép là một thuộc tính duy nhất và nó cho phép sử dụng lại @Embeddablelớp trong các bảng khác.

Cách tiếp cận tự nhiên nhất tiếp theo là sử dụng @EmbeddedIdthẻ. Ở đây, lớp khóa chính không thể được sử dụng trong các bảng khác vì nó không phải là một @Embeddablethực thể, nhưng nó cho phép chúng ta coi khóa là một thuộc tính duy nhất của một số lớp.

Cuối cùng, việc sử dụng các chú thích @IdClass@Idcho phép chúng ta ánh xạ lớp khóa chính ghép bằng cách sử dụng các thuộc tính của chính thực thể tương ứng với tên của các thuộc tính trong lớp khóa chính. Các tên phải tương ứng (không có cơ chế ghi đè này) và lớp khóa chính phải tôn trọng các nghĩa vụ giống như với hai kỹ thuật còn lại. Ưu điểm duy nhất của cách tiếp cận này là khả năng của nó để ẩn giấu việc sử dụng lớp khóa chính từ giao diện của thực thể kèm theo. Các @IdClasschú thích lấy một tham số giá trị của loại Class, mà cần phải có các lớp học để được sử dụng làm khóa chính hợp chất. Các trường tương ứng với các thuộc tính của lớp khóa chính được sử dụng đều phải được chú thích bằng @Id.

Tham khảo: http://www.apress.com/us/book/9781430228509


17

Tôi phát hiện ra một trường hợp mà tôi phải sử dụng EmbeddedId thay vì IdClass. Trong kịch bản này, có một bảng tham gia có các cột bổ sung được xác định. Tôi đã cố gắng giải quyết vấn đề này bằng IdClass để thể hiện khóa của một thực thể thể hiện rõ ràng các hàng trong bảng tham gia. Tôi không thể làm cho nó hoạt động theo cách này. Rất may "Java Persistence With Hibernate" có một phần dành riêng cho chủ đề này. Một giải pháp được đề xuất rất giống với tôi nhưng nó đã sử dụng EmbeddedId thay thế. Tôi đã mô hình hóa các đối tượng của mình sau khi những thứ trong cuốn sách hiện hành xử chính xác.


13

Theo như tôi biết nếu PK tổng hợp của bạn có chứa FK thì việc sử dụng dễ dàng và đơn giản hơn @IdClass

Với @EmbeddedIdbạn phải xác định bản đồ cho cột FK của bạn hai lần, onece trong @Embeddedablevà một lần cho là tức là @ManyToOnenơi @ManyToOnecó được read-only ( @PrimaryKeyJoinColumn) bởi vì bạn không thể có một tập hợp các cột trong hai biến (xung đột có thể).
Vì vậy, bạn phải đặt FK của bạn bằng cách sử dụng loại đơn giản @Embeddedable.

Trên trang khác sử dụng @IdClasstình huống này có thể được xử lý dễ dàng hơn nhiều như được hiển thị trong Khóa chính thông qua Mối quan hệ OneToOne và ManyToOne :

Ví dụ chú thích JPA 2.0 ManyToOne id

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

    @Id
    private String type;

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

Ví dụ lớp id 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
Chỉ cần đừng quên thêm getters vào lớp PK của bạn
Sonata

@Sonata tại sao chúng ta cần các getters? Tôi đã thử nó mà không có bất kỳ getters / setters nào và nó hoạt động tốt
xagaffar

Cảm ơn ví dụ về lớp id! Mặc dù cuối cùng tôi cũng cần phải thực hiện Nối tiếp. Cũng có thể thêm getters và setters, đặc biệt là nếu IDE của bạn có thể tự động tạo chúng.
Starwarswii

8

Tôi nghĩ lợi thế chính là chúng ta có thể sử dụng @GeneratedValueid khi sử dụng @IdClass? Tôi chắc chắn chúng ta không thể sử dụng @GeneratedValuecho @EmbeddedId.


1
Có phải là không thể sử dụng @GeneratedValue trong Embeddedid ??
Kayser

1
Tôi đã sử dụng thành công với EmbeddedId nhưng rõ ràng sự hỗ trợ của nó thay đổi theo DB. Điều này cũng đúng về việc sử dụng nó với IdClass. Thông số kỹ thuật cho biết: "Chú thích GeneratedValue chỉ có thể được sử dụng cho các khóa chính đơn giản (nghĩa là không tổng hợp)."
BPS

4

Khóa tổng hợp không được có @Idtài sản khi @EmbeddedIdđược sử dụng.


1

Với EmbeddedId, bạn có thể sử dụng mệnh đề IN trong HQL, ví dụ: FROM Entity WHERE id IN :idstrong đó id là EmbeddedId trong khi thật khó để đạt được kết quả tương tự với IdClass bạn sẽ muốn làm một cái gì đó nhưFROM Entity WHERE idPartA = :idPartA0 AND idPartB = :idPartB0 .... OR idPartA = :idPartAN AND idPartB = :idPartBN

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.