2012-05-15 9 views
7

Ich implementiere einen Entity-Attribut-Wert basierten Persistenz-Mechanismus. Alle DB-Zugriffe erfolgen über Hibernate. Ich habe eine Tabelle, die Pfade für Knoten enthält, es ist extrem einfach, nur eine ID und ein Pfad (String) Die Pfade wären klein in der Zahl, um ein paar tausend.Wie verwende ich Hibernate Second-Level-Cache mit JPA?

Die Haupttabelle hat Millionen von Zeilen, und anstatt die Pfade zu wiederholen, habe ich die Pfade zu ihrer eigenen Tabelle normalisiert. Im Folgenden ist das Verhalten, das ich will, wenn in den Haupt Tabelle einfügen

1) Überprüfen Sie, ob der Pfad in Pfaden Tabelle (Abfrage über Entity Manager existiert, Pfadwert als Parameter verwendet wird)

2), wenn es nicht vorhanden ist , einfügen und ID erhalten (persistent über Entitätsmanager)

3) ID als Fremdschlüsselwert in die Haupttabellenzeile eingeben und diese in die Haupttabelle einfügen.

Dies wird tausende Male für eine Reihe von Domänenobjekten passieren, die vielen Zeilen in der Haupttabelle und einigen anderen Tabellen entsprechen. So sind die obigen Schritte werden wiederholt, um eine einzelne Transaktion verwendet wie folgt aus:

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

Wenn ich Schritt 2 durchzuführen, es stellt eine enorme Leistungsabfall auf den Gesamtbetrieb. Es bittet um Caching, denn nach einer Weile wird dieser Tisch höchstens 10-20k Einträge mit sehr seltenen neuen Beilagen haben. Ich habe versucht, dies mit Hibernate zu tun, und verlor fast 2 Tage.

Ich verwende Hibernate 4.1, mit JPA-Anmerkungen und ECache. Ich habe versucht, Query-Caching zu aktivieren, auch die gleiche Abfrage Objekt in den Einsätzen verwenden, wie unten dargestellt:

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(); 
     } 

Die NodePath Einheit kommentierte wie folgt:

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

Der Abfrage-Cache ist sein verwendet, soweit ich aus den Statistiken sehen kann, aber keine Verwendung für Cache zweiter Ebene berichtet wird:

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 
.... 

eine einfache, handgeschriebene hashtable als Cache, wie erwartet funktioniert, d Schneiden eigene Gesamtzeit drastisch. Ich schätze, dass ich wegen der Art meiner Operationen das Zwischenspeichern von Hibernate nicht auslösen kann.

Wie verwende ich Hibernate's Second Level Cache mit dieser Einrichtung? Für das Protokoll, das ist meine Ausdauer xml:

http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd“ version = "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

Sehr interessant! Danke für die Frage und Antwort;) +1 – MychaL

Antwort

2

Ok, ich fand es. Mein Problem war, dass zwischengespeicherte Abfrage wurde hält nur Ids von Abfrageergebnissen im Cache, und es war (wahrscheinlich) zu db zurück, die tatsächlichen Werte zu erhalten, anstatt sich zu bekommen aus dem Cache der zweiten Ebene

Das Problem besteht natürlich darin, dass die Abfrage diese Werte nicht in den Cache der zweiten Ebene legte, da sie nicht von der primären ID ausgewählt wurden. Die Lösung besteht also darin, eine Methode zu verwenden, die Werte in den Cache der zweiten Ebene und in den Ruhezustand 4 versetzt.1, ich habe es geschafft, dies mit natürlicher ID zu tun. Hier ist die Funktion, die entweder den Wert aus dem Cache einfügt oder zurückgibt, falls er anderen hilft: