RedisTemplate hashvalue serializer to use for nested object with multiple types
The Jackson2JsonRedisSerializer
does not include mapping information into the actual hash structure.
The resulting Redis HASH results in something like:
127.0.0.1:6379> hgetall job:1
1) "id"
2) "\"1\""
3) "createTime"
4) "1455778716799"
5) "submitterName"
6) "\"Jon Snow\""
7) "jobDef"
8) "{\"def\":\"nightwatch\"}"
The ObjectMapper
produces a LinkedHashMap
for the JobDefinition
entry which fails to deserialize as the type is unknown.
Using the GenericJackson2JsonRedisSerializer
includes type information so the resulting Redis HASH looks like this:
127.0.0.1:6379> hgetall job:1
1) "id"
2) "\"1\""
...
7) "jobDef"
8) "{\"@class\":\"java.util.LinkedHashMap\",\"def\":\"nightwatch\"}"
This allows to deserialize values correctly.
Another approach would be to NOT use a specific HashValueSerializer
but instead use a DecoratingStringHashMapper
along with the StringRedisTemplate
.
DecoratingStringHashMapper mapper = new DecoratingStringHashMapper<Job>(
new JacksonHashMapper<Job>(Job.class));
template.opsForHash().putAll("job:" + job.id, mapper.toHash(job));
Map jobMap = template.opsForHash().entries("job:" + job.id);
The DecoratingStringHashMapper
will produce a Redis Hash as follows:
127.0.0.1:6379> hgetall job:1
1) "id"
2) "1"
3) "createTime"
4) "1455780810643"
5) "submitterName"
6) "Jon Snow"
7) "jobDef"
8) "{def=nightwatch}"
Unfortunately there is no Jackson2HashMapper
. Please vote for DATAREDIS-423 and help us prioritize.
Derek
Updated on June 18, 2022Comments
-
Derek almost 2 years
I'm trying to use Redis to store some cache data for my entity, which has different types of fields inside, for example,
public class Job { private String id; private Date createTime; //Long private String submitterName; private JobDefinition jobDef; //Another class }
There are more fields and due to the fact that several fields are updated more frequently than others, I decided to save this
job
as a Hashmap in Redis with each field as a key. Here the nested object likejobDef
is not important so I usedJackson2JsonRedisSerializer
ashashValueSerializer
forRedisTemplate
and thejobDef
obj will just be serialized as a long JSON string, which is totally fine in my case.But I don't know how can I effectively deserialize the whole
job
object back from Redis. The type I set to deserializer is likeJackson2JsonRedisSerializer(Map.class)
but it complains when deserializing String keys and values.So is this an invalid usage with
RedisTemplate
or how should I configure my serializer for it?EDIT: Adding more code details,
@Autowired private StringRedisTemplate redisTemplate; //Here I'm using a String template as I need to use the same redisTemplate for some key-value/list operations too Map jobHash= new ObjectMapper().convertValue(job, Map.class); redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer(Map.class)); redisTemplate.opsForHash().putAll("job:"+job.getId(), jobHash); //After this the job hash shows up in Redis as I expected, while the jobDef member is serialized and saved as a JSON string Map jobMap = redisTemplate.opsForHash().entries("job:" + job.getId()); //But this won't work as it'll throw exception complaining cannot deserialize a String value to Map. But when I set Jackson2JsonRedisSerializer(String.class) it throws exception that cannot resolve the byte code
2nd EDIT:
If using
JdkSerializationRedisSerializer
as HashValueSerializer inRedisTemplate
then the deserialization works fine, however the downside for using this one is the value stored in Redis is not the same human readable string value as when usingJackson2JsonRedisSerializer
. -
Derek about 8 yearsThanks a lot for the help! Since I'm running on Spring-data-redis 1.4.2 seems to me
GenericJackson2JsonRedisSerializer
is not available yet. The second approach almost works like magic except the hash converted has createTime missing, is there any problem converting non-string value? I noticed my original line ofMap jobHash= new ObjectMapper().convertValue(job, Map.class);
uses a different ObjectMapper (com.fasterxml.jackson.databind.ObjectMapper#ObjectMapper()
) and it can generate date value in hash. -
Christoph Strobl about 8 yearsThe
JacksonHashMapper
still uses theorg.codehaus.jackson.map.ObjectMapper
. You can try passing in your own configuration of that one - I did not have trouble serializingjava.util.Date
values using it with the defaults. The code forGenericJackson2JsonRedisSerializer
is on github, so you might just want to copy it into your project in case you cannot upgrade to a more recent version.