2013-07-19 14 views
11

Czy jest jakiś sposób używania symboli wieloznacznych w @CacheEvict?Spring @ CacheEvict przy użyciu symboli wieloznacznych

Mam aplikacji z wielu najmu że czasami potrzebuje eksmitować wszystkie dane z pamięci podręcznej najemcy, ale nie od wszystkich lokatorów w systemie.

Rozważmy następującą metodę:

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

tak, to chciałbym zrobić coś takiego:

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

Czy mimo to zrobić?

Odpowiedz

1

Podobnie jak w przypadku 99% wszystkich pytań we wszechświecie, odpowiedź brzmi: to zależy. Jeśli twój menedżer pamięci podręcznej zaimplementuje coś, co się z tym wiąże, to świetnie. Ale tak się nie wydaje.

Jeśli używasz SimpleCacheManager, który jest podstawowym menedżerem pamięci podręcznej w pamięci dostarczonym przez Spring, prawdopodobnie używasz ConcurrentMapCache, który również pochodzi ze Spring. Chociaż nie jest możliwe rozszerzenie ConcurrentMapCache w celu obsługi symboli wieloznacznych w kluczach (ponieważ magazyn pamięci podręcznej jest prywatny i nie masz do niego dostępu), możesz po prostu użyć go jako inspiracji dla własnej implementacji.

Poniżej przedstawiono możliwą implementację (nie testowałem jej zbyt wiele, poza sprawdzaniem, czy działa). Jest to zwykła kopia ConcurrentMapCache z modyfikacją metody evict(). Różnica polega na tym, że ta wersja evict() traktuje klucz, aby sprawdzić, czy jest to wyrażenie regularne. W takim przypadku następuje iteracja wszystkich kluczy w sklepie i usunięcie tych, które pasują do wyrażenia regularnego.

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 { 
    } 
} 

Ufam, że czytelnicy wiedzą, jak zainicjować menedżera pamięci podręcznej za pomocą niestandardowej implementacji pamięci podręcznej. Istnieje wiele dokumentacji, która pokazuje, jak to zrobić.Po projekt jest prawidłowo skonfigurowany, można użyć adnotacji zwykle tak:

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

Ponownie, jest to dalekie od odpowiednio przetestowane, ale to daje drogę do tego, co chcesz. Jeśli używasz innego menedżera pamięci podręcznej, możesz podobnie rozszerzyć implementację pamięci podręcznej.

3

Odpowiedź brzmi: Nie.

Nie jest to łatwy sposób na osiągnięcie tego, co chcesz.

  1. Adnotacje w Spring Cache muszą być łatwe do wdrożenia przez dostawcę cache.
  2. Efektywne buforowanie musi być proste. Jest klucz i wartość. Jeśli klucz znajduje się w pamięci podręcznej, użyj wartości, w przeciwnym razie wylicz wartość i umieść w pamięci podręcznej. Wydajny klucz musi mieć szybki i uczciwy równy() i hashcode(). Załóżmy, że buforujesz wiele par (klucz, wartość) od jednego dzierżawcy. W celu zwiększenia wydajności różne klucze powinny mieć różne hashcode() (). I decydujesz się na eksmisję całego lokatora. Nie jest łatwo znaleźć elementy dzierżawy w pamięci podręcznej. Musisz powtórzyć wszystkie zbuforowane pary i odrzucić pary należące do najemcy. To nie jest wydajne. Nie jest raczej atomowy, więc jest skomplikowany i wymaga pewnej synchronizacji. Synchronizacja nie jest wydajna.

Dlatego nie.

Ale jeśli znajdziesz rozwiązanie, powiedz mi, ponieważ funkcja, którą chcesz, jest naprawdę przydatna.

Powiązane problemy