Jak używać pamięci podręcznej drugiego poziomu Hibernate z JPA?
Wdrażam mechanizm trwałości oparty na wartości atrybutu Entity. Cały dostęp do bazy danych odbywa się poprzez Hibernate. Mam tabelę, która zawiera ścieżki dla węzłów, jest bardzo prosta, tylko id i ścieżka (ciąg) Ścieżki byłyby małe, około kilku tysięcy.
Główna tabela ma miliony wierszy i zamiast powtarzać ścieżki, znormalizowałem ścieżki do ich własnej tabeli. Poniżej przedstawiam zachowanie, którego chcę podczas wstawiania do głównej tabeli
1) Sprawdź, czy ścieżka istnieje w tabeli ścieżek (zapytanie przez menedżera encji, używając wartości ścieżki jako parametru)
2) jeśli nie istnieje, wstaw i pobierz id (persist via entity manager)
3) umieść identyfikator jako wartość klucza obcego w głównym wierszu tabeli i wstaw go do głównej tabeli.
Stanie się to tysiące razy dla zestawu obiektów domeny, które odpowiadają wielu wierszom w tabeli głównej i kilku innych tabelach. Powyższe kroki są powtarzane przy użyciu pojedynczej transakcji takiej jak ta:
EntityTransaction t = entityManager.getTransaction();
t.begin();
//perform steps given above, check, and then persist etc..
t.commit();
Kiedy wykonuję krok 2, wprowadza ogromny spadek wydajności w całej operacji. Błaga o buforowanie, ponieważ po pewnym czasie tabela będzie zawierała najwyżej 10-20k wpisów z bardzo rzadkimi nowymi wstawkami. Próbowałem to zrobić za pomocą Hibernate i straciłem prawie 2 dni.
Używam Hibernate 4.1, z adnotacjami JPA i ECache. Próbowałem włączyć buforowanie zapytań, nawet przy użyciu tego samego obiektu zapytania we wstawkach, 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();
}
Obiekt NodePath jest opisywany w następujący sposób:
@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Table(name = "node_path", schema = "public")
public class NodePath implements java.io.Serializable {
Pamięć podręczna zapytań jest używana, o ile widzę ze statystyk, ale nie jest zgłaszane użycie pamięci podręcznej drugiego poziomu:
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
....
Prosta, ręcznie napisana tablica hashtable jako pamięć podręczna działa zgodnie z oczekiwaniami, drastycznie skracając całkowity czas. Chyba nie udało mi się uruchomić buforowania Hibernate'a z powodu natury moich operacji.
Jak korzystać z pamięci podręcznej drugiego poziomu hibernacji w tej konfiguracji? Dla przypomnienia, to jest moja trwałość 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>