Redis充當mybatis二級緩存相關問題

最近項目中需要使用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從而使其能夠正常使用。

上述內容若有錯誤,請您指出探討。謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章