2012-06-15 48 views
9

Tình hình

Tôi có một Entity với một DiscriminatorColumn, cấu hình cho thừa kế bảng duy nhất:DiscriminatorColumn như một phần của khóa chính/id

@Entity 
@Inheritance(strategy=InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name="TYPE") 
public class ContainerAssignment{ 
... 
} 

'ContainerAssignment' có một tham chiếu đến một Entity:

@JoinColumn(name="CONTAINER_ID") 
private Container container; 

Vùng chứa có thể có một ContainerAssignment của mỗi TYPE. Điều này có nghĩa là khóa chính của bảng ContainerAssignment được xác định bởi CONTAINER_IDTYPE.

ContainerAssignment có một số lớp con, ví dụ:

@Entity 
@DiscriminatorValue("SOME_TYPE") 
public class SomeTypeOfContainerAssignment extends ContainerAssignment{ 
... 
} 

Sẽ chỉ có một ví dụ SomeTypeOfContainerAssignment cho CONTAINER_ID nhất định.

Vấn đề

Nếu tôi xác định JPA @Id như chỉ là container trên bàn ContainerAssignment, tôi có thể làm entityManager.find(SomeTypeOfContainerAssignment.class, containerId), đó là rất tốt. Điều này chạy một cái gì đó dọc theo dòng SELECT * FROM CONTAINER_ASSIGNMENT WHERE CONTAINER_ID = 1 AND TYPE = 'SOME_TYPE';. Nó biết nó cần kiểm tra TYPE ở đây, vì chú thích @DiscriminatorValue("SOME_TYPE") trên thực thể.

Tuy nhiên, điều này có nghĩa là các tham chiếu ngược từ vùng chứa đến vùng chứaAssignment bị hỏng vì Vùng chứa không thực sự là khóa chính. Ví dụ: nếu Vùng chứa có một số @OneToOne(mappedBy=container) private SomeTypeOfContainerAssignment assignment;, khi bạn đọc trong một vùng chứa, nó sẽ đọc trong phần gán bởi một cái gì đó như SELECT * FROM CONTAINER_ASSIGNMENT WHERE CONTAINER_ID = 1; mà không cần kiểm tra loại. Điều này mang lại cho nó tất cả các nhiệm vụ cho một container, và sau đó nó chọn một trong những dường như ngẫu nhiên, có khả năng loại sai, trong trường hợp đó, nó ném một ngoại lệ.

Nếu thay vào đó, tôi xác định JPA @Id của ContainerAssignment làm id tổng hợp bằng cách sử dụng vùng chứa và loại, tham chiếu đến các lớp con của công việc của ContainerAssignment.

Tuy nhiên, tôi không thể làm entityManager.find(SomeTypeOfContainerAssignment.class, containerId), vì containerId không phải là id. Tôi phải làm entityManager.find(SomeTypeOfContainerAssignment.class, new MyPk(containerId, "SOME_TYPE")), dường như đánh bại điểm @DiscriminatorValue("SOME_TYPE"). Tôi cũng có thể chỉ sử dụng một thực thể ContainerAssignment duy nhất nếu tôi phải chỉ định kiểu trên tìm anyway.

Câu hỏi

Có cách nào để có tài liệu tham khảo làm việc để phụ lớp học của một Entity bảng thừa kế duy nhất mà khóa chính trên bảng là tổng hợp trên cột phân biệt, trong khi cũng có khả năng EntityManager.find bởi chỉ (các) phần của khóa chính không phải là người phân biệt đối xử?

Trả lời

0

Nếu Container có OneToOne hai chiều với SomeTypeOfContainerAssignment, kéo dài ContainerAssignment, sau đó lĩnh vực thùng chứa nên không được xác định và ánh xạ trong ContainerAssignment, nhưng trong SomeTypeOfContainerAssignment:

public class Container { 
    @Id 
    private Long id; 

    @OneToOne(mappedBy = "container") 
    private SomeTypeOfContainerAssignment someTypeOfContainerAssignment; 
} 

public class ContainerAssignment { 
    @Id 
    private Long id; 
} 

public class SomeTypeOfContainerAssignment extends ContainerAssignment { 
    @OneToOne 
    private Container container; 
} 

Nếu tất cả các loại bài tập chứa có như vậy liên kết OneToOne với COntainer, bạn có thể xác định Vùng chứa là

public abstract class ContainerAssignment { 
    @Id 
    private Long id; 

    public abstract Container getContainer(); 
    public abstract void setContainer(Container container); 
} 

Thành thật mà nói, tôi không biết liệu bạn có được phép sử dụng cùng một jo hay không trong cột trong bảng để ánh xạ các trường @OneToOne container của mỗi lớp con.

Tôi nghĩ đây là điều tốt nhất bạn có thể có. Nếu bạn đặt trường vùng chứa trong lớp cơ sở, thì bạn phải xác định liên kết là liên kết OneToMany/ManyToOne, vì đó là kết quả thực sự của nó.

Tôi không nghĩ những gì bạn muốn làm là có thể, và tôi sẽ không lộn xộn với PK tổng hợp, vì họ đang chán nản vì lý do tốt, và một cơn ác mộng để sử dụng.

+1

Cảm ơn câu trả lời của bạn, nhưng nó không thực sự giải quyết được câu hỏi - có cách nào để đặt loại 'DiscriminatorColumn' bên trong PK không? Tôi đồng ý rằng một PK nhân tạo là lựa chọn tốt nhất, nhưng trong nhiều hệ thống, chúng tôi không thể kiểm soát lược đồ và phải đối phó với các PK tổng hợp. –

+0

Cũng lưu ý rằng KHÔNG BAO GIỜ sử dụng các phím tổng hợp chính là một mẫu chống, theo sách Bill Karwin. – atorres

0

Tôi sẽ giả định rằng khóa chính kết hợp của ContainerAssignment đang hoạt động tốt (tôi thực sự nghĩ rằng nó có thể phụ thuộc vào thực thi JPA!), Và tất cả những gì bạn vẫn phiền là cuộc gọi gây phiền toái cho thực thểManager.find và PK instantiation.

Giải pháp của tôi là xác định các phương thức tìm độc lập với API JPA. Đừng tự khóa mình với JPA. Cách đơn giản nhất là chỉ định nghĩa một công cụ tìm tĩnh ở lớp miền của bạn (hoặc, xác định một lớp khác chỉ với các trình tìm kiếm, nếu bạn muốn giữ miền tách riêng làm JPA. Đào tại IoC để biết cách thực hiện điều đó).

Tại ContainerAssignment (hoặc lớp tìm bạn):

public static <T extends ContainerAssignment> T findByPK(EntityManager manager,Class<T> type,long id) { 
    DiscriminatorValue val = type.getAnnotation(DiscriminatorValue.class); // this is not optimal...can be cached... 
    return (T) manager.find(type, new MyPk(containerId, val.getValue())); 
} 

Tại mã của bạn:

SomeTypeOfContainerAssignment ca = ContainerAssignment.findByPK(entityManager,SomeTypeOfContainerAssignment.class,containerId); 

Chú ý rằng việc loại một phần của PK có nghĩa là bạn có thể có hai trường hợp ContainerAssignment của biệt các loại có cùng id. Bạn sẽ cần một truy vấn để lấy ContainerAssignment nếu bạn không biết loại của nó. Tuy nhiên, nếu id của bạn được tạo từ một chuỗi, bạn có thể chỉ cần viết một phương thức tìm kiếm khác ẩn các cuộc gọi bên trong đến khung thực thể, trả về kết quả đầu tiên của resultset.