web-dev-qa-db-fra.com

Spring Cache avec collection d'éléments / entités

J'utilise Spring Cache, où je passe une collection de clés, et le retour est une liste d'entités. Je voudrais que le cadre de mise en cache comprenne que chaque élément de la liste de retour doit être mis en cache avec le code correspondant. Pour le moment, il semble que la clé soit la liste entière, et si je manque une clé lors de l'appel suivant, elle essaiera de recharger à nouveau toute la collection.

@Override
@Cacheable(value = "countries")
public List<Country> getAll(List<String>codes) {
    return countryDao.findAllInCodes(codes);
}

une autre possibilité est que le retour soit une carte, de la même manière, je voudrais que le cache soit suffisamment intelligent pour interroger uniquement les éléments qui n'ont jamais été interrogés auparavant, également pour les mettre en cache chaque élément avec sa clé.

@Override
@Cacheable(value = "countries")
public Map<String,Country> getAllByCode(List<String>codes) {
    return countryDao.findAllInCodes(codes);
}

Supposons que la classe de pays ressemble à ceci:

class Country{
  String code; 
  String fullName;
  long id;

... // getters setters constructurs etc.. 
}

Est-ce possible avec Spring Cache?

16
Charbel

En fait, c'est possible, même avec Spring's Caching Abstraction , mais pas prêt à l'emploi (OOTB). Essentiellement, vous devez personnaliser Spring's l'infrastructure de mise en cache (expliqué ci-dessous)

Par par défaut , Spring's l'infrastructure de mise en cache utilise l'intégralité de @Cacheable arguments de paramètre de méthode comme "clé" de cache, comme expliqué ici . Bien sûr, vous pouvez également personnaliser la résolution de la clé en utilisant une Expression SpEL ou avec une implémentation personnalisée de KeyGenerator, comme expliqué ici .

Pourtant, ne décompose pas la collection ou le tableau des arguments de paramètre avec le @Cacheable la valeur de retour de la méthode dans les entrées de cache individuelles (c'est-à-dire les paires clé/valeur basées sur le tableau/la collection ou la carte).

Pour cela, vous avez besoin d'une implémentation personnalisée des interfaces SpringCacheManager (en fonction de votre stratégie de mise en cache/fournisseur) et Cache.

REMARQUE: Ironiquement, ce sera la troisième fois que je répondrai à presque la même question, d'abord ici , puis ici et maintenant ici , :-). En tous cas...

J'ai mis à jour/nettoyé mon exemple (un peu) pour cette publication.

Notez que mon exemple étend et personnalise le ConcurrentMapCacheManager fourni dans le Spring Framework lui-même.

Théoriquement, vous pouvez étendre/personnaliser n'importe quelle implémentation de CacheManager, comme Redis dans Spring Data Redis, ici ( source ), ou Pivotal GemFire ​​CacheManager in Spring Data GemFire, ici ( source ). La version open source de Pivotal GemFire ​​ est Apache Geode , qui a un Spring Data Geode projet, ( source pour CacheManager in Spring Data Geode, qui est fondamentalement identique à SD GemFire). Bien sûr, vous pouvez appliquer cette technique à d'autres fournisseurs de cache ... Hazelcast, Ehcache, etc.

Cependant, les entrailles réelles du travail sont gérées par le implémentation personnalisée (ou plus spécifiquement, le classe de base ) de Spring's - Cache interface.

Quoi qu'il en soit, espérons-le mon exemple , vous serez en mesure de comprendre ce que vous devez faire dans votre application pour satisfaire les exigences de mise en cache de votre application.

De plus, vous pouvez appliquer la même approche à la gestion de Maps, mais je vais laisser cela comme un exercice pour vous, ;-).

J'espère que cela t'aides!

À la vôtre, John

16
John Blum

Avec @CachePut et une méthode d'assistance, vous pouvez le réaliser très simplement comme ceci:

public List<Country> getAllByCode(List<String>codes) {
    return countryDao.findAllInCodes(codes);
}

public void preloadCache(List<String>codes) {
    Map<String,Country> allCountries = getAllByCode(codes);
    for (Country country : allCountries) {
        cacheCountry(country);
    }
}

@CachePut
public Country cacheCountry(Country country) {
    return country
}

Remarque

Cela n'ajoutera que des valeurs au cache, mais ne supprimera jamais les anciennes valeurs. Vous pouvez facilement effectuer l'expulsion du cache avant d'ajouter de nouvelles valeurs

Option 2

Il y a une proposition pour le faire fonctionner comme ceci:

@CollectionCacheable
public List<Country> getAllByCode(List<String>codes) {    

Voir:

Si vous êtes impatient, prenez le code de GitHub et intégrez localement

0
Peter Szanto