13

我正在使用 Spring 和 EhCache

我有以下方法

@Override
@Cacheable(value="products", key="#root.target.PRODUCTS")
public Set<Product> findAll() {
    return new LinkedHashSet<>(this.productRepository.findAll());
}

我还有其他使用@Cacheable、@CachePut 和@CacheEvict 的方法。

现在,假设数据库返回 100 个产品并通过 缓存它们key="#root.target.PRODUCTS",然后其他方法将插入 - 更新 - 删除一个项目到数据库中。因此,通过 缓存的产品key="#root.target.PRODUCTS"不再相同,例如数据库。

我的意思是,检查以下两种方法,它们能够更新/删除一个项目,并且同一个项目缓存在另一个中key="#root.target.PRODUCTS"

@Override
@CachePut(value="products", key="#product.id")
public Product update(Product product) {
    return this.productRepository.save(product);
}

@Override
@CacheEvict(value="products", key="#id")
public void delete(Integer id) {
    this.productRepository.delete(id);
}

我想知道是否可以通过 更新/删除位于缓存中的项目key="#root.target.PRODUCTS",如果产品被删除,它将是 100 更新产品或 499。

我的观点是,我想避免以下情况:

@Override
@CachePut(value="products", key="#product.id")
@CacheEvict(value="products", key="#root.target.PRODUCTS")
public Product update(Product product) {
    return this.productRepository.save(product);
}

@Override
@Caching(evict={
        @CacheEvict(value="products", key="#id"),
        @CacheEvict(value="products", key="#root.target.PRODUCTS")
})
public void delete(Integer id) {
    this.productRepository.delete(id);
}

我不想再次调用 500 或 499 产品缓存到key="#root.target.PRODUCTS"

有可能这样做吗?如何?

提前致谢。

4

5 回答 5

6

使用缓存抽象来缓存集合是底层缓存系统正在执行的操作的副本。而且因为这是一个重复项,所以事实证明,您必须以一种或另一种方式在自己的代码中使用某种重复项(该集合的重复键是这种情况的明显表示)。而且因为有重复,你必须以某种方式同步状态

如果您真的需要访问整个集合和单个元素,那么您可能应该使用最简单的捷径。首先,您应该确保您的缓存包含所有不明显的元素。其实远非如此。考虑到你有这个:

//EhCacheCache cache = (EhCacheCache) cacheManager.getCache("products");


@Override
public Set<Product> findAll() {
    Ehcache nativeCache = cache.getNativeCache();
    Map<Object, Element> elements = nativeCache.getAll(nativeCache.getKeys());
    Set<Product> result = new HashSet<Product>();
    for (Element element : elements.values()) {
        result.add((Product) element.getObjectValue());
    }
    return Collections.unmodifiableSet(result);
}

结果elements实际上是一个延迟加载的映射,因此调用values()可能会引发异常。您可能想要遍历键或其他东西。

您必须记住,缓存抽象简化了对底层缓存基础设施的访问,并且绝不会取代它:如果您必须直接使用 API,这可能是您必须以某种方式执行的操作。

现在,如果您认为我们可以改进该区域的缓存抽象,我们可以继续在SPR-12036上进行转换。谢谢!

于 2014-07-28T07:17:27.773 回答
5

