2009-09-11 39 views
17

Tôi có một bộ sưu tập trong mô hình có chứa một tập hợp các 'phiên bản trước' của đối tượng miền gốc của tôi. Các phiên bản trước đó do đó là 'bất biến' và chúng tôi sẽ không bao giờ muốn cập nhật chúng, và chỉ muốn thêm các phiên bản trước khi chúng phát sinh. Ngoài ra đối tượng miền 'được phiên bản' là khá phức tạp và gây ra truy cập cơ sở dữ liệu nặng để truy xuất.Hibernate - Cách duy trì một mục mới trong Bộ sưu tập mà không tải toàn bộ Bộ sưu tập

Khi tôi có phiên bản mới của một trong các đối tượng này, tôi muốn lưu nó với những người khác mà không tải toàn bộ bộ. Câu hỏi thường gặp nâng cao có một số lời khuyên về điều này:

Tại sao Hibernate luôn khởi tạo bộ sưu tập khi tôi chỉ muốn thêm hoặc xóa phần tử?

Thật không may API bộ sưu tập xác định giá trị trả lại phương thức chỉ có thể được tính bằng cách nhấn cơ sở dữ liệu. Có ba ngoại lệ cho điều này: Hibernate có thể thêm vào một số <bag>, <idbag> hoặc <list> được khai báo với inverse="true" mà không cần khởi tạo bộ sưu tập; giá trị trả về phải luôn đúng.

Nếu bạn muốn tránh lưu lượng truy cập cơ sở dữ liệu bổ sung (ví dụ: trong mã quan trọng hiệu suất), hãy tái cấu trúc mô hình của bạn để chỉ sử dụng các liên kết nhiều người. Điều này gần như luôn luôn có thể. Sau đó, sử dụng truy vấn thay cho quyền truy cập bộ sưu tập.

Tôi mới làm quen với tất cả điều này và không chắc chắn 100% về cách cấu trúc lại mô hình của bạn để chỉ sử dụng các liên kết nhiều người. Bất cứ ai có thể xin vui lòng cho tôi một ví dụ về điểm tôi đến một hướng dẫn để tôi có thể tìm hiểu cách thức này sẽ giải quyết vấn đề của tôi?

+0

Tôi nghĩ những gì tôi thực sự cần là 1) để có thể sử dụng 'bộ lọc hibernate' để chỉ lấy bản sửa đổi đầu khi tải bộ sưu tập (trừ báo cáo kiểm toán); và sau đó 2) nếu đầu này được sửa đổi bởi một thao tác (gây ra hai phần tử trong bộ sưu tập) cho hibernate chỉ cập nhật 'đầu' hiện tại (không phải là đầu nữa) và sau đó chèn phiên bản mới vào cơ sở dữ liệu. Thật không may, nếu chúng ta thực hiện phương pháp này Hibernate dường như nghĩ rằng các phần tử đã được lọc (các phiên bản trước đây) không còn được liên kết với bộ cha mẹ nữa. thiết lập bất biến = 'true' có vẻ không hoạt động. –

+0

Vui vì chúng tôi có thể giúp đỡ. Nếu bạn thích một số câu trả lời, bạn có thể bỏ phiếu cho họ (nhấn mũi tên nhỏ lên trên), để cảm ơn mọi người vì thời gian của họ. Ngoài ra, nếu một câu trả lời là đủ tốt cho bạn, nếu bạn đi đến một cái gì đó khác, bạn có thể đánh dấu một câu trả lời như là một 'chấp nhận'. – KLE

Trả lời

8

Cách tôi thường làm điều này là xác định bộ sưu tập là "nghịch đảo".

Điều đó có nghĩa là: định nghĩa chính của liên kết 1-N được thực hiện ở đầu "N". Tf bạn muốn thêm một cái gì đó vào bộ sưu tập bạn thay đổi đối tượng liên quan của dữ liệu chi tiết.

Một XML nhỏ ví dụ:

<class name="common.hibernate.Person" table="person"> 
    <id name="id" type="long" column="PERSON_ID"> 
     <generator class="assigned"/> 
    </id> 
    <property name="name"/> 
    <bag name="addresses" inverse="true"> 
     <key column="PERSON_ID"/> 
     <one-to-many class="common.hibernate.Address"/> 
    </bag> 
</class> 

<class name="common.hibernate.Address" table="ADDRESS"> 
    <id name="id" column="ADDRESS_ID"/> 
    <property name="street"/> 
    <many-to-one name="person" column="PERSON_ID"/> 
    </class> 

sau đó cập nhật được thực hiện độc quyền tại Địa chỉ:

Address a = ...; 
a.setPerson(me); 
a.setStreet("abc"); 
Session s = ...; 
s.save(a); 

Done. Bạn thậm chí không chạm vào bộ sưu tập. Hãy xem xét nó chỉ đọc, mà có thể rất thiết thực cho truy vấn với HQL, và lặp lại và hiển thị nó.

+0

