Home > Blockchain >  Spring @Cacheable for method no parameter
Spring @Cacheable for method no parameter

Time:12-09

I want to cache some db data. for example Cache Customer and use customer.id as the key.

How could I set the key if I want to load all customers (allCustomer() in the code) ?

@Cacheable(value = "customer", key = "#customerID")
public Customer getCustomer(Long customerID) {
    return getCustomerData(customerID);
}

// How to setup this key?
@Cacheable(value = "customer", key = "?")
public List<Customer> allCustomer(){
    return db.values().stream().collect(Collectors.toList());
}
    
 @CachePut(value = "customer", key = "#customer.id")
public void updateCustomer(Customer customer){
    db.put(customer.getId(), customer);
}

@CacheEvict(value = "customer", key = "#customerID")
public void deleteCustomer(Long customerID){
    db.remove(customerID);
}

CodePudding user response:

I would recommend using @CachePut instead of @Cacheable. In the case that a new entry is added to the DB from outside of this application instance, the cache would not contain that new value.

You can use #result.id to tell Spring which value to use as a key and I've included a conditional so that you don't get strange errors in case of a null value.

@CachePut(value = "customer", key = "#result.id", condition = "#result != null")

CodePudding user response:

It's impossible to do it for collections with the Spring's annotations - with @Cacheable you'd have just one element in a cache with a computed key and a value with the whole list inside.

If performance is not that important in your app, use getCustomer(...) in a loop.

Otherwise, you'll need to update your cache manually. Unfortunately, Cache interface doesn't provide a method to retrieve all keys/values/key-value pairs from a cache, so a bit of casting is required.

The example for the default in-memory cache (spring.cache.type=simple):

@Autowired
private org.springframework.CacheManager cacheManager;

public List<Customer> allCustomers() {
    ConcurrentMap<Long, Customer> customerCache = (ConcurrentMap<Long, Customer>) 
        cacheManager.getCache("customer").getNativeCache();

    if (!customerCache.isEmpty()) {
        return new ArrayList<>(customerCache.values());
    }

    List<Customer> customers = db.values().stream().collect(Collectors.toList());

    customers.forEach(customer -> customerCache.put(customer.getId(), customer));

    return customers;
}

Or for spring.cache.type=jcache with backed EhCache 3:

@Autowired
private org.springframework.CacheManager cacheManager;

public List<Customer> allCustomers() {
    javax.cache.Cache<Long, Customer> customerCache = (javax.cache.Cache<Long, Customer>) 
        cacheManager.getCache("customer").getNativeCache();
    Iterator<Cache.Entry<Long, Customer>> iterator = customerCache.iterator();

    List<Customer> cachedCustomers = new ArrayList<>();
    while (iterator.hasNext()) {
        Cache.Entry<Long, Customer> entry = iterator.next();
        cachedCustomers.add(entry.getValue());
    }
    if (!cachedCustomers.isEmpty()) {
        return cachedCustomers;
    }

    List<Customer> customers = db.values().stream().collect(Collectors.toList());

    customers.forEach(customer -> customerCache.put(customer.getId(), customer));

    return customers;
}

The same can be done similarly for any other cache type (redis, hazelcast, caffeine etc.).

The corresponding eviction method can be written much easier:

@CacheEvict(value = "customer", allEntries = true)
public void deleteAllCustomers(){
    db.removeAll(); //pseudocode
}
  • Related