前面学习了两种关于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
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节,有兴趣的可以玩一下.