For a Java POJO, I want to cache it to Redis using Spring's @Cacheable, @CachePut, and @CacheEvict, however I'd prefer to use Redis' Hash capabilities instead of just serializing the POJO into a String. Essentially, I would like to be able to use something like the ObjectHashMapper against the POJO so that POJO properties are automatically saved as Key/Value pairs in a single Redis entry. This behavior can be seen in the RedisRepository functionality which saves POJOs with the ObjectHashMapper, however, I prefer not to define the cache as a Repository, rather I want to use the Cache annotations.
I have successfully created some custom Spring/Redis configuration that helped me get String serialization working propertly. The Spring documentation for customizing the CacheManager and CacheConfiguration are very "light" and I've not found any relevant examples. I'm not sure whether I need a custom serializer, converter, or some other aspect of the CacheConfiguration. It seems like the serializers are more concerned with the individual keys and values, but I don't see where to configure to catch the entire Object and turn it into a Hash first.
Here is my Redis configuration. It is setup for two caches, "v", and "products", plus defaults to a StringRedisSerializer for other caches.
@Slf4j
@RequiredArgsConstructor
@Configuration
@EnableRedisRepositories(enableKeyspaceEvents=RedisKeyValueAdapter.EnableKeyspaceEvents.ON_STARTUP)
public class RedisConfig {
private final RedisConnectionFactory connectionFactory;
private static RedisCacheConfiguration createCacheConfiguration(long timeoutInSeconds, RedisSerializationContext.SerializationPair<?> serializationPair) {
logger.info("Creating CacheConfiguration with timeout of {} seconds", timeoutInSeconds);
return RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(serializationPair)
.entryTtl(Duration.ofSeconds(timeoutInSeconds));
}
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
logger.info("Creating cache manager");
Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
cacheConfigurations.put("v",createCacheConfiguration(1200, RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())));
cacheConfigurations.put("products",createCacheConfiguration(-1,RedisSerializationContext.SerializationPair.fromSerializer(new JdkSerializationRedisSerializer())));
return RedisCacheManager
.builder(connectionFactory)
.cacheDefaults(createCacheConfiguration(-1,RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())))
.withInitialCacheConfigurations(cacheConfigurations)
.build();
}
}
Here is an example of how a POJO is serialized per the configuration above:
@Data
@Builder
@AllArgsConstructor
public class ProductSummary implements Serializable {
@Id
private String id;
private String accountId;
private String name;
private String roles;
private String groups;
}
and the way it is serialized:
\xac\xed\x00\x05sr\x004com.foobar.ProductSummaryh\xb3\x9d
\xd4\x0f\xac\xea\xb3\x02\x00\x05L\x00\taccountIdt\x00
\x12Ljava/lang/String;L\x00\x06groupsq\x00~\x00\x01L\x00
\x02idq\x00~\x00\x01L\x00\x04nameq\x00~\x00\x01L\x00
\x05rolesq\x00~\x00\x01xpt\x00\x19acctFv825MKt\x00
\nimwebuserst\x00\x13prod0lwJAWEYt\x00\x11ProductName/2020t\x00\x00
What I'd like is for it to be (in Redis as a Hash):
>HGETALL KEY
1) "_class"
2) "com.foobar.cache.CheckoutState"
3) "accountId"
4) "ACC000001"
5) "name"
6) "lorem ipsum whatever"
7) "roles"
8) "role1,role2,role3"
9) "groups"
10) "groupA,groupB"
with the hash Key being the id.