Spring-Data-Redis Serializable

前面学习了两种关于Spring Boot 集成Redis,上一节也有聊到Redis序列化乱码(Hash),严重降低数据的可读性,增加时间成本.按照前面一篇文章的代码编译运行,可复现以上所述.那如何解决呢?源码片段如下:

if (defaultSerializer == null) {

	defaultSerializer = new JdkSerializationRedisSerializer(
			classLoader != null ? classLoader : this.getClass().getClassLoader());
}

我们可以看到redis默认序列化是JdkSerializationRedisSerializer通过调用JDK的IO操作ObjectInputStream和ObjectOutputStream类来实现序列化和反序列化的.因此我们将要重写RedisTemplate,ApplicationConfiguration如下代码:

@Bean
    fun redisTemplate(redisConnectionFactory: RedisConnectionFactory): RedisTemplate {
        var redisTemplate = RedisTemplate()
        redisTemplate.connectionFactory = redisConnectionFactory
        redisTemplate.keySerializer = StringRedisSerializer()
        redisTemplate.valueSerializer = Jackson2JsonRedisSerializer(Any::class.java)
        redisTemplate.afterPropertiesSet()
        return redisTemplate
    }

 至此我们在存储对象(Value)时会以Json方式展现,那么很明显的Key是String类型,当然Key还可以以其他的类型存在.在研读了StringRedisSerializer后我默默地依葫芦画瓢地自定义一个Long类型(其他类型都可以)的LongRedisSerialiazer重写Serialize和Deserialize方法如下:

class LongRedisSerializer : RedisSerializer {

    override fun serialize(t: Long?): ByteArray? =
            if (t.isNull()) null else t.toString().toByteArray(Charsets.UTF_8)

    override fun deserialize(bytes: ByteArray?): Long? =
            if (bytes.isNull() || bytes!!.isEmpty()) null else String(bytes, Charsets.UTF_8).toLong()
}

 替换ApplicationConfiguration:

redisTemplate.keySerializer = LongRedisSerializer()

 如此下来Key就可以是您想要的类型了(Long为例).也可以说是以上赘述的所有都不及以Spring-Data-Jpa风格来的简单粗暴,无需考虑对象序列化的问题.我们追repository继承的SimpleKeyValueRepository的源码来看,SimpleKeyValueRepository最终是将Objects放进Map中去以KeyValue方式进行存储,源码片段如下 :

@Override
public void writeTypeTo(RedisData sink, Object alias) {
	sink.getBucket().put(typeKey, conversionService.convert(alias, byte[].class));
}
public Bucket() {
	data = new LinkedHashMap();
}

将对象自动分装成byte数组.在Spring-Data官方文档中 8.3. Object-to-Hash Mapping有述:

The Redis Repository support persists Objects to Hashes. This requires an Object-to-Hash conversion which is done by a RedisConverter. The default implementation uses Converter for mapping property values to and from Redis native byte[].

Example 14. Sample Map Converters很好的说明了上述序列化问题.需要注意的是,我们在使用Curd的findByXXX时对象属性需要被标记为@Indexed例如根据名字查询Person.

repository:

interface PersonRepository : UserJpaRepository {
    fun findByName(name: String): Person?
}

entity:

@RedisHash
data class Person(
        @get:Id
        @javax.persistence.Id
        open var id: Long = 0,
        @Indexed
        open var name: String = "",
        open var age: Int = 0,
        open var sex: Gender = Gender.MALE
) : Serializable {
    override fun toString(): String {
        return "LongIdPerson(id=$id, name='$name', age=$age, sex=$sex)"
    }
}

 controller:

 @RequestMapping("person/set")
    fun setPerson(): Person =
            personRepository.save(Person(1, "女士", 21, Gender.FEMALE))

    @RequestMapping("person/get")
    fun getPerson(): Person = rersonRepository.findByName("女士")

必要补充:

事实上,我们之前提到的Redis存储对象属性都是基本类型,那么复杂类型结构又如何存储呢?SpringDataRedis给出了存储复杂对象的数据结构点击详情参阅8.3节,有兴趣的可以玩一下.

 

你可能感兴趣的:(Spring-Data-Redis Serializable)