近期

最近很懈怠,感觉自己做的事情没什么意义,未来很迷茫,现在很焦虑,常常觉得不能虚度光阴
但总是浑浑噩噩着过着,有点麻木了,不太喜欢现在这种生活,还是以前过着苦日子,吃着面条的生活更有精神
钱是一点存不了,自身也没什么值得骄傲的进步,时间一点点过,岁月从不饶人,我觉得是时候要做出改变了
我心里有内耗,这我是知道的,但总有一天我会丢掉它,我总有一天会活在当下,脱离掉内耗的自己,加油万一!

回归正题,这两天被一个bug整自闭了,项目中代码从oracle数据库中获取字段和相应的值,
拿到数据后序列化保存到reids中,但总是报错,报错样式见下方:

org.springframework.data.redis.serializer.SerializationException:Could not write JSON:
No serializer found for class java.io.ByteArrayInputStream and no properties discovered to create BeanSerializer 

Caused by:com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
No serializer found for class java.io.ByteArrayInputStream and no properties discovered to create BeanSerializer
(to avoid Exception,disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
(through reference chain:com.ecominfo.modules.oeams.core.dto.RedisStructDataDTO["oeaDateObjectList"]->java.util.ArrayList[0]
->com.ecominfo.modules.oeams.core.dto.RedisStructDataObject["dataMap"]->java.util.HashMap["end_time"]->oracle.sql.TIMESTAMP["stream"])

看样子是因为redis序列化oracle数据库中的TIMESTAMP字段失败,redis序列化使用的是jackson的类型转换器
但对TIMESTAMP类型字段并没有合适的解析器,然后就报错了,大概是这意思吧。所以我们要为redis使用的jackson转换器配置一个适用于TIMESTAMP类型的解析器

网上找了很久才找到解决办法:

  1. 创建OracleTimestampSerializer自定义解析器

    @Slf4j
    public class OracleTimestampSerializer extends JsonSerializer<TIMESTAMP> {
    
        @Override
        public void serialize(TIMESTAMP value, JsonGenerator gen,
                              SerializerProvider serializers) throws IOException {
    
            Timestamp tt = null;
            try {
                tt = value.timestampValue();
                DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS");
                gen.writeString(dateFormat.format(tt));
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
        @Override
        public void serializeWithType(TIMESTAMP value, JsonGenerator g, SerializerProvider provider,
                                      TypeSerializer typeSer) throws IOException
        {
            // NOTE: need not really be string; just indicates "scalar of some kind"
            WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, typeSer.typeId(value, JsonToken.VALUE_STRING));
            serialize(value, g, provider);
            typeSer.writeTypeSuffix(g, typeIdDef);
        }
    }
  2. 在redisConfig中添加我们配置的自定义解析器

    /**
    * 开启缓存支持
    * @author zyf
     * @Return:
    */
    @Slf4j
    @EnableCaching
    @Configuration
    public class RedisConfig extends CachingConfigurerSupport {
    
    	@Resource
    	private LettuceConnectionFactory lettuceConnectionFactory;
    
    	/**
    	 * RedisTemplate配置
    	 * @param lettuceConnectionFactory
    	 * @return
    	 */
    	@Bean("redisTemplate")
    	public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
    		log.info(" --- redis config init --- ");
            Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = jacksonSerializer();
    		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
    		redisTemplate.setConnectionFactory(lettuceConnectionFactory);
    		RedisSerializer<String> stringSerializer = new StringRedisSerializer();
    
    		// key序列化
    		redisTemplate.setKeySerializer(stringSerializer);
    		// value序列化
    		redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
    		// Hash key序列化
    		redisTemplate.setHashKeySerializer(stringSerializer);
    		// Hash value序列化
    		redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
    		redisTemplate.afterPropertiesSet();
    		return redisTemplate;
    	}
    
    
    
    	/**
    	 * 缓存配置管理器
    	 *
    	 * @param factory
    	 * @return
    	 */
    	@Bean("cacheManager")
    	public CacheManager cacheManager(LettuceConnectionFactory factory) {
            Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = jacksonSerializer();
            // 配置序列化(解决乱码的问题),并且配置缓存默认有效期 6小时
            RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(6));
            RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(
    					RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
    															.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
    															//.disableCachingNullValues();
    
    		// 以锁写入的方式创建RedisCacheWriter对象
    		//update-begin-author:taoyan date:20210316 for:注解CacheEvict根据key删除redis支持通配符*
    		RedisCacheWriter writer = new JeecgRedisCacheWriter(factory, Duration.ofMillis(50L));
    		//RedisCacheWriter.lockingRedisCacheWriter(factory);
    		// 创建默认缓存配置对象
    		/* 默认配置,设置缓存有效期 1小时*/
    		//RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1));
    		/* 自定义配置test:demo 的超时时间为 5分钟*/
    		RedisCacheManager cacheManager = RedisCacheManager.builder(writer).cacheDefaults(redisCacheConfiguration)
                .withInitialCacheConfigurations(singletonMap(CacheConstant.SYS_DICT_TABLE_CACHE,
                    					RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(10)).disableCachingNullValues()
                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))))
    				.withInitialCacheConfigurations(singletonMap(CacheConstant.TEST_DEMO_CACHE, 
    					RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5)).disableCachingNullValues()))
    				.withInitialCacheConfigurations(singletonMap(CacheConstant.PLUGIN_MALL_RANKING, 
    					RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(24)).disableCachingNullValues()))
    				.withInitialCacheConfigurations(singletonMap(CacheConstant.PLUGIN_MALL_PAGE_LIST, 
    					RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(24)).disableCachingNullValues()))
    				.transactionAware().build();
    		//update-end-author:taoyan date:20210316 for:注解CacheEvict根据key删除redis支持通配符*
    		return cacheManager;
    	}
    
    	/**
    	 * redis 监听配置
    	 *
    	 * @param redisConnectionFactory redis 配置
    	 * @return
    	 */
    	@Bean
    	public RedisMessageListenerContainer redisContainer(RedisConnectionFactory redisConnectionFactory, 
    RedisReceiver redisReceiver, MessageListenerAdapter commonListenerAdapter) {
    		RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    		container.setConnectionFactory(redisConnectionFactory);
    		container.addMessageListener(commonListenerAdapter, new ChannelTopic(GlobalConstants.REDIS_TOPIC_NAME));
    		return container;
    	}
    
    
    	@Bean
    	MessageListenerAdapter commonListenerAdapter(RedisReceiver redisReceiver) {
    		MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(redisReceiver, "onMessage");
    		messageListenerAdapter.setSerializer(jacksonSerializer());
    		return messageListenerAdapter;
    	}
    
    	private Jackson2JsonRedisSerializer jacksonSerializer() {
    		Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
    		ObjectMapper objectMapper = new ObjectMapper();
    		SimpleModule module = new SimpleModule();
    		// 注册相关类型的自定义Serializer
    		module.addSerializer(TIMESTAMP.class, new OracleTimestampSerializer());
    		objectMapper.registerModule(module);
    		objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    		objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    		jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
    		return jackson2JsonRedisSerializer;
    	}
    }
  3. 相关的文章

    1. jackson序列化问题Type id handling not implemented for type java.util.Date
    2. Jackson 序列化失败问题-oracle数据返回类型找不到对应的Serializer
    3. Redis序列化LocalDateTime报错