共享缓存上的@Cacheable 键?

@Cacheable Key on Shared Cache?

我有一个 Spring 应用程序使用 MyBatis 进行持久化。我正在使用 ehcache,因为速度对于这个应用程序很重要。我已经安装并配置了 MyBatis 和 Ehcache。我正在使用一个名为 "mybatis" 的缓存,因为否则为每个实体创建一个单独的缓存将是荒谬的。

这是我的ehcache.xml。

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="ehcache.xsd"
         updateCheck="false"
         monitoring="autodetect"
         dynamicConfig="true">

    <diskStore path="java.io.tmpdir" />

    <cache name="mybatis"
           maxBytesLocalHeap="100M"
           maxBytesLocalDisk="1G"
           eternal="false"
           timeToLiveSeconds="0"
           timeToIdleSeconds="0"
           statistics="true"
           overflowToDisk="true"
           memoryStoreEvictionPolicy="LFU">
    </cache>

    <cache name="jersey"
           maxBytesLocalHeap="100M"
           maxBytesLocalDisk="1G"
           eternal="false"
           timeToLiveSeconds="600"
           timeToIdleSeconds="300"
           statistics="true"
           overflowToDisk="true"
           memoryStoreEvictionPolicy="LFU">
    </cache>

</ehcache>

这是我的 mybatis 映射器接口的示例。

import java.util.List;

public interface InstitutionMapper {

    @Cacheable(value = "mybatis")
    List<Institution> getAll();

    @Cacheable(value = "mybatis", key = "id")
    Institution getById(long id);

    @CacheEvict(value = "mybatis")
    void save(Institution institution);

    @CacheEvict(value = "mybatis", key = "id")
    void delete(long id);
}

因为我有一个共享缓存,所以我需要一种方法让我的密钥对于域对象是唯一的。作为保存或删除的示例,我需要清除缓存,以便新值显示在 UI 上。但是我不想清除整个缓存。我不知道如何处理这个问题,以便在调用 delete 并清除缓存时,只有具有该 ID 的机构的 mybatis 缓存中的条目被清除。

密钥需要是域名 + 参数之类的东西。作为一个例子机构+ id。希望这是有道理的。

我看到了这个 post 但它似乎是按 class 名称 + 方法 + 参数进行的。

为整个域模型设置一个区域有点奇怪(至少可以这么说)。我可以想象您可以在同一缓存中收集具有相似语义的对象类型,但不能收集 all 对象类型。如果你有适当的分界,你在这里问的大多数问题都会自行解决。

但为了解释方便,这里有一些想法。

您的 getAll() 需要一把钥匙。如果您不提供一个,那么基本上任何其他没有参数的 @Cacheable 方法都会与缓存中的相同键冲突。

@Cacheable(value = 'mybatis', key = "'institutions'")
List<Institution> getAll();

您的 @CacheEvict 不会清除缓存列表(通过 getAll() 方法),因此您可能会遇到这样的情况:您驱逐了一个机构,但它仍然显示在缓存 getAll()打电话。如果你想在多个级别缓存相同的东西,你最好在 update/delete 某些东西时删除整个区域。如果每个实体类型都有一个区域,这当然不是什么问题。

您的 save 方法没有 ID。它究竟应该驱逐什么?它怎么知道它必须通过 id 找到一个现有的机构?

@CacheEvict(value = "mybatis", key = "#p0.id")
void save(Institution institution);

(虽然不能解决 getAll() 不一致的问题)

您的 getById 不需要密钥,因为那里唯一的方法参数 id。回到你原来的 "problem",如果你想给你的键加上一些前缀,你需要全面地做(这样驱逐就可以针对同一个键)。我不会在 SpEL 中这样做,因为忘记一个案例的机会太高了。

您可以实现自定义 KeyResolver 并根据方法的 return 类型附加一个唯一的前缀。

也就是说,您的示例代码几乎 全部 都是错误的,所以我建议您查看 documentation on this topic