Spring Boot con Redis para almacenar JSON y no la serialización predefinida

En esta entrada mostraré como utilizar Spring Boot y Redis con el objetivo de almacenar los objetos como JSON y no como la forma de serialización de Java por defecto que tiene RedisTemplate. Los test están con Testcontainers, Junit 5 y AssertJ.


  • Lo primero es la clase que nos convoca, la configuración de RedisTemplate:
@Configuration
public class RedisConfig {

  @Autowired
  private ConfigurableApplicationContext context;

  @Bean
  @Primary
  public RedisTemplate redisTemplate(JedisConnectionFactory conn) {
    RedisTemplate template = new RedisTemplate<>();
    template.setConnectionFactory(conn);
    template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
    template.setKeySerializer(new StringRedisSerializer());
    template.setHashKeySerializer(new StringRedisSerializer());
    template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    return template;
  }

  @Bean
  public JedisConnectionFactory redisConnectionFactory(@Value("${redis.host}") String host,
          @Value("${redis.puerto}") int puerto) {
    RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(host, puerto);
    return new JedisConnectionFactory(config);
  }
}

  • En la clase anterior, lo que importa para el objetivo del ejemplo es lo siguiente:
    template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
    template.setKeySerializer(new StringRedisSerializer());
    template.setHashKeySerializer(new StringRedisSerializer());
    template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    
    Acá definimos que en redis el valor será un JSON y las claves serán String.
  • La clase que usaré para la prueba es la siguiente:
    public class Persona {
    
      private int id;
      private String nombre;
    
      public int getId() {
        return id;
      }
    
      public void setId(int id) {
        this.id = id;
      }
    
      public String getNombre() {
        return nombre;
      }
    
      public void setNombre(String nombre) {
        this.nombre = nombre;
      }
    
    }
  • La clase de entrada para la aplicación:
    @SpringBootApplication
    public class RedisStringApplication {
    
      public static void main(String[] args) {
        SpringApplication.run(RedisStringApplication.class, args);
      }
    }
  • Y la clase de Test:
    @SpringBootTest
    @ContextConfiguration(initializers = {RedisStringJsonTests.Initializer.class})
    public class RedisStringJsonTests {
      private static String redisHost;
      private static int redisPuerto;
      
      @Rule
      public static GenericContainer cont = new GenericContainer<>("redis:5.0.3-alpine")
              .withExposedPorts(6379);
    
      @AfterAll
      static void detenerRedis() {
        cont.stop();
      }
    
      @BeforeAll
      static void iniciarRedis() {
        cont.start();
      }
    
      static class Initializer
              implements ApplicationContextInitializer {
    
        @Override
        public void initialize(ConfigurableApplicationContext ctx) {
          redisHost = cont.getContainerIpAddress();
          redisPuerto = cont.getFirstMappedPort();
          TestPropertyValues.of(
                  "redis.puerto=" + redisPuerto,
                  "redis.host=" + redisHost
          ).applyTo(ctx.getEnvironment());
        }
      }
    
      @Autowired
      private RedisTemplate redis;
    
      @Test
      public void validarRegistroJson() throws IOException {
        assertThat(redis).isNotNull();
        var hash = redis.opsForHash();
        final Persona p = new Persona();
        final var claveRedis = "redis-clave";
        final var claveRegistro = "clave";
        p.setId(1);
        p.setNombre("nombre");
        hash.put(claveRedis, claveRegistro, p);
        var x = (Persona) hash.get(claveRedis, claveRegistro);
        assertThat(x.getId()).isEqualTo(1);
        assertThat(x.getNombre()).isEqualTo("nombre");
        
        Jedis jedis = new Jedis(redisHost, redisPuerto);
        final var recuperado = jedis.hget(claveRedis, claveRegistro);
        var personaRecuperada = new ObjectMapper().readValue(recuperado, Persona.class);
        assertThat(personaRecuperada).isEqualToComparingFieldByField(p);
      }
    }
  • El Test consiste en lo siguiente:
    • Utiliza testcontainers para ejecutar redis
    • Define propiedades para acceder a Redis al inicializar los beans
    • Utiliza RedisTemplate para guardar un objeto y luego recuperarlo
    • Recuperar el valor json guardado para transformarlo en una instancia de la clase
  • El test demuestra que se puede guardar y recuperar el objeto y también se realiza una nueva conexion para recuperar el String y transformarlo para compararlo con la instancia que se guardó.
  • Para confirmar que se guardó como JSON la instancia al ser serializada, usando los datos que entrega testcontainers accederé al redis del contenedor para validar que se guardó como queremos (breakpoint en el código para conocer el puerto 😁):

Eso es todo, el código queda disponible en Github 👀

No hay comentarios.:

Publicar un comentario