【问题标题】:Is it possible to evict spring cache partial data?是否可以驱逐弹簧缓存部分数据?
【发布时间】:2016-09-11 13:10:34
【问题描述】:

鉴于这些实体和存储库可以访问它们在 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;
    }

}

由于 spring 缓存(和 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 表达式评估密钥。

有什么方法可以实现这一点或其他可行的方法吗?

【问题讨论】:

    标签: java spring caching ehcache spring-cache


    【解决方案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);
    

    但是,要回答有关从 CacheResolver 获取 customerId 的问题,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;
        }
    

    【讨论】:

    • 谢谢埃克姆。问题是每个客户将有不止一条记录(即@Cacheable public int getPurchasesAmountPerMonth(int month)),以及具有不同签名的方法来驱逐缓存,因此仅获取 args[0] 并不能完成这项工作。你让我意识到我最初的问题缺乏关键信息。我将对其进行编辑,以表明该想法是问题的重点。
    猜你喜欢
    • 1970-01-01
    • 2023-03-12
    • 2018-08-20
    • 2014-03-03
    • 1970-01-01
    • 2018-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多