Можно ли удалить частичные данные весеннего кеша?

Учитывая эти сущности и репозиторий для доступа к их данным в DDBB:

@Entity
public class Customer {
Long id;
}

@Entity
public class Purchase {
    Long customerId;
}

@Repository
public lass PurchaseDAO {

   public void insert(Purchase insert);

   public void deleteCustomerPurchases(Long customerId);

   public long getTotalPurchasesAmount(Long customerId);

   public long getTotalPurchasesAmountPerMonth(Long customerId, int month);
}

Я хочу добавить кеш для метода getTotalPurchaseAmounts (Long customerId) таким образом, чтобы при добавлении какой-либо покупки для клиента удалялись только покупки этого клиента.

Соответствующие записи зависимостей:

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>2.10.2</version>
        </dependency>

Связанная конфигурация:

@EnableCaching
@Configuration
public class CommonConfig {

    @Bean
    public CacheManager cacheManager() {
        EhCacheCacheManager cacheManager = new EhCacheCacheManager();
        cacheManager.setCacheManager( ehCacheManager().getObject() );
        return cacheManager;
    }

    @Bean
    public EhCacheManagerFactoryBean ehCacheManager() {
        EhCacheManagerFactoryBean ehcache = new EhCacheManagerFactoryBean();
        ehcache.setConfigLocation( new ClassPathResource( "ehcache.xml" ) );
        return ehcache;
    }

}

Поскольку выселение весеннего кеша (и ehcache) ограничено для каждого элемента или всех записей, разработанное мной решение создает кеши динамически (по одному для каждого клиента), чтобы я мог их выселить.

Точка расширения, которую я считаю лучшей для этого, - это реализация настраиваемого CacheResolver:

@Component("CustomerPurchasesCacheResolver")
public class CustomerPurchasesCacheResolver implements CacheResolver {

    @Autowired
    private EhCacheCacheManager cacheManager;

    @Override
    public Collection<? extends Cache> resolveCaches( CacheOperationInvocationContext<?> context ) {
        String cacheName = "customerPurchases_" + getCustomerId( context );
        // Add cache to cacheManager if it does not exists
        cacheManager.getCacheManager().addCacheIfAbsent( cacheName );

        Set<Cache> caches = new HashSet<>();
        caches.add( cacheManager.getCache( cacheName ) );
        return caches;
    }

    // Retrieves customerId from cache operation invocation context;
    private Long getCustomerId( CacheOperationInvocationContext<?> context ) {
    String key = ( (CacheOperation) context.getOperation() ).getKey();
        // TODO Evaluate key
        // HOW CAN I DO THIS????????????
        return null;
    }

}

и добавление кеша Spring в методы моего репозитория:

@Repository
public lass PurchaseDAO {

   @CacheEvict(cacheResolver="CustomerPurchasesCacheResolver", key="#purchase.customerId")       
   public void insert(Purchase purchase);

   @CacheEvict(cacheResolver="CustomerPurchasesCacheResolver", key="#customerId")       
   public void deleteCustomerPurchases(Long customerId);

   @Cacheable(cacheResolver="CustomerPurchasesCacheResolver")
   public long getTotalPurchasesAmount(Long customerId);

   @Cacheable(cacheResolver="CustomerPurchasesCacheResolver")
   public long getTotalPurchasesAmountPerMonth(Long customerId, int month);

}

Единственная проблема, с которой я сталкиваюсь, используя этот подход, - это получить оценку ключа с помощью Spring Expresions.

Есть ли способ получить тот или иной подход, который мог бы сработать?


person Basa    schedule 15.05.2016    source источник


Ответы (1)


Создание кеша для каждой записи о клиенте - излишество. Используя SpEL, вы можете указать ключ для записи клиента, которую нужно выселить. Настройте ehcache так, чтобы был один клиентский кеш. Затем вы меняете свои методы PurchaseDAO, чтобы они указывали ключ для кеширования или удаления. Измените свой код следующим образом

@CacheEvict(value = "customerCache" , key="#purchase.customerId")       
public void insert(Purchase purchase);

@Cacheable( value = "customerCache" ,key = "#customerId") // you can omit the key if using default key generator as it still uses the method argument
public long getTotalPurchasesAmount(Long customerId);

Однако, чтобы ответить на ваш вопрос о получении customerId из CacheResolver, CacheOperationInvocationContext имеет метод getArgs (), который возвращает аргументы, переданные методу для кэширования.

 private Long getCustomerId( CacheOperationInvocationContext<?> context ) {
        Object[] args = context.getArgs();
        Object firstArg = args[0];
        if(firstArg instanceof Long){
           return (Long)firstArg;
        }
        else if(firstArg instanceof Purchase){
            Purchase purchase = (Purchase)firstArg;
            return purchase.getCustomerId();
        }
        return null;
    }
person ekem chitsiga    schedule 15.05.2016
comment
Спасибо, Экем. Дело в том, что для каждого клиента будет более одной записи (например, @Cacheable public int getPurchasesAmountPerMonth (int month)) и методов с другой сигнатурой, которые удаляют кеш, поэтому просто получение args [0] не работает. Вы дали мне понять, что в моем первоначальном вопросе не хватает ключевой информации. Я собираюсь отредактировать это, чтобы показать эту идею как суть проблемы. - person Basa; 16.05.2016