2012-05-15 13 views
7

Wdrażam mechanizm utrwalania oparty na wartości elementu Entity. Cały dostęp do bazy danych odbywa się przez Hibernate. Mam tabelę, która zawiera ścieżki dla węzłów, jest niezwykle prosta, po prostu identyfikator i ścieżka (ciąg) Ścieżki byłyby małe, około kilku tysięcy.Jak używać pamięci podręcznej drugiego poziomu Hibernate z JPA?

Główna tabela ma miliony rzędów, a zamiast powtarzać ścieżki, normalizuję ścieżki do ich własnej tabeli. Poniżej znajduje się zachowanie chcę, podczas wkładania do głównej tabeli

1) Sprawdź, czy ścieżka istnieje w ścieżkach tabeli (zapytania przez kierownika jednostki, wykorzystując wartość ścieżki jako parametr)

2) Jeżeli nie istnieje , wstaw i uzyskaj identyfikator (trwa przez menedżera encji)

3) wpisz id jako wartość klucza obcego do głównego wiersza tabeli i wstaw to do głównej tabeli.

To zdarzy się tysiące razy dla zestawu obiektów domeny, które odpowiadają wielu wierszom w głównej tabeli i kilku innych tabelach. Tak więc powyższe kroki są powtarzane przy użyciu pojedynczej transakcji tak:

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

Kiedy wykonać krok 2, wprowadza ogromny spadek wydajności do całej operacji. To błaganie o buforowanie, ponieważ po chwili tabela ta będzie zawierać najwyżej 10-20k wpisów z bardzo rzadkimi nowymi wstawkami. Próbowałem to zrobić z Hibernate i straciłem prawie 2 dni.

Używam Hibernuj 4.1, z adnotacjami JPA i ECache. Próbowałem włączyć buforowanie zapytań, nawet przy użyciu tego samego obiektu zapytania całej wkładki, jak pokazano poniżej:

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

Podmiot NodePath oznakowana jest w sposób następujący:

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

Pamięć podręczna zapytanie jest stosowane, o ile widzę ze statystyk, ale nie ma sensu dla drugiego poziomu pamięci podręcznej jest zgłaszane:

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

prosty, ręcznie napisany hashtable jako cache, działa zgodnie z oczekiwaniami, cięcie d własny całkowity czas drastycznie. Domyślam się, że nie mogę uruchomić buforowania Hibernate z powodu natury moich operacji.

Jak korzystać z pamięci podręcznej drugiego poziomu hibernacji przy tej konfiguracji? Dla przypomnienia, jest to mój upór 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

Bardzo interesujące! Dziękuję za pytanie i odpowiedź;) +1 – MychaL

Odpowiedz

2

Ok, znalazłem go. Moim problemem było to, że, buforowane zapytań trzymał tylko identyfikatory z wyników kwerendy w pamięci podręcznej, a to był (prawdopodobnie) wracając do db uzyskać rzeczywiste wartości, zamiast zachęcając z pamięci podręcznej drugiego poziomu:

Problem polega oczywiście na tym, że zapytanie nie umieściło tych wartości w pamięci podręcznej drugiego poziomu, ponieważ nie zostały wybrane przez główny identyfikator. Rozwiązaniem jest więc użycie metody, która przeniesie wartości do pamięci podręcznej drugiego poziomu i hibernacji 4.1, udało mi się to zrobić z naturalnym identyfikatorem. Oto funkcja, która wstawia lub zwraca wartość z pamięci podręcznej, na wypadek gdyby pomogła komuś innemu:

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


    }