2011-11-14 32 views
26

Đây là một câu hỏi thiết kế, mã bê tông không được gửi để bảo vệ đáy của tôi.org.hibernate.Session.clear() được coi là có hại?

Khi làm việc với Hibernate các quy trình làm việc giữa các ý kiến ​​như sau:

  1. mở phiên
  2. Bắt đầu giao dịch
  3. Làm kinh doanh (đọc và sửa đổi dữ liệu)
  4. Commit Transaction
  5. Đóng phiên

có thể có thể lặp qua 2-4.

Trường hợp sử dụng hợp lý cho Session.clear() là gì?

A: Vấn đề cụ thể mà tôi có là một đoạn mã lớn tải và sửa đổi các thực thể, sau đó xóa() của phiên, về cơ bản loại bỏ các thay đổi đã được thực hiện. (Nhiệm vụ kinh doanh cần thực hiện không bao gồm sửa đổi các thực thể, do đó, mã "hoạt động").

Dường như với tôi thiết kế phù hợp là đảm bảo đoạn mã (lớn) không thực hiện các thay đổi mà nó không muốn lưu?

B: Tôi đoán Session.clear() tồn tại để thuận tiện/linh hoạt, không phải vì ý tưởng hay là sử dụng nó.

Tôi đã hiểu lầm triết lý Hibernate chưa?

C: Câu hỏi con: Có phải ý tưởng tồi về mã khung để vô điều kiện rõ ràng() phiên khi tác vụ hoàn tất không? IMHO, khuôn khổ nên khiếu nại nếu phiên là bẩn khi một nhiệm vụ hoàn thành! Phiên họp phải được đóng lại, xem khi công việc được thực hiện ... (Bỏ qua hiệu suất cho từng phút)

(Nhãn A, B và C để bạn có thể chỉ ra phần nào bạn đang trả lời).

Trả lời

29

Quảng cáo. A: Có vẻ như bạn biết điều gì là clear(). Lý do để gọi nó rõ ràng là xóa tất cả các thực thể được quản lý khỏi bộ nhớ cache L1, để nó không phát triển vô hạn khi xử lý các tập dữ liệu lớn trong một giao dịch.

Nó loại bỏ tất cả các thay đổi đã được thực hiện đối với các điều khoản được quản lý không được lưu giữ rõ ràng. Điều này có nghĩa là bạn có thể sửa đổi một thực thể một cách an toàn, cập nhật nó một cách rõ ràng và xóa phiên. Đây là thiết kế phải. Rõ ràng nếu không có thay đổi nào được thực hiện (dài, nhưng chỉ đọc phiên), clear() luôn an toàn.

Bạn cũng có thể sử dụng stateless sessions.

Quảng cáo. B: Không, nó tồn tại vì những lý do trên: để đảm bảo L1 (bộ nhớ cache phiên) không phát triển quá nhiều. Tất nhiên duy trì nó bằng tay là một ý tưởng tồi và một dấu hiệu cho thấy một công cụ khác nên được sử dụng cho các tập dữ liệu lớn, nhưng đôi khi nó là phải.

Lưu ý rằng trong đặc tả JPA, cũng có phương pháp clear()flush().Trong trường hợp này, bạn nên luôn gọi flush() trước để đẩy các thay đổi vào cơ sở dữ liệu (cập nhật rõ ràng) trước khi gọi clear().

Quảng cáo. C: Thực sự là một ý tưởng tốt để cảnh báo người dùng (có thể bằng cách phát hành thông báo cảnh báo thay vì ném một ngoại lệ) khi anh ta/cô ấy xóa phiên với những thay đổi bẩn. Ngoài ra tôi không nghĩ rằng mã nên gọi clear() vô điều kiện, trừ khi nó chắc chắn rằng mã người dùng nó chạy tuôn ra hoặc không thực hiện bất kỳ thay đổi nào.

+0

ad A): Xử lý các tập dữ liệu lớn trong một giao dịch có vẻ như thiết kế tồi với tôi (được cấp, tôi có thể không nghĩ đến tất cả các sự cố có thể xảy ra ...). Ý bạn là gì bởi "rõ ràng" bền bỉ một thực thể? session.save (thực thể)? Quảng cáo C): Có vẻ như chúng tôi đồng ý các khung công tác không nên làm điều này - khung công tác không thể chắc chắn những gì khách hàng làm. :-) –

