简而言之,当在方法上调用 @CacheEvict 时,如果未找到条目的键,Gemfire 将抛出 EntryNotFoundException。
现在详细说明,
我有课
class Person {
String mobile;
int dept;
String name;
}
我有两个缓存区域定义为 personRegion 和 personByDeptRegion,服务如下
@Service
class PersonServiceImpl {
@Cacheable(value = "personRegion")
public Person findByMobile(String mobile) {
return personRepository.findByMobile(mobile);
}
@Cacheable(value = "personByDeptRegion")
public List<Person> findByDept(int deptCode) {
return personRepository.findByDept(deptCode);
}
@Caching(
evict = { @CacheEvict(value = "personByDeptRegion", key="#p0.dept"},
put = { @CachePut(value = "personRegion",key = "#p0.mobile")}
)
public Person updatePerson(Person p1) {
return personRepository.save(p1);
}
}
当调用 updatePerson 时,如果 personByDeptRegion 中没有条目,则会抛出键 1 (或任何部门代码)的 EntryNotFoundException 异常。这个方法很有可能会在 @Cacheable 方法被调用之前被调用,并且希望避免这个异常。 当给定区域的 key 不存在时,我们是否可以调整 Gemfire 的行为以优雅地返回? 另外,我也很想知道是否有使用 Gemfire 作为缓存更好地实现上述场景。
Spring 数据 Gemfire:1.7.4
Gemfire 版本:v8.2.1
注意:上面的代码仅供引用,我在实际项目中有多个服务存在相同的问题。
最佳答案
首先,我赞扬您在应用程序上使用 Spring 的缓存注释 @Service
成分。开发人员经常在他们的存储库中启用缓存,我认为这是不好的形式,特别是如果在存储库交互之前或之后涉及复杂的业务规则(甚至额外的 IO;例如从服务组件调用 Web 服务) ,特别是在缓存行为不应受到影响(或确定)的情况下。
我还认为您通过遵循 personRegion
来缓存 UC(在数据存储更新时更新一个缓存 ( personByDeptRegion
),同时使另一个缓存 ( CachePut
) 无效)与 CacheEvict
对我来说似乎很合理。不过,我想指出 @Caching
的看似预期用途注解是组合多个相同类型的缓存注解(例如多个 @CacheEvict
或多个 @CachePut
),如核心 Spring 框架 Reference Guide 中所述。 。尽管如此,没有什么可以阻止您的预期用途。
我创建了一个类似的测试类here ,仿照上面的示例来验证问题。确实是jonDoeUpdateSuccessful测试用例失败(使用 GemFire EntryNotFoundException
,如下所示),因为 Department
中没有人与 janeDoeUpdateSuccessful 不同,“R&D”在更新之前之前已缓存在“DepartmentPeople
”GemFire 区域中。测试用例,这会导致在更新之前填充缓存(即使该条目没有值,这也没有影响)。
com.gemstone.gemfire.cache.EntryNotFoundException: RESEARCH_DEVELOPMENT
at com.gemstone.gemfire.internal.cache.AbstractRegionMap.destroy(AbstractRegionMap.java:1435)
NOTE: My test uses GemFire as both a "cache provider" and a System of Record (SOR).
问题确实在于 SDG 使用 Region.destroy(key)在 GemfireCache.evict(key)实现而不是,也许更合适,Region.remove(key) .
GemfireCache.evict(key)
已实现 Region.destroy(key)
自成立以来。然而,Region.remove(key)
直到 GemFire v5.0 才引入。尽管如此,我还是看不出 Region.destroy(key)
之间有明显的区别。和Region.remove(key)
除了 EntryNotFoundException
由 Region.destroy(key)
抛出。本质上,它们都会破坏本地条目(键和值),并将操作分发到集群中的其他缓存(假设使用了非 LOCAL
Scope)。
所以,我已经提交了SGF-539将 SDG 更改为调用 Region.remove(key)
在GemfireCache.evict(key)
而不是Region.destroy(key)
.
至于解决方法,基本上您只能做两件事:
- 重构您的代码和
@CacheEvict
的使用注释,和/或... - 利用
condition
上@CacheEvict
.
不幸的是condition
无法使用类类型来指定,类似于 Spring Condition (除了 SpEL),但此接口(interface)用于其他目的,并且 @CacheEvict
, condition
属性不接受类类型。
目前,我还没有一个很好的例子来说明它是如何工作的,所以我将继续前进 SGF-539 .
您可以关注此票证以了解更多详细信息和进度。
抱歉给您带来不便。
-约翰
关于java - @CacheEvict 的 Gemfire EntryNotFoundException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39830488/