SuperMe
SuperMe

Reputation: 103

Generic RedisTemplate using Jackson2JsonRedisSerializer in Kotlin

My controller uses a redis template to get and set data from redis

val redisTemplate: RedisTemplate<String, Game>

The template is injected using a bean

@Bean
fun <T> redisTemplate(builder: RestTemplateBuilder): RedisTemplate<String, T> {
    val serializer = Jackson2JsonRedisSerializer(Game::class.java)
    serializer.setObjectMapper(objectMapper)

    val template = RedisTemplate<String, T>()
    template.setConnectionFactory(connectionFactory())
    template.setDefaultSerializer(serializer)
    return template
}

This works however I have had to be explicit about the top level class being serialized and therefore the template returned will only work for RedisTemplate<String, Game> and not for RedisTemplate<String, T>.

I have tried changing the serializer instantiation to the following but it doesn't compile.

val serializer = Jackson2JsonRedisSerializer(T::class.java)

I have tried changing it to the following

val serializer = Jackson2JsonRedisSerializer(Object::class.java)

This compiles but I get an exception when reading from redis

java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class com.noicesoftware.redis.model.Game (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; com.noicesoftware.redis.model.Game is in unnamed module of loader 'app')

I have tried using GenericJackson2JsonRedisSerializer but I get the behaviour and exception as above.

val serializer = GenericJackson2JsonRedisSerializer(objectMapper)

Any thoughts would be greatly appreciated. The full code is available here; https://github.com/DangerousDarlow/springboot-redis

Upvotes: 2

Views: 4809

Answers (1)

SuperMe
SuperMe

Reputation: 103

I haven't been able to find a solution to keep the bean generic. If I make the type reified then I must also make the function inline which and then either private or final. This causes runtime errors. My solution is to move the generic code into a private inline function and then declare a bean for each template type I want. Not as neat as I had hoped but it's not too bad I suppose.

private inline fun <reified T> getRedisTemplate(): RedisTemplate<String, T> {
    val serializer = Jackson2JsonRedisSerializer(T::class.java)
    serializer.setObjectMapper(objectMapper)

    val template = RedisTemplate<String, T>()
    template.setConnectionFactory(connectionFactory())
    template.setDefaultSerializer(serializer)
    return template
}

@Bean
fun redisTemplate(): RedisTemplate<String, Game> = getRedisTemplate()

Upvotes: 2

Related Questions