2015-09-01 21 views
11

Sto lavorando a un'applicazione utilizzando Spring Data JPA con Hibernate e sto tentando di abilitare la cache di secondo livello utilizzando ehcache. Ho rotto la mia applicazione in due progetti:Dati primari JPA con Hibernate ed Ehcache non funzionanti

  • CoreDataFacade: dove mi definisco operazioni di accesso ai dati utilizzando QueryDSL, Primavera dati JPA con Hibernate e EHCache.
  • QueryComponent: è un progetto di avvio a molla che utilizza il progetto CoreDataFacade per accedere ai dati.

configurazione di CoreDataFacade è la seguente:

pom.xml

<dependencies> 
    <dependency> 
     <groupId>org.springframework.data</groupId> 
     <artifactId>spring-data-jpa</artifactId> 
     <version>1.7.3.RELEASE</version> 
    </dependency> 

    <dependency> 
     <groupId>org.hibernate</groupId> 
     <artifactId>hibernate-core</artifactId> 
     <version>4.3.6.Final</version> 
    </dependency> 

    <dependency> 
     <groupId>net.sf.ehcache</groupId> 
     <artifactId>ehcache-core</artifactId> 
     <version>2.4.7</version> 
    </dependency> 

    <dependency> 
     <groupId>org.hibernate</groupId> 
     <artifactId>hibernate-ehcache</artifactId> 
     <version>4.3.6.Final</version> 
    </dependency> 

    <dependency> 
     <groupId>org.hibernate</groupId> 
     <artifactId>hibernate-entitymanager</artifactId> 
     <version>4.3.6.Final</version> 
    </dependency> 

    <dependency> 
     <groupId>mysql</groupId> 
     <artifactId>mysql-connector-java</artifactId> 
     <version>5.1.33</version> 
    </dependency> 
    <dependency> 
     <groupId>c3p0</groupId> 
     <artifactId>c3p0</artifactId> 
     <version>0.9.1.2</version> 
    </dependency> 
    <dependency> 
     <groupId>com.mysema.querydsl</groupId> 
     <artifactId>querydsl-apt</artifactId> 
     <version>3.6.0</version> 
    </dependency> 
    <dependency> 
     <groupId>com.mysema.querydsl</groupId> 
     <artifactId>querydsl-jpa</artifactId> 
     <version>3.6.0</version> 
    </dependency> 

applicazione context.xml

annotazioni
<jpa:repositories 
    base-package="com.coredata.services.impl.sql.mysql.repositories" /> 

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" 
    destroy-method="close" p:driverClass="com.mysql.jdbc.Driver" 
    p:jdbcUrl="jdbc:mysql://localhost/FOO" p:user="****" p:password="****" 
    p:acquireIncrement="5" p:minPoolSize="10" p:maxPoolSize="100" 
    p:maxIdleTime="1200" p:unreturnedConnectionTimeout="120" /> 

<bean id="jpaVendorAdapter" 
    class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" 
    p:database="MYSQL" p:showSql="true" p:generateDdl="true" /> 


<bean id="entityManagerFactory" 
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" 
    p:dataSource-ref="dataSource" p:jpaVendorAdapter-ref="jpaVendorAdapter" 
    p:packagesToScan="com.coredata.services.impl.sql.mysql.model"> 
    <property name="jpaProperties"> 
     <props> 
      <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop> 
      <prop key="hibernate.cache.use_query_cache">true</prop> 
      <prop key="hibernate.cache.use_second_level_cache">true</prop> 
      <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop> 
      <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory 
      </prop> 
      <prop key="javax.persistence.sharedCache.mode">ENABLE_SELECTIVE</prop> 
      <prop key="hibernate.generate_statistics">true</prop> 
     </props> 
    </property> 
</bean> 

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" /> 

Entity cache

@Entity 
@Cacheable 
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region="cache_states") 
@Table(name="states") 
public class State implements Serializable { 
    private static final long serialVersionUID = 1L; 

    @Id 
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    @Column(name="id_state") 
    private int idState; 
    ... 

ehcache.xml

<?xml version="1.0" encoding="UTF-8"?> 
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"> 

