Как использовать кеш с запросами по разным полям / ключам?
Я реализую веб-приложение на основе Spring MVC, где я добавил кеширование, но теперь сталкиваюсь с проблемой, связанной с тем, как работать с кешем, когда изменения применяются к базовой базе данных.
Я изучал документацию, доступную на Ehcache ( http://ehcache.org/documentation), но не смог найти хороших примеров для решения моей проблемы.
Допустим, у меня есть класс DAO, в котором я решил применить кэш для двух методов, которые возвращают список объектов (некомпилируемый псевдокод):
@Repository
public class MyEntityDao {
@Autowired
DataSource ds;
@Autowired
EhCacheCacheManager cacheManager;
@Autowired
CacheKeyGenerator keyGenerator;
@Cacheable("myEntityCache")
public List<MyEntity> findByFieldAlpha(String fieldAlpha) {
return ds.query("select * from MyEntity where fieldAlpha = #fieldAlpha").execute();
}
@Cacheable("myEntityCache")
public List<MyEntity> findByFieldBeta(String fieldBeta) {
return ds.query("select * from MyEntity where fieldBeta = #fieldBeta").execute();
}
public void deleteByFieldAlpha(String fieldAlpha) {
ds.query("delete from MyEntity where fieldAlpha = #fieldAlpha").execute();
}
public void deleteByFieldBeta(String fieldBeta) {
ds.query("delete from MyEntity where fieldBeta = #fieldBeta").execute();
}
public void store(MyEntity myEntity) {
ds.insert(myEntity).execute();
}
}
Я использую пользовательский KeyGenerator, поэтому я знаю, как генерируются ключи при вызове метода:
public class CacheKeyGenerator implements KeyGenerator {
@Override
public Object generate(final Object target, final Method method, final Object... params) {
return generateCacheKey(method.getName(), params);
}
public String generateCacheKey(final String methodName, final Object... params) {
StringBuilder key = new StringBuilder();
key.append(methodName);
for (Object o : params) {
key.append("_").append(o.toString());
}
return key.toString();
}
}
Очевидная проблема, которую мне нужно решить, - это как реализовать политику модификации кэша, когда данные добавляются или удаляются в базовую базу данных.
Для метода store я нахожу проблему довольно тривиальной:
...
public void store(MyEntity myEntity) {
boolean success = ds.insert(myEntity).execute();
if (success) {
Cache cache = cacheManager.getCache("myEntityCache")
String keyFieldAlpha = keyGenerator.generateCacheKey("findByFieldAlpha", myEntity.getFieldAlpha());
List cacheListFieldAlpha = (List) cache.get(keyFieldAlpha).getObjectValue();
cacheListFieldAlpha.add(myEntity);
String keyFieldBeta = keyGenerator.generateCacheKey("findByFieldBeta", myEntity.getFieldBeta());
List cacheListFieldBeta = (List) cache.get(keyFieldBeta).getObjectValue();
cacheListFieldBeta.add(myEntity);
}
}
Но для методов удаления все становится намного сложнее.
Это одна из реализаций, которую я придумал до сих пор, но она кажется намного более сложной (дорогой), чем должна быть. У кого-нибудь есть ценный вклад, если мои шаблоны проектирования верны, или если должны решить мою проблему каким-либо другим способом?
public void deleteByFieldAlpha(String fieldAlpha) {
List<MyEntity> myEntititesToBeDeleted = this.findByFieldAlpha(fieldAlpha);
boolean success = ds.query("delete from MyEntity where fieldAlpha = #fieldAlpha").execute();
if (success) {
Cache cache = cacheManager.getCache("myEntityCache")
String keyFieldAlpha = keyGenerator.generateCacheKey("findByFieldAlpha", fieldAlpha);
cache.remove(keyFieldAlpha);
for (MyEntity myEntity : myEntititesToBeDeleted) {
String keyFieldBeta = keyGenerator.generateCacheKey("findByFieldBeta", myEntity.getFieldBeta());
List cacheListFieldBeta = (List) cache.get(keyFieldBeta).getObjectValue();
cacheListFieldBeta.remove(myEntity);
}
}
}
Возможно, это правильный способ ее решения? Я не наткнулся на какие-либо рецепты, которые решают эти проблемы, поэтому я предпочитаю здесь.
2 ответа
Используйте API поиска Ehcache, вероятно, в операторе будет полезно в вашем случае.
Я выскажу свое мнение. Кажется, это (почти) правильный подход. Я думаю, что после операции удаления вы должны очистить весь кэш и начать заново. Обычно после операций по изменению данных кэш сбрасывается. Это сделано из-за объединения таблиц. Кажется, это не ваш случай, потому что у вас есть только два запроса и нет объединений, но обычно кэш очищается и перезапускается. При этом вам не нужно сканировать кеш, который ссылается на "fieldBeta" после изменений в "fieldAlfa". Надеюсь, что-нибудь из этого поможет вам. Кстати, MyBatis работает именно так.