Rất cám ơn vì đã dành thời gian và lời khuyên của bạn. Đây là một gợi ý tuyệt vời. Tôi nhìn vào điều này, nhưng, tiếc là tôi có hai vấn đề mâu thuẫn nhau: 1) 'Các thành phần phụ được quản lý' là từ một hệ thống bên ngoài vì vậy tôi không thể thêm sự liên kết nghịch đảo cần thiết cho chúng; 2) hơn nữa các thực thể sở hữu đã tuyên bố 'loại' của 'quản lý-tiểu thành phần' trong một lớp cha làm cho nó khó khăn để tạo và wrapper trung gian. Tuy nhiên, lời khuyên này sẽ phù hợp với những người khác trong một tình huống tương tự mà tôi cảm thấy. Cảm ơn! –

+0

a.setPerson (tôi) => bây giờ tôi hiểu! Cảm ơn! – Karussell

2

Nếu tôi hiểu tốt nhu cầu của bạn:

  • bạn có một trong bộ nhớ add-chỉ tập hợp các đối tượng bất biến
  • làm thế nào để thêm một mục vào bộ sưu tập mà không cần khởi tạo toàn bộ bộ sưu tập (trong Phiên hiện tại)?

Bạn có xem xét duy trì bộ sưu tập của bạn bị ngắt kết nối không?

  • tải nó một lần khi ứng dụng bắt đầu
  • Thêm một yếu tố có nghĩa là hai hoạt động, thực hiện bởi một dịch vụ duy nhất tại một nơi duy nhất:
    • tiết kiệm các yếu tố (hoạt động cơ sở dữ liệu nhanh chóng, không có tầng là thực hiện để công ty mẹ)
    • thêm yếu tố để thu bị ngắt kết nối hiện tại (không hoạt động cơ sở dữ liệu)
+0

Rất cám ơn vì đã dành thời gian và lời khuyên của bạn. Có một thực thể cấp cao duy trì một bộ sưu tập (Bản đồ) của phiên bản trước và phiên bản hiện tại của thành phần phụ được quản lý. Người dùng có thể chỉ định các hoạt động tùy ý trên thành phần phụ được quản lý hiện tại. Nếu bất kỳ hoạt động nào trong số này dẫn đến việc sửa đổi thành tiểu hợp phần thì phiên bản hiện tại sẽ trở thành phiên bản trước và phiên bản thay đổi mới là 'đầu'. Tôi sẽ xem xét việc xóa hoạt động xếp tầng và xử lý thủ công sau đó. Cảm ơn! –

+0

OK, vui vì tôi có thể giúp bạn. – KLE

11

Khi bạn có bộ sưu tập Danh sách hoặc Tập hợp và bạn thêm đối tượng mới vào bộ sưu tập của mình, Hibernate sẽ luôn truy cập cơ sở dữ liệu vì nó so sánh từng đối tượng bằng cách sử dụng thực hiện bằng trước khi lưu hoặc cập nhật - khi sử dụng Bộ - hoặc bằng cách so sánh cột chỉ mục khi sử dụng Danh sách. Hành vi này là cần thiết vì ngữ nghĩa Set và List. Do đó, hiệu suất của ứng dụng của bạn có thể giảm đáng kể cho dù bạn có một loạt các bản ghi.

Một số cách giải quyết để khắc phục vấn đề này

mô hình chuyển đổi bằng cách sử dụng một bộ sưu tập encapsuled Bag cộng Set mong muốn của bạn hoặc Danh sách tiếp xúc như một tài sản

@Entity 
public class One { 

    private Collection<Many> manyCollection = new ArrayList<Many>(); 

    @Transient 
    public Set<Many> getManyCollectionAsSet() { return new HashSet<Many>(manyCollection); } 
    public void setManyCollectionAsSet(Set<Many> manySet) { manyCollection = new ArrayList<Many>(manySet); } 

    /** 
     * Keep in mind that, unlike Hibernate, JPA specification does not allow private visibility. You should use public or protected instead 
     */ 
    @OneToMany(cascade=ALL) 
    private Collection<Many> getManyCollection() { return manyCollection; } 
    private void setManyCollection(Collection<Many> manyCollection) { this.manyCollection = manyCollection; } 

} 

Sử dụng ManyToOne thay vì OneToMany

@Entity 
public class One { 

    /** 
     * Neither cascade nor reference 
     */ 

} 

@Entity 
public class Many { 

    private One one; 

    @ManyToOne(cascade=ALL) 
    public One getOne() { return one; } 
    public void setOne(One one) { this.one = one } 

} 

Caching - khi được áp dụng vì, tùy thuộc vào yêu cầu của bạn, cấu hình của bạn có thể tăng hoặc giảm hiệu suất của ứng dụng của bạn. Xem here

4 ° SQL chế - Nếu bạn muốn có một bộ sưu tập mà cư xử như một Set, bạn có thể sử dụng một hạn chế SQL, có thể được áp dụng cho một cột hoặc bộ cột. Xem here

+4

Tôi nghĩ rằng nó đọc bộ sưu tập hiện tại để thực hiện việc thêm. Điều này là bởi vì nó cần phải kiểm tra các ràng buộc 'Set'. tức là nó cần phải xem thực thể bạn đang thêm đã có trong bộ sưu tập chưa, vì nó phải trả về false nếu nó đã có sẵn. – davidsheldon

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