跳到主要内容

启用缓存

CacheFactory接口

要启用缓存,首先需要实现CacheFactory/KCacheFactory接口,该接口定义如下

CacheFactory.java
package org.babyfish.jimmer.sql.cache;

import org.babyfish.jimmer.meta.ImmutableProp;
import org.babyfish.jimmer.meta.ImmutableType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;

public interface CacheFactory {

@Nullable
default Cache<?, ?> createObjectCache(@NotNull ImmutableType type) {
return null;
}

@Nullable
default Cache<?, ?> createAssociatedIdCache(@NotNull ImmutableProp prop) {
return null;
}

@Nullable
default Cache<?, List<?>> createAssociatedIdListCache(@NotNull ImmutableProp prop) {
return null;
}

@Nullable
default Cache<?, ?> createResolverCache(@NotNull ImmutableProp prop) {
return null;
}
}
信息

对于Kotlin而言,为了在覆盖createAssociatedIdListCache时让IDE生成更好的代码,请实现org.babyfish.jimmer.sql.kt.cache.KCacheFactory接口。

否则,IDE生成的override方法代码时,返回类型是Cache<*, MutableList<*>>,而非期望的Cache<*, List<*>>

Jimmer调用此接口初始化缓存系统,用户实现此接口回答问题

  • createObjectCache:启用对象缓存

    参数指定一个实体类型,如果想让它支持对象缓存,就创建缓存并返回;否则,返回null。

    所谓

    ,指把id映射为实体对象

    信息

    该实体对象是孤单的,没有关联属性。

    基于外键的一对一/多对一关联除外,它们可以持有只有id属性的关联对象,因为关联对象的id其实就是当前表的外键字段。

  • createAssociatedIdCache和createAssociatedIdListCache:启用关联缓存

    createAssociatedIdCachecreateAssociatedIdListCache都用于启用关联缓存。二者唯一的区别是:

    • createAssociatedIdCache: 一对一或多对一关联

    • createAssociatedIdListCache:一对多或多对多关联

    参数指定一个关联属性,如果想让它支持关联缓存,就创建缓存并返回;否则返回null。

    所谓

    ,指把id映射为关联id (或其集合)

  • createResolverCache:启用计算缓存

    参数指定一个复杂计算属性,如果想让它支持计算属性,就创建缓存并返回;否则,返回null。

    所谓

    缓存,指把id映射为计算结果

多级缓存架构

CacheFactory接口的所有方法的返回类型都是org.babyfish.jimmer.sql.cache.Cache<K, V>

用户无需直接实现Cache<K, V>,而需要使用org.babyfish.jimmer.sql.cache.chain.ChainCacheBuilder来构建多级别缓存。

从理论上讲,ChainCacheBuilder支持任意级缓存。然而,大部分项目中,两级缓存已经足够了,例如

return new CacheFactory() {

@Override
@Nullable
public Cache<?, ?> createObjectCache(@NotNull ImmutableType type) {
return new ChainCacheBuilder<>()
.add(
CaffeineValueBinder
.forObject(type)
.maximumSize(1024)
.duration(caffeineDuration)
.build()
)
.add(
RedisValueBinder
.forObject(type)
.redis(connectionFactory)
.objectMapper(objectMapper)
.duration(redisDuration)
.build()
)
.build();
}
};
  • ❶ 表示一级缓存,基于Caffeine的进程内JVM缓存

  • ❷ 表示二级缓存,基于Redis的分远程内部缓存。

如果我们所见,ChainCacheBuilder采用链式编程风格,多次调用add方法就可以构建多级缓存。

ChainCacheBuilder.add方法的定义如下

public class ChainCacheBuilder<K, V> {

public ChainCacheBuilder<K, V> add(LoadingBinder<K, V> binder) {
...省略代码...
return this;
}

public ChainCacheBuilder<K, V> add(LoadingBinder.Parameterized<K, V> binder) {
...省略代码...
return this;
}

public ChainCacheBuilder<K, V> add(SimpleBinder<K, V> binder) {
...省略代码...
return this;
}

...省略其他代码...
}
  • org.babyfish.jimmer.sql.cache.chain.LoadingBinder是一个接口,任何首次访问某个键时会自动加载值的缓存技术都可以通过该接口适配。

    几乎进程内的JVM缓存,都具备自动加载能力。比如上文代码中所用的Caffeine或Guava Cache。

  • ❷ 处代码仅被多视角缓存使用,读者可以先行忽略

  • org.babyfish.jimmer.sql.cache.chain.SimpleBinder是一个接口,任何不具备自动加载值行为的缓存技术都可以通过该接口适配。

    几乎所有远程缓存,都不具备自动加载能力。比如上文代码中所用的Redis

