web-dev-qa-db-fra.com

Comment utiliser @Cacheable de JPA2 au lieu de @Cache d'Hibernate

En règle générale, j'utilise @ Cache (utilisation = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) d'Hibernate pour mettre en cache une classe @Entity, et cela fonctionne bien.

Dans JPA2, il existe une autre annotation @Cacheable qui semble être la même fonctionnalité que @Cache d'Hibernate. Pour rendre ma classe d'entité indépendante du package de hibernate, je veux l'essayer. Mais je ne peux pas le faire fonctionner. Chaque fois, une simple requête d'ID frappe toujours la base de données.

Quelqu'un peut-il me dire où va mal? Merci.

Classe d'entité:

@Entity
//@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Cacheable(true) 
public class User implements Serializable
{
 // properties
}

Classe de test:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:app.xml"})
@TransactionConfiguration(transactionManager="transactionManager")
public class UserCacheTest
{
  @Inject protected UserDao userDao;

  @Transactional
  @Test
  public void testGet1()
  {
    assertNotNull(userDao.get(2L));
  }

  @Transactional
  @Test
  public void testGet2()
  {
    assertNotNull(userDao.get(2L));
  }

  @Transactional
  @Test
  public void testGet3()
  {
    assertNotNull(userDao.get(2L));
  }
}

Le résultat du test montre chaque couche de base de données "get" hits (avec hibernate.show_sql = true).

Persistence.xml:

<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.use_outer_join" value="true"/>

<property name="hibernate.cache.provider_class" value="org.hibernate.cache.SingletonEhCacheProvider"/>
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<property name="hibernate.cache.use_query_cache" value="true"/>

Code JPA:

@Override
public T get(Serializable id)
{
  return em.find(clazz, id);
}
46
smallufo

Selon la spécification JPA 2.0, si vous souhaitez mettre en cache de manière sélective des entités à l'aide de @Cacheable annotation, vous êtes censé spécifier un <shared-cache-mode> dans le persistence.xml (ou l'équivalent javax.persistence.sharedCache.mode lors de la création du EntityManagerFactory).

Ci-dessous, un exemple persistence.xml avec l'élément et les propriétés appropriés:

<persistence xmlns="http://Java.Sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://Java.Sun.com/xml/ns/persistence http://Java.Sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0">
  <persistence-unit name="FooPu" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    ...
    <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
    <properties>
      ...
      <property name="hibernate.cache.provider_class" value="org.hibernate.cache.SingletonEhCacheProvider"/>
      <property name="hibernate.cache.use_second_level_cache" value="true"/>
      <property name="hibernate.cache.use_query_cache" value="true"/>
    </properties>
  </persistence-unit>
</persistence>

Notez que j'ai vu au moins un problème HHH-53 lié à la mise en cache. Donc, ce qui précède n'est pas garanti :)

Les références

  • Guide de référence Hibernate EntityManager
  • Spécifications JPA 2.0
    • Section 3.7.1 "L'élément en mode cache partagé"
    • Section 11.1.7 "Annotation de mise en cache"
44
Pascal Thivent

Pour ceux qui utilisent Spring config au lieu de persistence.xml, Voici un exemple:

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="database" value="MYSQL"/>
            <property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
            <property name="showSql" value="true"/>
            <property name="generateDdl" value="false"/>
        </bean>
    </property>
    <property name="packagesToScan" value="com.mycompany.myproject.domain"/>
    <property name="jpaPropertyMap">
        <map>
            <entry key="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"/>
            <entry key="hibernate.cache.use_second_level_cache" value="true"/>
            <entry key="hibernate.cache.use_query_cache" value="true"/>
            <entry key="javax.persistence.sharedCache.mode" value="ENABLE_SELECTIVE" />
        </map>
    </property>
</bean>

Notez également que si vous utilisez des annotations @Cacheable, Vous ne pouvez utiliser qu'une stratégie de concurrence par défaut du cache, qui est déterminée par la méthode getDefaultAccessType() de RegionFactory. Dans le cas d'EhCache, c'est READ_WRITE . Si vous souhaitez utiliser une autre stratégie, vous devez utiliser les annotations @Cache D'Hibernate.

11
John29