2013-07-19 17 views
11

C'è un modo per utilizzare caratteri jolly in @CacheEvict?Spring @CacheEvict utilizzando caratteri jolly

Ho un'applicazione con multi-tenancy che a volte ha bisogno di sfrattare tutti i dati dalla cache del tenant, ma non di tutti i tenant nel sistema.

Si consideri il seguente metodo:

@Cacheable(value="users", key="T(Security).getTenant() + #user.key") 
public List<User> getUsers(User user) { 
    ... 
} 

Quindi, mi piacerebbe fare qualcosa di simile:

@CacheEvict(value="users", key="T(Security).getTenant() + *") 
public void deleteOrganization(Organization organization) { 
    ... 
} 

Esiste un modo per farlo?

risposta

1

Come nel caso del 99% di ogni domanda nell'universo, la risposta è: dipende. Se il gestore della cache implementa qualcosa che lo gestisce, ottimo. Ma non sembra essere il caso.

Se si utilizza SimpleCacheManager, che è un gestore di cache in memoria di base fornito da Spring, probabilmente si utilizza ConcurrentMapCache fornito anche con Spring. Sebbene non sia possibile estendere lo ConcurrentMapCache per gestire i caratteri jolly nelle chiavi (poiché l'archivio della cache è privato e non è possibile accedervi), puoi semplicemente utilizzarlo come ispirazione per la tua implementazione.

Di seguito c'è una possibile implementazione (non l'ho testata molto se non per verificare se funziona). Questa è una semplice copia di ConcurrentMapCache con una modifica sul metodo evict(). La differenza è che questa versione di evict() tratta la chiave per vedere se si tratta di un'espressione regolare. In tal caso, itera su tutte le chiavi nel negozio e sfrutta quelle che corrispondono alla regex.

package com.sigraweb.cache; 

import java.io.Serializable; 
import java.util.concurrent.ConcurrentHashMap; 
import java.util.concurrent.ConcurrentMap; 

import org.springframework.cache.Cache; 
import org.springframework.cache.support.SimpleValueWrapper; 
import org.springframework.util.Assert; 

public class RegexKeyCache implements Cache { 
    private static final Object NULL_HOLDER = new NullHolder(); 

    private final String name; 

    private final ConcurrentMap<Object, Object> store; 

    private final boolean allowNullValues; 

    public RegexKeyCache(String name) { 
     this(name, new ConcurrentHashMap<Object, Object>(256), true); 
    } 

    public RegexKeyCache(String name, boolean allowNullValues) { 
     this(name, new ConcurrentHashMap<Object, Object>(256), allowNullValues); 
    } 

    public RegexKeyCache(String name, ConcurrentMap<Object, Object> store, boolean allowNullValues) { 
     Assert.notNull(name, "Name must not be null"); 
     Assert.notNull(store, "Store must not be null"); 
     this.name = name; 
     this.store = store; 
     this.allowNullValues = allowNullValues; 
    } 

    @Override 
    public final String getName() { 
     return this.name; 
    } 

    @Override 
    public final ConcurrentMap<Object, Object> getNativeCache() { 
     return this.store; 
    } 

    public final boolean isAllowNullValues() { 
     return this.allowNullValues; 
    } 

    @Override 
    public ValueWrapper get(Object key) { 
     Object value = this.store.get(key); 
     return toWrapper(value); 
    } 

    @Override 
    @SuppressWarnings("unchecked") 
    public <T> T get(Object key, Class<T> type) { 
     Object value = fromStoreValue(this.store.get(key)); 
     if (value != null && type != null && !type.isInstance(value)) { 
      throw new IllegalStateException("Cached value is not of required type [" + type.getName() + "]: " + value); 
     } 
     return (T) value; 
    } 

    @Override 
    public void put(Object key, Object value) { 
     this.store.put(key, toStoreValue(value)); 
    } 

    @Override 
    public ValueWrapper putIfAbsent(Object key, Object value) { 
     Object existing = this.store.putIfAbsent(key, value); 
     return toWrapper(existing); 
    } 

    @Override 
    public void evict(Object key) { 
     this.store.remove(key); 
     if (key.toString().startsWith("regex:")) { 
      String r = key.toString().replace("regex:", ""); 
      for (Object k : this.store.keySet()) { 
       if (k.toString().matches(r)) { 
        this.store.remove(k); 
       } 
      } 
     } 
    } 

    @Override 
    public void clear() { 
     this.store.clear(); 
    } 

    protected Object fromStoreValue(Object storeValue) { 
     if (this.allowNullValues && storeValue == NULL_HOLDER) { 
      return null; 
     } 
     return storeValue; 
    } 

    protected Object toStoreValue(Object userValue) { 
     if (this.allowNullValues && userValue == null) { 
      return NULL_HOLDER; 
     } 
     return userValue; 
    } 

    private ValueWrapper toWrapper(Object value) { 
     return (value != null ? new SimpleValueWrapper(fromStoreValue(value)) : null); 
    } 

    @SuppressWarnings("serial") 
    private static class NullHolder implements Serializable { 
    } 
} 

Confido che i lettori sappiano come inizializzare il gestore cache con un'implementazione cache personalizzata. C'è molta documentazione là fuori che ti mostra come farlo.Dopo che il progetto è configurato correttamente, è possibile utilizzare l'annotazione normalmente in questo modo:

@CacheEvict(value = { "cacheName" }, key = "'regex:#tenant'+'.*'") 
public myMethod(String tenant){ 
... 
} 

Ancora una volta, questo è ben lungi dall'essere adeguatamente testati, ma ti dà un modo per fare quello che vuoi. Se si sta utilizzando un altro gestore cache, è possibile estendere l'implementazione della cache in modo simile.

3

risposta è: No.

E non è un modo semplice per ottenere quello che vuoi.

  1. Le annotazioni di Spring Cache devono essere semplici da implementare facilmente dal provider cache.
  2. Il caching efficiente deve essere semplice. C'è una chiave e un valore. Se la chiave è trovata nella cache usa il valore, altrimenti calcola il valore e metti in cache. La chiave efficiente deve essere veloce e onesta uguale a() e hashcode(). Supponi di aver memorizzato nella cache molte coppie (chiave, valore) da un tenant. Per l'efficienza, le diverse chiavi devono avere un codice diverso hashcode(). E tu decidi di sfrattare l'intero inquilino. Non è facile trovare elementi tenant nella cache. Devi ripetere tutte le coppie memorizzate nella cache e scartare le coppie appartenenti all'inquilino. Non è efficiente È piuttosto non atomico, quindi è complicato e richiede una certa sincronizzazione. La sincronizzazione non è efficiente.

Pertanto n.

Ma, se trovi una soluzione dimmi, perché la funzione che desideri è davvero utile.

Problemi correlati