我认为这样的事情应该有用......实际上,如果“Stéphane Nicoll”回答当然只是一种变化,但它可能对某人有用。我在这里写它并没有在 IDE 中检查它,但在我的项目中类似的工作。

  1. 覆盖 CacheResolver:

    @Cacheable(value="products", key="#root.target.PRODUCTS", cacheResolver = "customCacheResolver")
    
  2. 实现你自己的缓存解析器,它在你缓存的项目“内部”搜索并在那里完成工作

    public class CustomCacheResolver implements CacheResolver{
      private static final String CACHE_NAME = "products";
      @Autowired(required = true) private CacheManager cacheManager;
    
    @SuppressWarnings("unchecked")
    @Override
    public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> cacheOperationInvocationContext) {
      // 1. Take key from query and create new simple key
      SimpleKey newKey;
      if (cacheOperationInvocationContext.getArgs().length != null) { //optional
        newKey = new SimpleKey(args); //It's the key of cached object, which your "@Cachable" search for           
      } else {
        //Schould never be... DEFAULT work with cache if something wrong with arguments
        return new ArrayList<>(Arrays.asList(cacheManager.getCache(CACHE_NAME)));
      }
    
      // 2. Take cache
      EhCacheCache ehCache = (EhCacheCache)cacheManager.getCache(CACHE_NAME);  //this one we bringing back                
      Ehcache cache = (Ehcache)ehCache.getNativeCache();  //and with this we working        
      // 3. Modify existing Cache if we have it
      if (cache.getKeys().contains(newKey) && YouWantToModifyIt) {
        Element element = cache.get(key);
        if (element != null && !((List<Products>)element.getObjectValue()).isEmpty()) {
        List<Products> productsList = (List<Products>)element.getObjectValue();
        // ---**--- Modify your "productsList" here as you want. You may now Change single element in this list.                   
        ehCache.put(key, anfragenList); //this method NOT adds cache, but OVERWRITE existing                            
      // 4. Maybe "Create" new cache with this key if we don't have it
      } else {
        ehCache.put(newKey, YOUR_ELEMENTS);
      }
      return new ArrayList<>(Arrays.asList(ehCache));  //Bring all back - our "new" or "modified" cache is in there now...
    }
    

阅读更多关于 EhCache 的 CRUD:EhCache 代码示例

希望能帮助到你。对不起我的英语:(

于 2016-07-14T07:37:07.297 回答
2

我认为有一种方法可以从 spring 的底层缓存结构中读取集合。您可以从底层ConcurrentHashMap中检索集合作为键值对,而无需使用EhCache或其他任何东西。然后您可以更新或删除该集合中的条目,然后您也可以更新缓存。这是一个可能有帮助的例子:

import com.crud.model.Post;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.cache.interceptor.SimpleKey;
import org.springframework.stereotype.Component;
import java.util.*;


@Component
@Slf4j
public class CustomCacheResolver implements CacheResolver {

private static final String CACHE_NAME = "allPost";

@Autowired
private CacheManager cacheManager;

@SuppressWarnings("unchecked")
@Override
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> cacheOperationInvocationContext) {

    log.info(Arrays.toString(cacheOperationInvocationContext.getArgs()));

    String method = cacheOperationInvocationContext.getMethod().toString();
    Post post = null;
    Long postId = null;
    if(method.contains("update")) {
        //get the updated post
        Object[] args = cacheOperationInvocationContext.getArgs();
        post = (Post) args[0];
    }
    else if(method.contains("delete")){
        //get the post Id to delete
        Object[] args = cacheOperationInvocationContext.getArgs();
        postId = (Long) args[0];
    }


    //read the cache
    Cache cache = cacheManager.getCache(CACHE_NAME);

    //get the concurrent cache map in key-value pair
    assert cache != null;
    Map<SimpleKey, List<Post>> map = (Map<SimpleKey, List<Post>>) cache.getNativeCache();

    //Convert to set to iterate
    Set<Map.Entry<SimpleKey, List<Post>>> entrySet = map.entrySet();
    Iterator<Map.Entry<SimpleKey, List<Post>>> itr = entrySet.iterator();

    //if a iterated entry is a list then it is our desired data list!!! Yayyy
    Map.Entry<SimpleKey, List<Post>> entry = null;
    while (itr.hasNext()){
        entry = itr.next();
        if(entry instanceof List) break;
    }

    //get the list
    assert entry != null;
    List<Post> postList = entry.getValue();

    if(method.contains("update")) {
        //update it
        for (Post temp : postList) {
            assert post != null;
            if (temp.getId().equals(post.getId())) {
                postList.remove(temp);
                break;
            }
        }
        postList.add(post);
    }
    else if(method.contains("delete")){
        //delete it
        for (Post temp : postList) {
            if (temp.getId().equals(postId)) {
                postList.remove(temp);
                break;
            }
        }
    }


    //update the cache!! :D
    cache.put(entry.getKey(),postList);

    return new ArrayList<>(Collections.singletonList(cacheManager.getCache(CACHE_NAME)));
}
}

以下是使用CustomCacheResolver的方法

@Cacheable(key = "{#pageNo,#pageSize}")
public List<Post> retrieveAllPost(int pageNo,int pageSize){ // return list}

@CachePut(key = "#post.id",cacheResolver = "customCacheResolver")
public Boolean updatePost(Post post, UserDetails userDetails){ //your logic}

@CachePut(key = "#postId",cacheResolver = "customCacheResolver")
public Boolean deletePost(Long postId,UserDetails userDetails){ // your logic}

@CacheEvict(allEntries = true)
public Boolean createPost(String userId, Post post){//your logic}

希望它有助于手动操作您的 Spring 应用程序缓存!

于 2020-05-13T18:21:29.107 回答
0

虽然我没有看到任何简单的方法,但是您可以通过提供缓存装饰器来覆盖 Ehcache 缓存功能。很可能您想使用EhcahceDecoratorAdapter来增强EhCacheCache put 和 evict 方法使用的功能。

于 2014-07-27T20:14:58.653 回答
0

简单粗暴的解决方案是:

@Cacheable(key = "{#pageNo,#pageSize}")
public List<Post> retrieveAllPost(int pageNo,int pageSize){ // return list}

@CacheEvict(allEntries = true)
public Boolean updatePost(Post post, UserDetails userDetails){ //your logic}

@CacheEvict(allEntries = true)
public Boolean deletePost(Long postId,UserDetails userDetails){ // your logic}

@CacheEvict(allEntries = true)
public Boolean createPost(String userId, Post post){//your logic}
于 2021-08-27T01:59:50.333 回答