2012-05-15 31 views
7

Tôi đang triển khai cơ chế dựa trên giá trị thuộc tính dựa trên thuộc tính. Tất cả các truy cập DB được thực hiện thông qua Hibernate. Tôi có một bảng có chứa đường dẫn cho các nút, nó cực kỳ đơn giản, chỉ một id và một đường dẫn (chuỗi) Các đường dẫn sẽ có số lượng nhỏ, khoảng vài nghìn.Làm cách nào để sử dụng bộ nhớ cache cấp hai của Hibernate với JPA?

Bảng chính có hàng triệu hàng và thay vì lặp lại các đường dẫn, tôi đã chuẩn hóa các đường dẫn tới bảng của riêng chúng. Sau đây là hành vi tôi muốn, khi chèn vào bảng chính

1) Kiểm tra xem con đường tồn tại trong bảng đường dẫn (truy vấn thông qua quản lý tổ chức, sử dụng giá trị con đường như tham số)

2) nếu nó không tồn tại , chèn và nhận id (tồn tại qua trình quản lý thực thể)

3) đặt id làm giá trị khóa ngoài cho hàng bảng chính và chèn giá trị này vào bảng chính.

Điều này sẽ xảy ra hàng nghìn lần đối với một tập hợp các đối tượng miền, tương ứng với nhiều hàng trong bảng chính và một số bảng khác. Vì vậy, các bước trên được lặp lại bằng cách sử dụng một giao dịch duy nhất như thế này:

EntityTransaction t = entityManager.getTransaction(); 
    t.begin(); 
    //perform steps given above, check, and then persist etc.. 
    t.commit(); 

Khi tôi thực hiện bước 2, nó sẽ giảm hiệu suất hoạt động rất lớn. Nó đang cầu xin cho bộ nhớ đệm, bởi vì sau một thời gian mà bảng sẽ có nhiều nhất 10-20k mục với chèn mới rất hiếm. Tôi đã cố gắng làm điều này với Hibernate, và mất gần 2 ngày.

Tôi đang sử dụng Hibernate 4.1, với chú thích JPA và ECache. Tôi đã cố gắng để cho phép bộ nhớ đệm truy vấn, thậm chí sử dụng các đối tượng cùng một truy vấn trong suốt chèn, như hình dưới đây:

Query call = entityManager.createQuery("select pt from NodePath pt " + 
       "where pt.path = :pathStr)"); 
     call.setHint("org.hibernate.cacheable", true); 
     call.setParameter("pathStr", pPath); 
     List<NodePath> paths = call.getResultList(); 
     if(paths.size() > 1) 
      throw new Exception("path table should have unique paths"); 
     else if (paths.size() == 1){ 
      NodePath path = paths.get(0); 
      return path.getId(); 
     } 
     else {//paths null or has zero size 
      NodePath newPath = new NodePath(); 
      newPath.setPath(pPath); 
      entityManager.persist(newPath); 
      return newPath.getId(); 
     } 

Thực thể NodePath được chú thích như sau:

@Entity 
@Cacheable 
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) 
@Table(name = "node_path", schema = "public") 
public class NodePath implements java.io.Serializable { 

Bộ nhớ cache truy vấn đang được được sử dụng, như xa như tôi có thể nhìn thấy từ số liệu thống kê, nhưng không sử dụng cho bộ nhớ cache mức thứ hai được báo cáo:

queries executed to database=1 
query cache puts=1 
query cache hits=689 
query cache misses=1 
.... 
second level cache puts=0 
second level cache hits=0 
second level cache misses=0 
entities loaded=1 
.... 

một đơn giản, viết tay Hashtable như một bộ nhớ cache, các công trình như mong đợi, cắt d sở hữu tổng thời gian một cách quyết liệt. Tôi đoán tôi không thể kích hoạt bộ nhớ đệm của Hibernate do bản chất của các hoạt động của tôi.

Tôi làm cách nào để sử dụng bộ nhớ cache cấp hai của hibernate với thiết lập này? Đối với hồ sơ, đây là xml kiên trì của tôi:

http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" phiên bản = "2.0">

<provider>org.hibernate.ejb.HibernatePersistence</provider> 
<class>...</class> 
<exclude-unlisted-classes>true</exclude-unlisted-classes> 
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode> 

    <properties> 
    <property name="hibernate.connection.driver_class" value="org.postgresql.Driver" /> 
    <property name="hibernate.connection.password" value="zyx" /> 
    <property name="hibernate.connection.url" value="jdbc:postgresql://192.168.0.194:5432/testdbforml" /> 
    <property name="hibernate.connection.username" value="postgres"/> 
    <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/> 
    <property name="hibernate.search.autoregister_listeners" value="false"/> 
    <property name="hibernate.jdbc.batch_size" value="200"/> 
    <property name="hibernate.connection.autocommit" value="false"/> 
    <property name="hibernate.generate_statistics" value="true"/> 
    <property name="hibernate.cache.use_structured_entries" value="true"/> 

    <property name="hibernate.cache.use_second_level_cache" value="true"/> 
    <property name="hibernate.cache.use_query_cache" value="true"/>   

    <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"/>    

    </properties> 

+0

Rất thú vị! Cảm ơn bạn đã đặt câu hỏi và trả lời;) +1 – MychaL

Trả lời

2

Ok, tôi đã tìm thấy nó Vấn đề của tôi là, truy vấn được lưu trong bộ nhớ cache chỉ giữ Id của kết quả truy vấn trong bộ nhớ cache và nó (có thể) quay lại db để nhận giá trị thực tế thay vì nhận chúng từ bộ nhớ cache cấp hai.

Vấn đề là tất nhiên, truy vấn không đặt các giá trị đó vào bộ nhớ cache cấp thứ hai, vì chúng không được chọn bởi id chính. Vì vậy, giải pháp là sử dụng một phương pháp mà sẽ đặt giá trị cho bộ nhớ cache cấp thứ hai, và với hibernate 4.1, tôi đã quản lý để làm điều này với id tự nhiên. Đây là chức năng chèn hoặc trả về giá trị từ bộ nhớ cache, chỉ trong trường hợp nó giúp bất kỳ ai khác:

private UUID persistPath(String pPath) throws Exception{ 
     org.hibernate.Session session = (Session) entityManager.getDelegate(); 
     NodePath np = (NodePath) session.byNaturalId(NodePath.class).using("path", pPath).load(); 
     if(np != null) 
      return np.getId(); 
     else {//no such path entry, so let's create one 
      NodePath newPath = new NodePath(); 
      newPath.setPath(pPath); 
      entityManager.persist(newPath); 
      return newPath.getId(); 
     } 


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