英文:
Is there a way to apply Spring caching functionality for saveAll Or updateAll with Cacheable function, respectively?
问题
@CachePut(cacheNames = "projectTeams", key = "#projectTeam.id")
public List<ProjectTeam> updateAll(List<ProjectTeam> projectTeam) {
return projectTeamRepository.save(projectTeam);
}
@Cacheable(cacheNames = "projectTeams", key = "#id")
public List<ProjectTeam> findByIds(ListOfIds<Long> ids) {
log.info("get project team from db");
log.info("projectTeam id " + id);
return projectTeamRepository.findByIds(ids);
}
我不知道应该在key
中放什么内容。Spring Cache是否支持SaveAll
?
英文:
@CachePut(cacheNames = "projectTeams", key = "#projectTeam.id")
public List<ProjectTeam> updateAll(List<ProjectTeam> projectTeam) {
return projectTeamRepository.save(projectTeam);
}
@Cacheable(cacheNames = "projectTeams", key = "#id")
public List<ProjectTeam> findByIds(ListOfIds<Long> ids) {
log.info("get project team from db");
log.info("projectTeam id " + id);
return projectTeamRepository.findByIds(ids);
}
I don't have any idea what i should put in key.
Does spring cache supports SaveAll.
答案1
得分: 1
Spring使用默认的键生成策略从启用缓存的(@Cacheable
或@CachePut
注解的)服务或存储库方法的参数中派生缓存条目的键。
当然,您可以通过以下方式自定义任何启用缓存的bean方法使用的键生成:1) 实现KeyGenerator
接口,2) 在缓存注解中声明KeyGenerator
bean,如下所示:
class ListKeyGenerator implements KeyGenerator {
public Object generate(Object target, Method method, Object... params) {
List<?> list = params[0];
// 使用列表生成缓存条目的(唯一)键
Object generatedKey = ...;
return generatedKey;
}
}
注意:
target
是包含您的updateAll(..)
和findByIds(..)
方法的Spring托管bean。Method
引用是被调用的启用缓存的方法,例如updateAll(..)
。而Object
参数数组是缓存启用方法的参数(例如List
)。
提示: 为了简单和清晰(推荐),您可能希望有两个独立的基于
List
的KeyGenerators
,其中一个是ID的List
(在finder/query方法中使用),另一个是ProjectTeam
实例的List
(在更新中使用)。
在配置中,您可以声明:
@Configuration
class ApplicationCacheConfiguration {
@Bean
KeyGenerator listKeyGenerator() {
return new ListKeyGenerator();
}
}
您可以使用以下方式应用自定义的KeyGenerator
:
@Service
class ProjectTeamService {
@CachePut(cacheNames = "projectTeams", keyGenerator = "listKeyGenerator")
public List<ProjectTeam> updateAll(List<ProjectTeam> projectTeams) {
// 执行必要的预处理步骤
return this.projectTeamRepository.saveAll(projectTeams);
}
}
然而,如果您只依赖于Spring的默认键生成策略,则不一定需要自定义KeyGenerator
。
关于您选择的方法,这将不起作用,因为它试图使用Spring SpEL表达式来访问ProjectTeam
对象的List
中的单个元素/项。对于ID的List
也是如此。
您从List
中使用哪个元素?您使用所有元素(这将很难使用SpEL进行汇总)?如果传递给启用缓存方法的List
参数为空,或更糟糕的是null
,会发生什么?
提示: 您应该查看Spring的SpEL功能以获取更多详细信息。
这些类型的问题更适合在自定义KeyGenerator
实现中处理。
您还应该知道,Spring托管bean的启用缓存方法的缓存条目将使用“默认键生成”:
cache-enabled method | cache key | cache value
----------------------------------------------------------------------
findByIds(:List<Long>) | List<Long> | List<ProjectTeam>
updateAll(:List<ProjectTeam>) | List<ProjectTeam> | List<ProjectTeam>
如果您期望将List
中的个别元素/项(例如Long
ID或ProjectTeam
实例)分开并单独缓存在不同的缓存条目中,那么您是错误的,因为这不是Spring的缓存抽象默认工作方式。
我提到这一点是因为您在注解中声明的缓存配置似乎暗示了这一点。
Spring获取启用缓存方法的参数(例如@Cacheable
注解的方法)作为缓存条目的键。方法的返回值用作值。由于启用缓存方法接受一个List
并返回一个List
,所以缓存条目的键是List
参数,而List
返回值是缓存条目的值。
可以想象,对于请求的每个不同的ID列表或ProjectTeam
实例的List
,您的缓存内存空间会迅速填满!这是因为如果每个请求(启用缓存方法的调用)中的List
只有1个元素不同,那么它将构成缓存中的新条目。根据应用程序处理的不同请求(调用)的数量和频率,这可能会导致严重问题(例如OOME)!
如果您需要单独请求的项目具有单独的缓存条目,请参阅早期的这篇SO帖子。
祝你好运!
英文:
Spring uses a default key generation strategy to derive the key for the cache entry from the caching-enabled (@Cacheable
or @CachePut
annotated) service or repository methods based on the arguments to the method.
Of course, you can customize key generation used by any cache-enabled bean methods by 1) implementing the KeyGenerator
interface and 2) declaring the KeyGenerator
bean in your caching annotations, like so:
class ListKeyGenerator implements KeyGenerator {
public Object generate(Object target, Method method, Object... params) {
List<?> list = params[0];
// use the list to generate a (unique) key for the cache entry
Object generatedKey = ...;
return generatedKey;
}
}
> NOTE: The target
is the Spring managed bean containing your updateAll(..)
and findByIds(..)
methods. The Method
ref is the cache-enabled method invoked, such as updateAll(..)
. And, the array of Object params are the arguments (e.g. the List
) to the cache-enabled methods.
> TIP: For simplicity and clarity (recommended), you may want to have 2 independent List
-based KeyGenerators
since 1 is a List
of IDs (Long
-typed; in the finder/query method) and the other is a List
of ProjectTeam
instances (in the update).
In configuration, you would declare:
@Configuration
class ApplicationCacheConfiguration {
@Bean
KeyGenerator listKeyGenerator() {
return new ListKeyGenerator();
}
}
You would apply the custom KeyGenerator
using:
@Service
class ProjectTeamService {
@CachePut(cacheNames = "projectTeams" keyGenerator = "listKeyGenerator")
public List<ProjectTeam> updateAll(List<ProjectTeam> projectTeams) {
// perform any preprocessing steps as necessary
return this.projectTeamRepository.saveAll(projectTeams);
}
}
> NOTE: See corresponding Javadoc for @Cacheable
and Javadoc for @CachePut
.
However, you do not necessarily need a custom KeyGenerator
if you simply rely on Spring's default key generation strategy as I mentioned to begin with.
Regarding the approach you chose, this will not work since it is trying to use a Spring SpEL expression to access a single element/item (i.e. a single ProjectTeam
instance) in a List
of ProjectTeam
objects. Same applies for the List
of IDs.
Which element from the List
do you use? Do you use all elements (which would be difficult to aggregate using SpEL)? What happens if the List
argument to the cache-enabled method is empty, or worse, null
?
> TIP: You should review Spring's SpEL capabilities for more details.
These sort of concerns are more appropriately handled in your custom KeyGenerator
implementation.
You should also be aware that the cached entries for the cache-enabled methods of your Spring managed bean will be (using "default key generation"):
cache-enabled method | cache key | cache value
----------------------------------------------------------------------
findByIds(:List<Long>) | List<Long> | List<ProjectTeam>
updateAll(:List<ProjectTeam>) | List<ProjectTeam> | List<ProjectTeam>
If you are expecting the individual elements/items (e.g. either Long
IDs or ProjectTeam
instances) of the Lists
to be broken out and cached individually in separate cache entries, then you are mistaken because this is NOT how Spring's Cache Abstraction works by default.
The reason I mention this is because it is sort of implied by your (attempted) caching configuration as declared in the annotations.
Spring takes the argument(s) to the cache-enabled method (e.g. @Cacheable
annotated method) and uses that as a key for the cache entry. The return value of the method is used as the value. Since the cache-enabled methods take a List
and return a List
then the cache entry key is the List
argument and the List
return value is the cache entry value.
As you might imagine, with each different List
of IDs or List
of ProjectTeam
instances requested, your cache memory space is going to fill up fast! That is because if the List
in each request (cache-enable method) just differs by 1 element, then that is going to constitute a new cache entry in the cache. Depending on how many (different) requests (invocations) your application processes, and the frequency, this could lead to a serious problem (e.g. OOME)!
If you need individual cache entries for individually requested items, then you should have a look at this SO post from earlier.
Good luck!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论