    <defaultCache maxElementsInMemory="10000" eternal="false" 
     timeToIdleSeconds="120" timeToLiveSeconds="120" 
     overflowToDisk="false" diskPersistent="false" /> 

    <cache name="cache_states" maxElementsInMemory="300" eternal="false" 
     timeToIdleSeconds="5000" timeToLiveSeconds="5000" overflowToDisk="false"> 
    </cache> 
</ehcache> 

importazioni di configurazione di QueryComponent sopra la configurazione ed escludere JPA:

@Configuration 
@PropertySource("classpath:/component.properties") 
@ImportResource({ "classpath:/application-context.xml"}) 
@EnableAutoConfiguration(exclude = { JpaRepositoriesAutoConfiguration.class }) 
public class Application { 

    public void run(String... args) { } 

    public static void main(String[] args) { 
     SpringApplication.run(Application.class, args); 
    } 
} 

Quando ComponentQuery inizia tutto è OK. Quando eseguire una query per trovare tutti gli stati in banca dati, ibernazione statistiche sono i seguenti:

Hibernate: select count(state0_.id_state) as col_0_0_ from states state0_ 
Hibernate: select state0_.id_state as id_stat1_5_, state0_.name_state as name_e2_5_ from states state0_ limit ? 
[2015-08-31 18:52:21.402] boot - 1946 INFO [SimpleAsyncTaskExecutor-1] --- StatisticalLoggingSessionEventListener: Session Metrics { 
    32992 nanoseconds spent acquiring 1 JDBC connections; 
    0 nanoseconds spent releasing 0 JDBC connections; 
    238285 nanoseconds spent preparing 2 JDBC statements; 
    935976 nanoseconds spent executing 2 JDBC statements; 
    0 nanoseconds spent executing 0 JDBC batches; 
    269717 nanoseconds spent performing 4 L2C puts; 
    0 nanoseconds spent performing 0 L2C hits; 
    0 nanoseconds spent performing 0 L2C misses; 
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections); 
    68790 nanoseconds spent executing 2 partial-flushes (flushing a total of 0 entities and 0 collections) 
    } 

Quando mi ripeto la stessa query ho avuto questa statistica:

Hibernate: select count(state0_.id_state) as col_0_0_ from states state0_ 
Hibernate: select state0_.id_state as id_stat1_5_, state0_.name_state as name_e2_5_ from states state0_ limit ? 
[2015-08-31 19:26:48.479] boot - 1946 INFO [SimpleAsyncTaskExecutor-1] --- StatisticalLoggingSessionEventListener:  Session Metrics { 
    314930 nanoseconds spent acquiring 1 JDBC connections; 
    0 nanoseconds spent releasing 0 JDBC connections; 
    356832 nanoseconds spent preparing 2 JDBC statements; 
    681615 nanoseconds spent executing 2 JDBC statements; 
    0 nanoseconds spent executing 0 JDBC batches; 
    209324 nanoseconds spent performing 4 L2C puts; 
    0 nanoseconds spent performing 0 L2C hits; 
    0 nanoseconds spent performing 0 L2C misses; 
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections); 
    12368 nanoseconds spent executing 2 partial-flushes (flushing a total of 0 entities and 0 collections) 
} 

Sembra che ogni query sta mettendo risultati (4 membri nel database) nella cache:

269717 nanoseconds spent performing 4 L2C puts; 
    209324 nanoseconds spent performing 4 L2C puts; 

mi aspetto seconda query per retrive i dati dalla cache ma vale statistiche pro e contro sono pari a zero:

0 nanoseconds spent performing 0 L2C hits; 
    0 nanoseconds spent performing 0 L2C misses; 

La mia domanda è: perché i riscontri L2C e quelli L2C sono zero quando eseguo la seconda query?

UPDATE

Ecco come sto correndo la mia ricerca

Reposiory

sto lavorando con QueryDSL con JPA dati primaverili.Ho seguito questo tutorial di integrare il mio JpaRepository utilizzando QueryDslPredicateExecutor

public interface StateRepository extends JpaRepository<State, Integer>, QueryDslPredicateExecutor<State> { 
} 

Servizio

