web-dev-qa-db-fra.com

Spring Boot + JPA2 + Hibernate - active le cache de second niveau

J'utilise Spring Boot 1.2.5 avec JPA2 pour annoter des entités (et hiberner en tant que sous-jacentes à l'implémentation JPA).

Je voulais utiliser le cache de second niveau dans cette configuration, donc les entités ont été annotées avec @javax.persistence.Cacheable

J'ai aussi ajouté ce qui suit dans application.properties:

spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.use_query_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory

Pendant le démarrage, Hibernate s’est plaint du manque de EhCacheRegionFactory alors j’ajoute également ceci à pom:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-ehcache</artifactId>
</dependency>

Néanmoins, des requêtes telles que entityManager.find(Clazz.class, pk) déclenchent une requête de base de données au lieu d’utiliser des données mises en cache.

Une idée de ce qui manque?

16
Daimon

Eh bien, après quelques recherches supplémentaires, voici ce qui me manquait dans application.properties:

spring.jpa.properties.javax.persistence.sharedCache.mode=ALL

J'espère que ça aide quelqu'un :)

26
Daimon

@Daimon Je ne suis pas vraiment sûr de savoir si

spring.jpa.properties.javax.persistence.sharedCache.mode=ALL

est la meilleure décision.

Cité de Hibernate 20.2.1. Section de documentation sur les mappages de cache

Par défaut, les entités ne font pas partie du cache de second niveau et nous vous recommandons de vous en tenir à ce paramètre. Toutefois, vous pouvez remplacer ce paramètre en définissant l'élément shared-cache-mode dans votre fichier persistence.xml ou en utilisant la propriété javax.persistence.sharedCache.mode dans votre configuration.

tandis que

ENABLE_SELECTIVE (valeur par défaut et recommandée): les entités ne sont pas mises en cache sauf si explicitement marquées comme pouvant être mises en cache.

Alors, est-il possible que vous n'ayez pas annoté toutes les entités affectées avec @ javax.persistence.Cacheable ou plutôt @ org.hibernate.annotations.Cache? Cela pourrait entraîner le fait que le cache de requête a tenté sans succès de rechercher les entités affectées dans le cache de second niveau, puis a commencé à extraire chaque entité par une sélection unique.

7
GeBeater

As-tu ajouté 

@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY) 

sur la classe que vous voulez mettre en cache?

2
m1416

Vous devriez avoir un fichier ehcache.xml dans votre chemin de classe. Le fichier doit contenir au moins la stratégie de cache par défaut. Pour faciliter le débogage, veillez à ce que les entités ne soient pas expulsées du cache: 

ehcache.xml:

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:noNamespaceSchemaLocation="ehcache.xsd"
  Name="CacheManager" 
  maxBytesLocalHeap="250m">

<defaultCache eternal="true"
...
/>

<cache name="org.hibernate.cache.internal.StandardQueryCache"
       eternal="true"
...
/>

Pour vous assurer que tout va bien, vous devez avoir le journal suivant lors du démarrage de votre application:

Could not find a specific ehcache configuration for cache named [com.yourcompany.YourClass]; Using defaults.

Cela signifie que vos annotations de cache d'entités ont été correctement lues et que le cache par défaut sera utilisé.

Si vous testez avec entityManager.find(Clazz.class, pk), le cache de requêtes n'est pas étendu, mais uniquement le cache d'entités. Le cache de requêtes est utilisé pour les requêtes (em.createQuery (...) et pour les relations

Aussi, j'utilise org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory, mais je ne sais pas ce qui est meilleur.

0
crv

Vous pouvez utiliser un fournisseur de cache tiers, parmi JCache, Ehcache, Cache Gvava, Cache Hazelcast, Cache Caféine.

Veuillez vous référer à cette réponse sur Quora pour savoir comment activer et configurer le cache de second niveau lors du démarrage du printemps.

0
Arun Raaj

Pour tout récapituler (cache L2 et cache de requête):

La première chose à faire est d’ajouter un fournisseur de cache (je recommande d’utiliser EhCache) à votre chemin de classe.

Dans les versions précédentes d'Hibernate (antérieures à la 5.3), cette dépendance était ajoutée. Cette dépendance contient EhCache 2 qui est maintenant abandonnée.

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

Les versions plus récentes des caches Hibernate implémentant l'API JSR-107 (JCache) doivent être utilisées. Donc, il y a 2 dépendances nécessaires - une pour l'API JSR-107 et la seconde pour l'implémentation JCache (EhCache 3).

<dependency>
     <groupId>org.hibernate</groupId>
     <artifactId>hibernate-jcache</artifactId>
     <version>your_hibernate_version</version>
</dependency>

<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.6.3</version>
    <scope>runtime</scope>
</dependency>

Passons maintenant au fichier application.properties/yml:

spring:
  jpa:
    #optional - show SQL statements in console. 
    show-sql: true 
    properties:
      javax:
        persistence:
          sharedCache: 
            #required - enable selective caching mode - only entities using @Cacheable annotation will use L2 cache.
            mode: ENABLE_SELECTIVE 
      hibernate:
        #optional - enable SQL statements formatting.
        format_sql: true 
        #optional - generate statistics to check if L2/query cache is actually being used.
        generate_statistics: true
        cache:
          #required - turn on L2 cache.
          use_second_level_cache: true
          #optional - turn on query cache.
          use_query_cache: true 
          region:
            #required - classpath to cache region factory.
            factory_class: org.hibernate.cache.ehcache.EhCacheRegionFactory 

Pour EhCache 3, cette région doit être utilisée:

factory_class: org.hibernate.cache.jcache.JCacheRegionFactory

Vous pouvez également activer la journalisation au niveau TRACE pour que Hibernate vérifie votre code:

logging:
  level:
    org:
      hibernate:
        type: trace

Passons maintenant au code. Pour activer la mise en cache L2 sur votre entité, vous devez ajouter ces deux annotations:

@javax.persistence.Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE) //Provide cache strategy.
public class MyEntity {
  ...
}

Remarque - si vous souhaitez mettre en cache votre relation @OneToMany ou @ManyToOne - ajoutez également une annotation @Cache sur ce champ.

Et pour activer le cache de requêtes dans votre référentiel spring-data-jpa, vous devez ajouter la variable QueryHint appropriée.

public class MyEntityRepository implements JpaRepository<MyEntity, Long> {

  @QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
  List<MyEntity> findBySomething(String something);

}

Maintenant, vérifiez via les journaux si votre requête est exécutée une seule fois et n'oubliez pas de désactiver tous les éléments de débogage - vous avez terminé.

Note 2 - vous pouvez également définir la stratégie de cache manquante à create si vous souhaitez conserver les valeurs par défaut sans générer d'avertissements dans vos journaux:

spring:
  jpa:
    properties:
      hibernate:
        javax:
          cache:
            missing_cache_strategy: create
0
Michał Stochmal