最近项目中需要使用redis充当mybatis二级缓存。因此出现了一系列问题。
由于部署时使用了redis集群,所以选择使用了JedisCluster类进行存取操作。但是后续发现,JedisCluster存取操作只支持String、byte[]。而项目中获取出来的类型非常灵活,可能是String、List、Map等等,只能使用Object去进行存储操作,显然JedisCluster没有一个合适的API支持我使用。
问题:已知类对象与其String类型的值能否将其变成正常的对象?
由于没有解决上述问题,因此放弃JedisCluster改为使用RedisTemplate。而在使用中需要重写Redis序列化方式。最初使用Jackson2JsonRedisSerializer,其代码如下:
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
//Jackson2JsonRedisSerializer
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 配置连接工厂
template.setConnectionFactory(factory);
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jacksonSeial.setObjectMapper(om);
// 值采用json序列化
template.setValueSerializer(jacksonSeial);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
// 设置hash key 和value序列化模式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(jacksonSeial);
template.afterPropertiesSet();
return template;
}
使用Jackson2JsonRedisSerializer后,发现在postMan直接调用接口时,能够正常使用,而在充当接口由外部调用时,出现以下报错:
org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Unrecognized field "tParentId" (class xxxxxxx), not marked as ignorable (17 known properties: "updateBy", "parentId", "dirType", "parent", "name", "text", "parentIds", "office_code", "createBy", "updateDate", "type", "id", "attrname", "sort", "createDate", "remarks", "delFlag"])
at [Source: [B@3601e498; line: 1, column: 531] (through reference chain: java.util.ArrayList[0]->com.xxxxxx["parent"]->xxxxxxx["tParentId"]); nested exception is com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "tParentId" (class xxxxxxx.IrmTree), not marked as ignorable (17 known properties: "updateBy", "parentId", "dirType", "parent", "name", "text", "parentIds", "office_code", "createBy", "updateDate", "type", "id", "attrname", "sort", "createDate", "remarks", "delFlag"])
at org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer.deserialize(Jackson2JsonRedisSerializer.java:73)
at org.springframework.data.redis.core.AbstractOperations.deserializeValue(AbstractOperations.java:318)
at org.springframework.data.redis.core.AbstractOperations$ValueDeserializingRedisCallback.doInRedis(AbstractOperations.java:58)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:207)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:169)
at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:91)
at org.springframework.data.redis.core.DefaultValueOperations.get(DefaultValueOperations.java:43)
at org.apache.ibatis.cache.decorators.LoggingCache.getObject(LoggingCache.java:57)
at org.apache.ibatis.cache.decorators.TransactionalCache.getObject(TransactionalCache.java:68)
at org.apache.ibatis.cache.TransactionalCacheManager.getObject(TransactionalCacheManager.java:35)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:101)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:83)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.ibatis.plugin.Invocation.proceed(Invocation.java:49)
at com.github.pagehelper.SqlUtil._processPage(SqlUtil.java:401)
at com.github.pagehelper.SqlUtil.processPage(SqlUtil.java:374)
at com.github.pagehelper.PageHelper.intercept(PageHelper.java:254)
at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:61)
at com.sun.proxy.$Proxy130.query(Unknown Source)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:148)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:141)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:434)
at com.sun.proxy.$Proxy87.selectList(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:231)
at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:128)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:68)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:53)
at com.sun.proxy.$Proxy103.findList(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.servicecomb.swagger.engine.SwaggerProducerOperation.doInvoke(SwaggerProducerOperation.java:148)
at org.apache.servicecomb.swagger.engine.SwaggerProducerOperation.syncInvoke(SwaggerProducerOperation.java:139)
at org.apache.servicecomb.swagger.engine.SwaggerProducerOperation.invoke(SwaggerProducerOperation.java:109)
at org.apache.servicecomb.core.handler.impl.ProducerOperationHandler.handle(ProducerOperationHandler.java:40)
at org.apache.servicecomb.core.Invocation.next(Invocation.java:155)
at org.apache.servicecomb.bizkeeper.BizkeeperCommand.lambda$construct$2(BizkeeperCommand.java:79)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:48)
at rx.internal.operators.OnSubscribeMap.call(OnSubscribeMap.java:33)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10211)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35)
at rx.Observable.subscribe(Observable.java:10307)
at rx.Observable.subscribe(Observable.java:10274)
at rx.Observable.subscribe(Observable.java:10155)
at org.apache.servicecomb.bizkeeper.BizkeeperHandler.handle(BizkeeperHandler.java:77)
at org.apache.servicecomb.core.Invocation.next(Invocation.java:155)
at com.huawei.paas.cse.tcc.handler.ProviderTccTransactionHandler.handle(ProviderTccTransactionHandler.java:59)
at org.apache.servicecomb.core.Invocation.next(Invocation.java:155)
at org.apache.servicecomb.qps.ProviderQpsFlowControlHandler.handle(ProviderQpsFlowControlHandler.java:50)
at org.apache.servicecomb.core.Invocation.next(Invocation.java:155)
at com.huawei.paas.cse.handler.stats.PerfStatsHandler.handle(PerfStatsHandler.java:60)
at org.apache.servicecomb.core.Invocation.next(Invocation.java:155)
at org.apache.servicecomb.core.handler.ShutdownHookHandler.handle(ShutdownHookHandler.java:68)
at org.apache.servicecomb.core.Invocation.next(Invocation.java:155)
at org.apache.servicecomb.common.rest.AbstractRestInvocation.doInvoke(AbstractRestInvocation.java:176)
at org.apache.servicecomb.common.rest.AbstractRestInvocation.invoke(AbstractRestInvocation.java:152)
at org.apache.servicecomb.common.rest.AbstractRestInvocation.runOnExecutor(AbstractRestInvocation.java:136)
at org.apache.servicecomb.common.rest.AbstractRestInvocation.lambda$scheduleInvocation$2(AbstractRestInvocation.java:124)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
根据报错网上查询资料发现序列化方式有问题,然后和我们刚才序列化的方式对比一下,发现真的是序列化方式不一样,旧的是通过StringRedisSerializer进行序列化的,springboot是通过Jackson2JsonRedisSerializer进行序列化的。
详细参考https://www.2cto.com/kf/201807/764238.html
后来根据参考的案例替换使用StringRedisSerializer,其代码如下:
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
//StringRedisTemplate
RedisTemplate redisTemplate = new StringRedisTemplate(factory);
StringRedisSerializer stringRedisSerializer =new StringRedisSerializer();
redisTemplate.setValueSerializer(stringRedisSerializer);
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setHashValueSerializer(stringRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
而改用StringRedisSerializer,那么key、value又是只能使用String,限制了我们的自由,使用中各种报java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.lang.String
因此StringRedisSerializer、Jackson2JsonRedisSerializer都不能够使用,最终参考Redis 序列化方式StringRedisSerializer、FastJsonRedisSerializer和KryoRedisSerializer重写序列化器,自定义了FastJsonRedisSerializer从而使其能够正常使用。
上述内容若有错误,请您指出探讨。谢谢!