提示

任何缓存技术都可以被适配成抽象接口LoadingBinderSimpleBinder,所以,在Jimmer的多级缓存架构中,任何一级都不会对缓存的技术选型做出任何假设或限制。

如果采用Jimmer的SpringBoot Starter,则可以使用三个缓存技术适配类,如同上文代码中那样

Jimmer内置的适配类实现接口支持多视角缓存
org.babyfish.jimmer.spring.cache.CaffeineBinderorg.babyfish.jimmer.sql.cache.chain.LoadingBinder
org.babyfish.jimmer.spring.cache.RedisValueBinderorg.babyfish.jimmer.sql.cache.chain.SimpleBinder
org.babyfish.jimmer.spring.cache.RedisHashBinderorg.babyfish.jimmer.sql.cache.chain.SimpleBinder.Parameterized
备注

多视角缓存会在后续文章中阐述,这里请读者先忽略之。

配置CacheFactory

现在,我们已经介绍了CacheFactory接口和多级缓存架构,但离启用缓存还差最后一步。

最后一步,为Jimmer把注册CacheFactory

SpringBoot配置

如果使用SpringBoot Starter,让CacheFactory受到Spring的托管即可。

@Bean
public CacheFactory cacheFactory() {
return new CacheFactory() {
...省略代码...
};
}

底层API配置

JSqlClient sqlClient = JSqlClient
.newBuilder()
.setCacheFactory(
new CacheFactory() {
...省略代码...
}
)
...省略其他配置...
.build();
}

Redis缓存辅助API

前面我们提到了,如果采用Jimmer的SpringBoot Starter,则可以用现成的org.babyfish.jimmer.spring.cache.RedisValueBinder,无需自己去适配Redis。

备注

RedisHashBinder多视角缓存相关,本文不讨论。

要构建RedisValueBinder,需要一个RedisOptions<String, byte[]>

Jimmer的SpringBoot Stater提供org.babyfish.jimmer.spring.cache.RedisCaches类,其静态方法RedisCaches.cacheRedisTemplate可快速构建这个RedisOptions<String, byte[]>对象。

辅助方法RedisCaches.cacheRedisTemplate的例子如下:

@Bean
public CacheFactory cacheFactory(
RedisConnectionFactory connectionFactory,
ObjectMapper objectMapper
) {
return new CacheFactory() {

@Override
@Nullable
public Cache<?, ?> createObjectCache(@NotNull ImmutableType type) {
return new ChainCacheBuilder<Object, Object>()
.add(
RedisValueBinder
.forProp(prop)
.redis(connectionFactory)
.objectMapper(objectMapper)
.duration(Duration.ofHours(24))
.build()
)
.add(
CaffeineValueBinder
.forProp()
.maximumSize(1024)
.duration(RedisCaches.ofMinutes(24))
.bind()
)
.build();
}

@Override
@Nullable
public Cache<?, ?> createAssociatedIdCache(@NotNull ImmutableProp prop) {
return createPropCache(
prop,
Duration.ofMinutes(10),
Duration.ofHours(10)
);
}

@Override
@Nullable
public Cache<?, List<?>> createAssociatedIdListCache(@NotNull ImmutableProp prop) {
return createPropCache(
prop,
Duration.ofMinutes(5),
Duration.ofHours(5)
);
}

@Override
@Nullable
public Cache<?, ?> createResolverCache(@NotNull ImmutableProp prop) {
return createPropCache(
prop,
Duration.ofMinutes(5),
Duration.ofHours(5)
);
}

private Cache<?, ?> createPropCache(
ImmutableProp prop,
Duration redisDuration,
Duration caffeineDuration
) {
return new ChainCacheBuilder<K, V>()
.add(
RedisValueBinder
.forProp(prop)
.redis(connectionFactory)
.objectMapper(objectMapper)
.duration(redisDuration)
.build()
)
.add(
CaffeineValueBinder
.forProp()
.maximumSize(128)
.duration(caffeineDuration)
.bind()
)
.build();
}
};
}