¿Cómo uso el caché de segundo nivel de Hibernate con JPA?

Estoy implementando un mecanismo de persistencia basado en el valor de atributo de entidad. Todo el acceso a la base de datos se realiza a través de Hibernate. Tengo una tabla que contiene rutas para nodos, es extremadamente simple, solo una identificación y una ruta (cadena) Las rutas serían pequeñas en número, alrededor de unos pocos miles.

La tabla principal tiene millones de filas y, en lugar de repetir las rutas, he normalizado las rutas a su propia tabla. El siguiente es el comportamiento que quiero, al insertarlo en la tabla principal

1) Compruebe si la ruta existe en la tabla de rutas (consulta a través del administrador de entidades, usando el valor de ruta como parámetro)

2) Si no existe, inserte y obtenga una identificación (persistir a través del administrador de entidades)

3) coloque id como valor de clave externa en la fila de la tabla principal e insértelo en la tabla principal.

Esto sucederá miles de veces para un conjunto de objetos de dominio, que corresponden a muchas filas en la tabla principal y algunas otras tablas. Así que los pasos anteriores se repiten usando una sola transacción como esta:

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

Cuando realizo el paso 2, se introduce una caída de rendimiento enorme en la operación total. Está pidiendo el almacenamiento en caché, porque después de un tiempo esa tabla tendrá como máximo 10-20k entradas con inserciones nuevas muy raras. Intenté hacer esto con Hibernate y perdí casi 2 días.

Estoy usando Hibernate 4.1, con anotaciones JPA y ECache. He intentado habilitar el almacenamiento en caché de consultas, incluso utilizando el mismo objeto de consulta en las inserciones, como se muestra a continuación:

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

La entidad NodePath se anota como sigue:

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

La memoria caché de consulta se está utilizando, por lo que puedo ver en las estadísticas, pero no se informa de ningún uso para la memoria caché de segundo nivel:

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

Un simple hashtable escrito a mano como caché, funciona como se espera, reduciendo drásticamente el tiempo total. Supongo que no puedo activar el almacenamiento en caché de Hibernate debido a la naturaleza de mis operaciones.

¿Cómo uso el caché de segundo nivel de hibernate con esta configuración? Para el registro, este es mi persistencia 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>

Respuestas a la pregunta(1)

Su respuesta a la pregunta