Nel mio servizio eseguo la mia query utilizzando queryDLS predicati e PathBuilder come mostrato in questo grande article in modo che, posso trovare Uniti o qualsiasi altra entità da qualsiasi campo. E.g "StateName = Texas", "StatePopulation = 26448193".

@Autowired 
StateRepository repo; 

public List<State> getStatesByFields(String options, Integer page, Integer pageSize,String order) { 
    PredicateBuilder predicateBuilder = new PredicateBuilder().onEntity("State") 
    Pattern pattern = Pattern.compile(OPERATION_PATTERN); 
    Matcher matcher = pattern.matcher(options + ","); 
    while (matcher.find()) { 
     predicateBuilder.with(matcher.group(1), matcher.group(2), matcher.group(3)); 
    } 
    PageRequest pag = new PageRequest(page, page_size) 
    BooleanExpression predicate = predicateBuilder.build(); 
    //findAll is provided by QueryDslPredicateExecutor interface 
    Page<State> result = repo.findAll(predicate, pag); 
} 

Le query vengono eseguite come un incantesimo, ma i dati non sembrano essere memorizzati nella cache.

+2

Aggiungere le query in esecuzione. – manish

+0

possibile duplicato di [Inefficienti prestazioni EhCache] (http://stackoverflow.com/questions/27334492/inefficient-ehcache-performance) –

+0

@manish, ho aggiornato la mia domanda con le informazioni sulle query. –

risposta

2

La configurazione della propria regione cache ha un errore di battitura. Nella classe di entità, la vostra configurazione (@Cache (utilizzo = CacheConcurrencyStrategy.READ_ONLY, region = "cache_states")) punta a una regione diversa da quella definita in ehcache.xml (cache name = "cache_estados")

+0

wth - perché un downvote? –

+0

Ciao @ R4J, la mia configurazione della cache è stata un errore quando stavo modificando la mia domanda. In realtà la mia regione ehcache.xml ha lo stesso nome che ho nella mia configurazione dell'entità ("cache_states"). Ho aggiornato la mia domanda. A proposito, non so perché un downvote, ma grazie per il tuo tempo. –

6

La cache delle entità funziona solo se l'entità viene recuperata utilizzando il suo id es. load(), get(). Non funziona se usi la query.

Per abilitare la memorizzazione nella cache della query è necessario utilizzare la cache delle query. per esempio.

List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger") 
    .setEntity("blogger", blogger) 
    .setMaxResults(15) 
    .setCacheable(true) 
    .setCacheRegion("frontpages") 
    .list(); 

o utilizzando JPA

query.setHint(“org.hibernate.cacheable”, true); 

Non sono sicuro di come implementare questo con QueryDslPredicateExecutor, ma spera che questo aiuterà a capire letargo secondo cache di lvl

colpi L2C e manca L2C sono uguale a zero significa che l'ibernazione non esegue mai la ricerca dei dati dalla cache perché si stanno recuperando i record utilizzando la query senza abilitare la cache della query

L2C da zero mette a causa di sospensione memorizzare nella cache i record da utilizzare in seguito, se si recupera l'entità dal suo id (questo è diverso da quello in cache il risultato della query)

+1

Beh, non esattamente. Cercherà di trovare un colpo nella cache di secondo livello dopo aver creato il risultato. Una bella spiegazione [qui] (http://www.javalobby.org/java/forums/t48846.html) anche se un po 'datato vale ancora. –

+0

@MangEngkus, grazie per il tuo tempo e la spiegazione. Sfortunatamente, a causa della scadenza del progetto, abbiamo deciso di non implementare l'ehcache, ma lo faremo in futuro. Ho provato a usare query.setHint ("org.hibernate.cacheable", true) in un progetto demo e funziona, quindi abbiamo bisogno di riadattare il nostro codice per usarlo. Terrò comunque aperta la domanda Se qualcuno ha lo stesso problema e trova un modo per usare ehcache con QueryDslPredicateExecutor e trovare una soluzione. –

+0

@CharlieG Hai provato a utilizzare: ** org.hibernate.cache.SingletonEhCacheProvider ** e ** org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory ** per * hibernate.cache.provider_class * e * hibernate.cache.region.factory_class * rispettivamente? Siamo in grado di far funzionare la cache con Spring JPA e QueryDSL utilizzati nel nostro progetto. Sarò in grado di inviare una risposta a breve. –

0

Prova questo

@CacheConfig(cacheNames = "com.abc.domain.State") 
public interface StateRepository extends CrudRepository<State, Integer>, QueryDslPredicateExecutor<State> { 

    @QueryHints(value = { 
      @QueryHint(name = "org.hibernate.cacheable", value = "true"), 
      @QueryHint(name = "org.hibernate.cacheMode", value = "NORMAL"), 
      @QueryHint(name = "org.hibernate.cacheRegion", value = "CacheRegion") 
    }) 
    Page<State> findAll(Predicate predicate, Pageable pageable); 

} 

Ha funzionato per me.

0

Questo è ciò che viene fatto nel nostro caso. In effetti, sono stati utilizzati Spring JPA e QueryDSL. In un'altra nota, QueryDSL non impedisce né ha nulla a che fare con la memorizzazione nella cache. Spero che questa risposta aiuti.

Di seguito è riportata la configurazione del codice che è necessario elaborare.

gestore di entità di fagioli config

@Configuration 
@EnableJpaRepositories(
    basePackages = { "com.abc.examples.persistence.repository" }, 
    entityManagerFactoryRef = "entityManagerFactory", 
    transactionManagerRef = "transactionManager" 
) 
@ComponentScan("com.abc.examples.persistence") 
public class PersistenceConfig { 

    /*Code omitted. Beans configured for other items like testDataSource, transactionManager*/ 

    @Bean(name = "entityManagerFactory") 
    public LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean(
     @Qualifier("testDataSource") DataSource dataSource, 
     @Value("${hibernate.show_sql}") String hibernateShowSql, 
     @Value("${hibernate.generate_statistics}") String hibernateShowStats 
     ) { 
     LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean(); 
     factoryBean.setDataSource(dataSource); 
     factoryBean.setPackagesToScan("com.abc.examples.persistence.entity"); 
     factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); 
     factoryBean.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE); 

     Properties hibernateProperties = new Properties(); 
     hibernateProperties.setProperty("hibernate.cache.provider_class", "org.hibernate.cache.SingletonEhCacheProvider"); 
     hibernateProperties.setProperty("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"); 
     hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", "true"); 
     hibernateProperties.setProperty("hibernate.cache.use_query_cache", "true"); 

     factoryBean.setJpaProperties(hibernateProperties); 
     return factoryBean; 
    } 
} 

