2010-10-26 22 views
16

Tôi gặp sự cố khi xóa các phần tử khỏi danh sách được ánh xạ như được mô tả ở trên. Dưới đây là các bản đồ:Vi phạm hạn chế trong bản đồ OneToMany Hibernate một chiều với JoinTable và OrderColumn khi xóa các phần tử

 
@Entity 
@Table(name = "foo") 
class Foo { 

    private List bars; 

    @OneToMany 
    @OrderColumn(name = "order_index") 
    @JoinTable(name = "foo_bar_map", joinColumns = @JoinColumn(name = "foo_id"), inverseJoinColumns = @JoinColumn(name = "bar_id")) 
    @Fetch(FetchMode.SUBSELECT) 
    public List getBars() { 
     return bars; 
    } 
} 

Chèn Bar-trường và tiết kiệm Foo hoạt động tốt, nhưng khi tôi loại bỏ một phần tử từ danh sách và lưu lại, hạn chế duy nhất trên bar_id trong bảng mapping bị vi phạm. SQL-câu sau đây được cấp bởi Hibernate, và những trông khá kỳ quặc:

 
LOG: execute : delete from foo_bar_map where foo_id=$1 and order_index=$2 
DETAIL: parameters: $1 = '4', $2 = '6' 
LOG: execute S_5: update foo_bar_map set bar_id=$1 where foo_id=$2 and order_index=$3 
DETAIL: parameters: $1 = '88', $2 = '4', $3 = '0' 
ERROR: duplicate key value violates unique constraint "foo_bar_map_bar_id_key" 

Các lỗi một cách hoàn hảo làm cho tinh thần, được đưa ra những điều khoản được tạo ra bởi Hibernate (có năm mặt hàng trong danh sách, tôi loại bỏ một trong những đầu tiên và Hibernate xóa hàng ánh xạ với chỉ mục LAST và cố gắng cập nhật các chỉ mục còn lại, bắt đầu từ đầu tiên).

Điều gì sai với ánh xạ ở trên?

Trả lời

14

lập bản đồ của bạn là hoàn toàn hợp lệ và làm việc với EclipseLink như JPA thực hiện 2.0 (không có chú thích Fetch tất nhiên), nhưng thực sự không thành công với Hibernate.

Đây là DDL với Hibernate:

create table foo_bar_map (foo_id bigint not null, bar_id bigint not null, order_index integer not null, primary key (foo_id, order_index), unique (bar_id)) 
alter table foo_bar_map add constraint FK14F1CB7FA042E82 foreign key (bar_id) references Bar4022509 
alter table foo_bar_map add constraint FK14F1CB7B6DBCCDC foreign key (foo_id) references Foo4022509 

Vì vậy, chúng ta hãy nói Foo#1 giữ một danh sách với Bar#1, Bar#2, Bar#3, bảng tham gia bao gồm:

foo_id | bar_id | order_index 
    1 |  1 |   1 
    1 |  2 |   2 
    1 |  3 |   3 

Khi tháo, nói là người đầu tiên mục từ danh sách, Hibernate đầu tiên delete hàng cuối cùng (WTF?) từ bảng tham gia:

foo_id | bar_id | order_index 
    1 |  1 |   1 
    1 |  2 |   2 

Và sau đó cố gắng update cột bar_id trong bảng tham gia thay vì order_index (WTF !?) để phản ánh thứ tự "mới" của các mục trong danh sách. Đầu tiên (sơ đồ):

foo_id | bar_id | order_index 
    1 |  2 |   1 
    1 |  2 |   2 

nơi bước tiếp theo sẽ cho kết quả:

foo_id | bar_id | order_index 
    1 |  2 |   1 
    1 |  3 |   2 

Rõ ràng, cách tiếp cận này không phải âm thanh và không hoạt độngunique hạn chế về bar_id . Nói chung, tại sao Hibernate lại gây hỗn độn với bar_id thay vì cập nhật cột order_index?

