#spring #spring-cache
#spring #spring-кэш
Вопрос:
Я хочу кэшировать некоторые данные БД. например, кэшировать Customer
и использовать customer.id
в качестве ключа.
Как я могу установить ключ, если я хочу загрузить всех клиентов ( allCustomer()
в коде)?
@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);
}
Ответ №1:
Я бы рекомендовал использовать @CachePut
вместо @Cacheable
. В случае, если новая запись добавляется в базу данных извне этого экземпляра приложения, кэш не будет содержать это новое значение.
Вы можете использовать #result.id
, чтобы указать Spring, какое значение использовать в качестве ключа, и я включил условие, чтобы вы не получали странных ошибок в случае нулевого значения.
@CachePut(value = "customer", key = "#result.id", condition = "#result != null")
Ответ №2:
Это невозможно сделать для коллекций с аннотациями Spring — у @Cacheable
вас будет только один элемент в кэше с вычисляемым ключом и значением со всем списком внутри.
Если производительность в вашем приложении не так важна, используйте getCustomer(...)
in a loop .
В противном случае вам потребуется обновить кэш вручную. К сожалению, интерфейс кэша не предоставляет метода для извлечения всех ключей / значений / пар ключ-значение из кэша, поэтому требуется небольшое приведение.
Пример для кэша в памяти по умолчанию ( 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;
}
Или для spring.cache.type=jcache
с резервным 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;
}
То же самое можно сделать аналогичным образом для любого другого типа кэша (redis, hazelcast, caffeine и т. Д.).
Соответствующий метод удаления может быть написан намного проще:
@CacheEvict(value = "customer", allEntries = true)
public void deleteAllCustomers(){
db.removeAll(); //pseudocode
}