Cose importanti da cercare nella configurazione di cui sopra sono

factoryBean.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE); 
... 
... 
hibernateProperties.setProperty("hibernate.cache.provider_class", "org.hibernate.cache.SingletonEhCacheProvider"); 
hibernateProperties.setProperty("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"); 
hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", "true"); 
hibernateProperties.setProperty("hibernate.cache.use_query_cache", "true"); 

cache di fagioli Responsabile config (abbiamo usato EHCache)

@Configuration 
@EnableCaching 
public class CacheConfig { 

    @Bean(name = "cacheManager") 
    public EhCacheCacheManager ehCacheCacheManager() { 
     return new EhCacheCacheManager(ehCache()); 
    } 

    @Bean 
    public CacheManager ehCache() { 
     CacheManager cacheManager = CacheManager.create(); 

     Cache sampleEntityCache = new Cache(
      new CacheConfiguration("com.abc.examples.entity.SampleEntity", 500) 
        .memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LRU) 
        .eternal(false) 
        .timeToLiveSeconds(60 * 60 * 24) 
        .timeToIdleSeconds(60 * 60 * 24) 
        .persistence(new PersistenceConfiguration().strategy(PersistenceConfiguration.Strategy.NONE)) 
     ); 
     cacheManager.addCache(sampleEntityCache); 

     return cacheManager; 
    } 
} 

classe Entity

import org.hibernate.annotations.Cache; 
import org.hibernate.annotations.CacheConcurrencyStrategy; 

@Cache(usage= CacheConcurrencyStrategy.READ_ONLY, 
    region="com.abc.examples.entity.SampleEntity") //This name should match with the name used in the CacheConfiguration above. 
public class SampleEntity{ 
    @Id 
    ... 
    ... 
    .. 
}