Puis-je définir un TTL pour @Cacheable

101

J'essaie la @Cacheableprise en charge des annotations pour Spring 3.1 et je me demande s'il existe un moyen de supprimer les données en cache après un certain temps en définissant un TTL? À l'heure actuelle, d'après ce que je peux voir, je dois le nettoyer moi-même en utilisant le @CacheEvict, et en l'utilisant avec @Scheduledje peux faire moi-même une implémentation TTL, mais cela semble un peu trop pour une tâche aussi simple?

Piotr
la source

Réponses:

39

Voir http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/htmlsingle/spring-framework-reference.html#cache-specific-config :

Comment puis-je définir la fonctionnalité TTL / TTI / Politique d'expulsion / XXX?

Directement via votre fournisseur de cache. L'abstraction du cache est ... eh bien, une abstraction pas une implémentation de cache

Donc, si vous utilisez EHCache, utilisez la configuration d'EHCache pour configurer le TTL.

Vous pouvez également utiliser le CacheBuilder de Guava pour créer un cache et transmettre la vue ConcurrentMap de ce cache à la méthode setStore de ConcurrentMapCacheFactoryBean .

JB Nizet
la source
57

Spring 3.1 et Guava 1.13.1:

@EnableCaching
@Configuration
public class CacheConfiguration implements CachingConfigurer {

    @Override
    public CacheManager cacheManager() {
        ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager() {

            @Override
            protected Cache createConcurrentMapCache(final String name) {
                return new ConcurrentMapCache(name,
                    CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.MINUTES).maximumSize(100).build().asMap(), false);
            }
        };

        return cacheManager;
    }

    @Override
    public KeyGenerator keyGenerator() {
        return new DefaultKeyGenerator();
    }

}
Magnus Heino
la source
21
Pour Spring 4.1, étendez CachingConfigurerSupport et écrasez uniquement cacheManager ().
Johannes Flügel
39

Voici un exemple complet de configuration de Guava Cache au printemps. J'ai utilisé Guava plutôt qu'Ehcache car il est un peu plus léger et la configuration me paraissait plus simple.

Importer les dépendances Maven

Ajoutez ces dépendances à votre fichier maven pom et exécutez clean and packages. Ces fichiers sont les méthodes d'assistance Guava dep et Spring à utiliser dans CacheBuilder.

    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>18.0</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>4.1.7.RELEASE</version>
    </dependency>

Configurer le cache

Vous devez créer un fichier CacheConfig pour configurer le cache à l'aide de Java config.

@Configuration
@EnableCaching
public class CacheConfig {

   public final static String CACHE_ONE = "cacheOne";
   public final static String CACHE_TWO = "cacheTwo";

   @Bean
   public Cache cacheOne() {
      return new GuavaCache(CACHE_ONE, CacheBuilder.newBuilder()
            .expireAfterWrite(60, TimeUnit.MINUTES)
            .build());
   }

   @Bean
   public Cache cacheTwo() {
      return new GuavaCache(CACHE_TWO, CacheBuilder.newBuilder()
            .expireAfterWrite(60, TimeUnit.SECONDS)
            .build());
   }
}

Annoter la méthode à mettre en cache

Ajoutez l'annotation @Cacheable et transmettez le nom du cache.

@Service
public class CachedService extends WebServiceGatewaySupport implements CachedService {

    @Inject
    private RestTemplate restTemplate;


    @Cacheable(CacheConfig.CACHE_ONE)
    public String getCached() {

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        HttpEntity<String> reqEntity = new HttpEntity<>("url", headers);

        ResponseEntity<String> response;

        String url = "url";
        response = restTemplate.exchange(
                url,
                HttpMethod.GET, reqEntity, String.class);

        return response.getBody();
    }
}

Vous pouvez voir un exemple plus complet ici avec des captures d'écran annotées: Guava Cache in Spring

anataliocs
la source
2
Remarque: le cache Guava est désormais obsolète au printemps 5 ( stackoverflow.com/questions/44175085/… )
Amin Ziaei
33

J'utilise le piratage de la vie comme ça