Tôi coi đây là lỗi Hibernate (được báo cáo là HHH-5694, xem HHH-1268 bây giờ).

+1

Cảm ơn, yeah, đó là những gì tôi cũng phát hiện ra sau một thời gian khá dài trong trình gỡ rối. Như một sửa chữa Tôi đang sử dụng ManyToMany (mà giảm sự hạn chế duy nhất), nhưng nó cảm thấy xấu xí. Điều thú vị là, không ai khác đã gặp vấn đề này, vì tôi cho rằng đây là trường hợp sử dụng phổ biến ... – tbk

+0

@tbh Có, làm cho hiệp hội nhiều người nhiều sẽ giảm ràng buộc duy nhất (để cho phép "nhiều "). Nhưng điều này thực sự là một cách giải quyết, Hibernate nên xử lý một đến nhiều đúng cách. Bạn có thể muốn bỏ phiếu cho vấn đề Jira :) –

0

Thông thường khi tham gia thông qua bảng tham gia, mối quan hệ là ManyToMany chứ không phải OneToMany. Hãy thử điều này

@ManyToMany 
@OrderColumn(name = "order_index") 
@JoinTable(name = "foo_bar_map", joinColumns = @JoinColumn(name = "foo_id"), inverseJoinColumns = @JoinColumn(name = "bar_id")) 
@Fetch(FetchMode.SUBSELECT) 
public List getBars() { 
    return bars; 
} 
+0

Nhưng từ quan điểm mô hình miền của tôi, nó chỉ là OneToMany. Thay thế điều này bằng ManyToMany sẽ tận dụng một số ràng buộc, rõ ràng không phải là điều tôi muốn. Tôi muốn giản đồ càng an toàn càng tốt. Lý do chính để sử dụng JoinTable ở đây là theo dõi chỉ số thứ tự, không nên biết trong lớp Bar. – tbk

+0

Với JPA tiêu chuẩn, OneToMany một chiều được ánh xạ bằng cách sử dụng bảng nối (mặc dù Hibernate cho phép ánh xạ này mà không cần tham gia bảng bằng cách sử dụng 'JoinColumn', mà bây giờ cũng được hỗ trợ trong JPA 2.0). –

1

Không, tôi không nghĩ đó là lỗi ngủ đông, và bạn sẽ thấy nếu bạn tìm kiếm lỗi ngủ đông này do Pascal Thivent trích dẫn là lỗi được biết đến từ năm 2006 và chưa bao giờ được giải quyết.

Tại sao?

Nguyên nhân tôi nghĩ vấn đề chỉ nằm trong các ràng buộc trên bàn chứ không phải ở chế độ ngủ đông.

Tôi không hiểu tại sao có một hạn chế duy nhất trên bar_id

Sử dụng một chỉ số thứ tự có nghĩa là bộ sưu tập của bạn là một danh sách (và không phải là một Set!). Và Danh sách là bộ sưu tập nơi bạn có thể chỉ định chỉ mục cho các phần tử bạn thêm (nó tương ứng với OrderColumn).
Sự khác biệt giữa Danh sách và Tập hợp là bạn và có thể cùng một dữ liệu hai lần (hoặc nhiều hơn), nhưng cùng một dữ liệu sẽ ở các chỉ mục khác nhau. Sau đó, bạn có thể có cùng một bar_id một chỉ mục khác nhau, bạn không phải chỉ định một ràng buộc duy nhất trên bar_id. Và khóa chính không thể (foo_id, order_index) khiến Danh sách mẫu cho phép cùng một dữ liệu ở các chỉ mục khác nhau. Có lẽ PK của bạn phải là (foo_id, bar_id, order_index)?

Tôi nghĩ rằng vấn đề là theo cách này :)

+0

Bao giờ nghe nói về một bộ đặt hàng? Hai ràng buộc là trực giao: Một thứ tự xác định và tính duy nhất. – tbk

Các vấn đề liên quan