Como eu uso o cache de segundo nível do Hibernate com JPA?
Eu estou implementando um mecanismo de persistência baseada em valor de atributo de entidade. Todo o acesso ao banco de dados é feito via Hibernate. Eu tenho uma tabela que contém caminhos para nós, é extremamente simples, apenas um id e um caminho (string) Os caminhos seriam pequenos em número, em torno de alguns milhares.
A tabela principal tem milhões de linhas e, em vez de repetir os caminhos, eu normalizei os caminhos para sua própria tabela. O seguinte é o comportamento que eu quero, ao inserir na tabela principal
1) Verifique se o caminho existe na tabela de caminhos (consulta via gerenciador de entidades, usando o valor do caminho como parâmetro)
2) se não existir, insira e obtenha id (persistir via gerenciador de entidades)
3) coloque o id como valor da chave estrangeira na linha da tabela principal e insira-o na tabela principal.
Isso acontecerá milhares de vezes para um conjunto de objetos de domínio, que correspondem a várias linhas na tabela principal e algumas outras tabelas. Assim, as etapas acima são repetidas usando uma única transação como esta:
EntityTransaction t = entityManager.getTransaction();
t.begin();
//perform steps given above, check, and then persist etc..
t.commit();
Quando executo o passo 2, introduz uma queda enorme de desempenho para a operação total. Ele está implorando por cache, porque depois de um tempo essa tabela terá no máximo 10-20k entradas com novas inserções muito raras. Eu tentei fazer isso com o Hibernate e perdi quase 2 dias.
Estou usando o Hibernate 4.1, com anotações JPA e ECache. Eu tentei ativar o cache de consulta, mesmo usando o mesmo objeto de consulta em todas as inserções, conforme mostrado abaixo:
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();
}
A entidade NodePath é anotada da seguinte forma:
@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Table(name = "node_path", schema = "public")
public class NodePath implements java.io.Serializable {
O cache de consulta está sendo usado, tanto quanto eu posso ver nas estatísticas, mas não há uso para cache de segundo nível é relatado:
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
....
Um hashtable simples, escrito à mão como um cache, funciona como esperado, reduzindo drasticamente o tempo total. Eu acho que estou deixando de acionar o cache do Hibernate devido à natureza das minhas operações.
Como eu uso o cache de segundo nível do hibernate com esta configuração? Para o registro, esta é minha persistência 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>