2009-02-26 33 views
13

Tôi có cấu trúc thực thể sau: Kinh doanh -> Chiến dịch -> Quảng cáo, trong đó MỘT doanh nghiệp có thể có nhiều chiến dịch và một chiến dịch có thể có nhiều khuyến mãi. Cả hai quan hệ một-nhiều được tuyên bố là LAZY. Một nơi trong mã của tôi, tôi cần phải háo hức lấy cả bộ sưu tập từ một doanh nghiệp, vì vậy tôi làm:JPA: hãy giúp hiểu "tham gia tìm nạp"

Query query = entityManager.createQuery("select b from Business b " + 
      "left join fetch b.campaigns c " + 
      "left join fetch c.promotions where b.id=:id"); 
query.setParameter("id", b.getId()); 
business = (Business) query.getResultList().get(0); 

Tuy nhiên, truy vấn trả về một danh sách kết quả có chứa 4 đối tượng kinh doanh trong đó, tất cả 4 đối tượng được đề cập đến cùng một cá thể nghiệp vụ. Trong cơ sở dữ liệu của tôi, doanh nghiệp này có 3 chiến dịch trong đó và tất cả 3 chiến dịch đều có 3 quảng cáo theo họ.

Tôi có hai câu hỏi:

  1. Lúc đầu, tôi sử dụng Danh sách để chứa nhiều khía cạnh của mối quan hệ, nhưng khi chương trình chạy, tôi nhận được "org.hibernate.HibernateException: không thể đồng thời lấy nhiều túi " ngoại lệ. Sau đó, tôi googled ngoại lệ này và có vẻ như tôi phải sử dụng Set, thay vì List. Vì vậy, tôi đã thay đổi bộ sưu tập thành Set và nó hoạt động. Ai đó có thể cho tôi biết tại sao Danh sách sẽ không hoạt động trong tình huống này?

  2. Tôi hy vọng truy vấn trả về một kết quả duy nhất, bởi vì nó truy vấn đối với id, là khóa chính và do đó chỉ nên trả về một kết quả. Nhưng nó chỉ ra rằng nó trả về 4 trường hợp trong một danh sách. Đây co phải vân đê? Hoặc là hành vi mong đợi này?

Mọi trợ giúp sẽ được đánh giá cao.

Trả lời

20

Các sql tạo ra sẽ giống như thế:

select * from Business b 
left outer join campaigns c on c.business_id = b.id 
left join promotions p on p.campaign_id = c.id 
where b.id=:id 

nội Hibernate sẽ chỉ có một cá thể nghiệp vụ, tuy nhiên các bản sao sẽ được giữ nguyên trong tập kết quả. Đây là hành vi mong đợi. Hành vi mà bạn yêu cầu có thể đạt được bằng cách sử dụng mệnh đề DISTINCT, hoặc bằng cách sử dụng một LinkedHashSet để lọc kết quả:

Collection result = new LinkedHashSet(query.getResultList()); 

mà sẽ trả lại kết quả độc đáo, giữ gìn trật tự chèn.

"org.hibernate.HibernateException: không thể tìm nạp đồng thời nhiều túi" xảy ra bất cứ khi nào bạn cố gắng háo hức tìm nạp nhiều bộ sưu tập theo kiểu đặt hàng (và có thể là các mục trùng lặp). Điều này có ý nghĩa nếu bạn xem xét SQL được tạo ra. Hibernate không có cách nào để biết liệu một đối tượng trùng lặp có phải do sự tham gia hoặc dữ liệu trùng lặp thực sự trong bảng con không. Hãy xem this để có giải thích tốt.

+1

Thêm "khác biệt" vào truy vấn đã thực hiện thủ thuật. Cảm ơn. –

1
  1. tôi giả sử nếu bạn sử dụng một danh sách, chế độ ngủ đông cho rằng trật tự là rất quan trọng, và nó sẽ suy ra thứ tự của các yếu tố từ các hàng trả lại, nhưng nếu bạn tham gia với một hibernate bảng Nother sẽ nhận được mỗi nhiều chiến dịch thời gian mà gây nhầm lẫn ngủ đông. Một lần nữa, tôi chỉ giả

  2. hành vi này này dự kiến ​​sẽ sử dụng một RootEntityResultTransformer để có được một thực thể gốc duy nhất: http://www.hibernate.org/hib_docs/v3/api/org/hibernate/transform/RootEntityResultTransformer.html

3
  1. Danh sách có thể có các yếu tố dư thừa vì sản phẩm Descartes và mọi người không nhận thức được điều đó, vì vậy những người ngủ đông bắt đầu ném lỗi này để buộc mọi người sử dụng Đặt thay vì Danh sách để tránh sự cố này. Nguồn 1
0

Một giải pháp khác thay vì sử dụng Set là sử dụng hai truy vấn để lấy dữ liệu: - Thứ nhất để tải chiến dịch và các quảng cáo liên quan của doanh nghiệp. Sau đó, tải Doanh nghiệp và chiến dịch của doanh nghiệp đó bằng cách tìm nạp

Query query1 = entityManager.createQuery("select c from Business b join b.campaigns c left join fetch c.promotions where b.id=:id"); 
    query1.setParameter("id", b.getId()); 
    query1.getResultList(); 

    Query query2 = entityManager.createQuery("select b from Business b left join fetch  b.campaigns where b.id=:id"); 
    query2.setParameter("id", b.getId()); 
    business = (Business) query2.getResultList().get(0); 
Các vấn đề liên quan