如何在对象上使用@CachePut
,并通过多个属性更新缓存?
实施例:
每次调用'findOneByFirstnameAndLastname()`方法时,缓存的人都会被添加到PERSONS
缓存中。
如果持久保存Person
对象,我还希望缓存更新此人。但是,如何判断@CachePut
使用firstname+lastname
作为PERSONS
缓存的关键字?现在,save()
方法不更新缓存...
public interface PersonRepository extends CrudRepository<Person, Long> {
//assume there is only one valid person
@Cachable("PERSONS")
Person findOneByFirstnameAndLastname(String firstname, String lastname);
//TODO how to update cache by entity.firstname + entity.lastname
@CachePut("PERSONS")
@Override
Person save(Person entity);
}
答案 0 :(得分:0)
我认为你选择缓存密钥有点天真......名字+姓氏?
显然,一个人的名字改变,特别是姓氏,例如一个人结婚时,这种情况并不少见。她(通常是他的)姓氏通常会改变。其次,我认为这个(&#34; 假设只有一个有效的人&#34;)在大多数情况下是一个相当弱的假设。
显然,您正在尝试在应用程序中缓存一个相当典型且常见的数据访问操作,例如&#34; 按姓名&#34;查找一个人,这会调用SD 存储库中定义的搜索操作(查询),当某人呼叫支持时,可能由某些CSR触发。但是,这是在错误的抽象级别上不适当地使用缓存。
如果缓存密钥发生变化,您认为会发生什么?
由于条目基本上无法访问,因此您实际上有内存泄漏。 即使条目最终到期或被驱逐,如果未及时执行到期或驱逐(受内存限制和加载,当然还有缓存驱逐/过期策略),很容易耗尽内存
对于大多数缓存来说,基于&#34; hash&#34;来缓存条目是很常见的。关键。这在一些缓存实现(技术上,&#34; 数据网格&#34;)中非常有用,其中数据在群集中的许多数据节点之间进行分区和平衡。虽然,您可能知道,缓存与java.util.HashMap
没有什么不同。实际上,他们中的许多人确实实现了java.util.Map
或java.util.concurrent.ConcurrentMap
接口。
这通常是为什么最好在缓存中使用代理键而不是自然键,例如人名+ DOB或SSN等。代理键不可能永远改变,只是用于内部的,参考的目的,特别是在基础数据可能和可能改变的可能性中,我在你的UC中很可能会说。另外,考虑到使用&#34;哈希&#34;在大多数缓存中,这就是标量值(例如Long
)用作键的原因。
使用自然键只应非常小心,并且密钥的equals
和hashCode
方法已正确实施。
尽管如此,如果您已经开始使用&#34; 第一个+姓氏&#34;在Person
中,您可以使用SpEL expression定义缓存密钥,如此...
@Cacheable(cacheNames = "PERSONS", key = "#firstname + #lastname")
Person findByFirstnameAndLastName(String firstname, String lastname);
@CachePut(cacheNames = "PERSONS", key = "#person.firstName + #person.lastName")
Person save(Person person);
注意:或者,
@CachePut
&#34;key
&#34;也可以定义为&#34;#result.firstname + #result.lastname
&#34;。
当然,如果您的Person
课程采用getName()
方法(或name
&#34;属性&#34;),您可以简化此操作,就像这样... < / p>
class Person {
...
String getName() {
return String.format("%1$s %2$s", getFirstName(), getLastName());
}
}
则...
@Cacheable(cacheNames = "PERSONS", key = "#firstname + ' ' + #lastname")
Person findByFirstnameAndLastName(String firstname, String lastname);
@CachePut(cacheNames = "PERSONS", key = "#person.name")
Person save(Person person);
有关详细信息,请参阅SpEL's mathematical operators,特别是涉及字符串连接。
另请参阅Section on Cache Keys了解更多详情。
就个人而言,我建议采用略有不同的缓存策略,特别是如果您不是很确定数据会在相对较快时使用,或者经常&#34;。许多缓存根据使用频率保存数据,或基于最近最少使用(LRU)逐出。
通常,只是&#34;逐出&#34;数据更改时的条目,只在需要时刷新缓存,下次访问该条目时,将其从基础数据存储(SOR)中拉出并存储在缓存中,如此...
interface PersonRepository extends CrudRepository<Person, Long> {
@Cacheable("PERSONS")
Person findById(Long id);
@CacheEvict(cacheNames = "PERSONS", key = "#result.id")
Person save(Person person);
}
此缓存策略建议您不应缓存搜索结果&#34;,其中结果的数量在给定时间范围内可能相当广泛,尤其是在高度并发的上下文中,而是缓存实际获得的记录&#34;使用&#34; (即加载或访问)。
此外,如果您以后更改查询方法以返回&#34;投影&#34;您的应用程序可能会崩溃。而不是整个Person
对象,它可能包含许多其他&#34;引用&#34;,但在某种程度上也取决于您的延迟加载策略。尽管如此,通过使用仅满足信息呈现所必需的投影(或DTO)来减少传输的数据量并不罕见。
坦率地说,最好优化查询并应用适当的索引,而不是尝试将搜索结果存储在内存中。
希望这有帮助!
答案 1 :(得分:0)
最后,我在查看过程中成功使用了对参数(#p1, #2
)的引用,并在持久化过程中引用了#result.*
:
@Cacheable(cacheNames = "PERSONS", key = "#p1 + #p2")
Person findByFirstnameAndLastName(String firstname, String lastname);
@CachePut(cacheNames = "PERSONS", key = "#result.firstName + #result.lastName")
Person save(Person person);
但我不知道为什么我不能使用#firstname + #lastname
或#person.firstname + #person.lastname
,但是Spring经常抱怨null
参数。也许Spring在这个阶段无法解析参数名称,无论如何。