@Configuration
@EnableCaching
@EnableScheduling
public class CachingConfig {
    public static final String GAMES = "GAMES";
    @Bean
    public CacheManager cacheManager() {
        ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager(GAMES);

        return cacheManager;
    }

@CacheEvict(allEntries = true, value = {GAMES})
@Scheduled(fixedDelay = 10 * 60 * 1000 ,  initialDelay = 500)
public void reportCacheEvict() {
    System.out.println("Flush Cache " + dateFormat.format(new Date()));
}
Atum
la source
Appelez-vous la reportCacheEvictméthode de n'importe où. Comment se passe le cacheEvict ??
Jaikrat
Tu piges. Nous n'appelons cette méthode de nulle part. Il est appelé après l'intervalle de temps fixedDelay. Merci pour l'indice.
Jaikrat
1
Effacer tout le cache selon un calendrier peut être un hack pratique pour faire fonctionner les choses, mais cette méthode ne peut pas être utilisée pour donner un TTL aux éléments. Même la valeur de la condition ne peut déclarer que la suppression de l'intégralité du cache. Sous-jacent, il y a le fait que ConcurrentMapCache stocke les objets sans horodatage, il n'y a donc aucun moyen d'évaluer un TTL tel quel.
jmb
est le code siège-du-pantalon (cette méthode a été griffonnée :)).
Atum
Approche agréable et propre
lauksas
31

Springboot 1.3.8

import java.util.concurrent.TimeUnit;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.guava.GuavaCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.common.cache.CacheBuilder;

@Configuration
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {

@Override
@Bean
public CacheManager cacheManager() {
    GuavaCacheManager cacheManager = new GuavaCacheManager();
    return cacheManager;
}

@Bean
public CacheManager timeoutCacheManager() {
    GuavaCacheManager cacheManager = new GuavaCacheManager();
    CacheBuilder<Object, Object> cacheBuilder = CacheBuilder.newBuilder()
            .maximumSize(100)
            .expireAfterWrite(5, TimeUnit.SECONDS);
    cacheManager.setCacheBuilder(cacheBuilder);
    return cacheManager;
}

}

et

@Cacheable(value="A", cacheManager="timeoutCacheManager")
public Object getA(){
...
}
jo8937
la source
Incroyable! C'était exactement ce que je cherchais
MerLito
6

cela peut être fait en étendant org.springframework.cache.interceptor.CacheInterceptor et en surchargeant la méthode "doPut" - org.springframework.cache.interceptor.AbstractCacheInvoker votre logique de remplacement doit utiliser la méthode put du fournisseur de cache qui sait définir TTL pour l'entrée de cache (dans mon cas, j'utilise HazelcastCacheManager)

@Autowired
@Qualifier(value = "cacheManager")
private CacheManager hazelcastCacheManager;

@Override
protected void doPut(Cache cache, Object key, Object result) {
        //super.doPut(cache, key, result); 
        HazelcastCacheManager hazelcastCacheManager = (HazelcastCacheManager) this.hazelcastCacheManager;
        HazelcastInstance hazelcastInstance = hazelcastCacheManager.getHazelcastInstance();
        IMap<Object, Object> map = hazelcastInstance.getMap("CacheName");
        //set time to leave 18000 secondes
        map.put(key, result, 18000, TimeUnit.SECONDS);



}

sur votre configuration de cache, vous devez ajouter ces 2 méthodes de bean, en créant votre instance d'intercepteur personnalisée.

@Bean
public CacheOperationSource cacheOperationSource() {
    return new AnnotationCacheOperationSource();
}


@Primary
@Bean
public CacheInterceptor cacheInterceptor() {
    CacheInterceptor interceptor = new MyCustomCacheInterceptor();
    interceptor.setCacheOperationSources(cacheOperationSource());    
    return interceptor;
}

Cette solution est bonne lorsque vous souhaitez définir le TTL au niveau d'entrée, et non globalement au niveau du cache

lukass77
la source
2

Depuis Spring-boot 1.3.3, vous pouvez définir le délai d'expiration dans CacheManager en utilisant RedisCacheManager.setExpires ou RedisCacheManager.setDefaultExpiration dans le bean de rappel CacheManagerCustomizer .

Andrew
la source
0

Lors de l'utilisation de Redis, TTL peut être défini dans le fichier de propriétés comme ceci:

spring.cache.redis.time-to-live=1d # 1 day

spring.cache.redis.time-to-live=5m # 5 minutes

spring.cache.redis.time-to-live=10s # 10 seconds

Hamid Mohayeji
la source
-2

Si vous travaillez avec redis et Java 8, vous pouvez jeter un œil à JetCache :

@Cached(expire = 10, timeUnit = TimeUnit.MINUTES) User getUserById(long userId);

Huang Li
la source
1
La question est pour le printemps @ Annotation
cacheable