+0

@MortenLauritsenKhodabocus: rõ ràng, tải và sửa đổi hàng ngàn bản ghi trong một giao dịch Hibernate là một dấu hiệu cho thấy một công cụ khác nhau nên được sử dụng. Có, bởi * rõ ràng * Tôi có nghĩa là gọi 'save()' như trái ngược với việc cho phép Hibernate khám phá các thực thể bẩn. –

+0

@MortenLauritsenKhodabocus Hãy khai sáng cho tôi. Tại sao bạn nên xử lý một tập dữ liệu chỉ đọc lớn trong một Giao dịch? Dường như với tôi rằng nếu nó liên quan đến việc đặt hàng ở phía DB, nó sẽ là thông minh bởi vì nó sẽ tiết kiệm được từ việc cần phải sắp xếp lại nhiều lần. Không sử dụng giao dịch có nghĩa là DB sẽ nhớ trạng thái đặt hàng của bạn trên các truy vấn khác nhau là một phần của cùng một giao dịch? Ngoài ra, không phải là 'sử dụng một giao dịch' chính xác những gì ScrollableResults API của Hibernate làm gì? – KyleM

3

Đây là một lý do khác mà tôi vừa gặp phải: lưu vào bộ nhớ cache kết quả trước đó khi gọi một thủ tục được lưu trữ nhiều lần trong cùng một giao dịch. Mã đơn giản như sau.

//Begin transaction 
SessionFactory sf = HibernateSessionFactory.getFactory(); 
Session dbSession = sf.getCurrentSession(); 
dbSession.beginTransaction(); 

//First call to stored procedure 
Query query = dbSession.getNamedQuery("RR_CUST_OPP_DATA"); 
query.setString("custName", "A"); 
List<ShipSummaryRow> shipSummaryRows = query.list(); 

//Second call to stored procedure 
Query query = dbSession.getNamedQuery("RR_CUST_OPP_DATA"); 
query.setString("custName", "B"); 
List<ShipSummaryRow> shipSummaryRows = query.list(); 

//Commit both  
dbSession.getTransaction().commit(); 

Nếu không có dấu rõ ràng() sau cuộc gọi đầu tiên, các hàng kết quả của cuộc gọi đầu tiên sẽ được nhân rộng vào kết quả của cuộc gọi thứ hai. Tôi đang sử dụng Oracle 11gR2.

Chìa khóa để nhân rộng lỗi này là thực hiện cả hai cuộc gọi trong cùng một giao dịch. Vì tôi đang sử dụng phiên mở trong dạng xem, cả hai cuộc gọi sẽ tự động xảy ra trong cùng một giao dịch (như mã ban đầu gọi proc trong vòng lặp lưu trữ kết quả của mỗi lần). Do đó tôi gọi nó là một lỗi; khác có thể được coi là một tính năng nhưng ngay cả sau đó rõ ràng() không được gọi ra trong các mẫu mã nói nó nên được gọi. session.flush() không làm gì cả. Lập bản đồ tệp như dưới đây. Kết quả là tôi đã thêm rõ ràng() vào cuối tất cả các cuộc gọi thủ tục của tôi. Chưa thử nghiệm với các cuộc gọi SQL tùy chỉnh của tôi. Đây là thứ tầm thường; ngạc nhiên khi lỗi tồn tại.

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 
<hibernate-mapping> 
    <class name="com.jfx.rr.model.ShipSummaryRow"> 
     <id name="id" type="integer"/> 
     <property name="shipQtrString" not-null="true" type="string"/> 
     <property name="shipAmount" not-null="true" type="double"/> 
    </class> 
    <sql-query callable="true" name="RR_CUST_OPP_DATA"> 
     <return class="com.jfx.rr.model.ShipSummaryRow"> 
      <return-property column="SHIPPED_ID" name="id"/> 
      <return-property column="SHIP_QTR" name="shipQtrString"/> 
      <return-property column="SHIPPED_AMOUNT" name="shipAmount"/> 
     </return> 
     { call RR_DASHBOARD_REPORTS_PKG.RR_CUST_OPP_DATA(?, :custName) } 
    </sql-query> 
</hibernate-mapping> 
Các vấn đề liên quan