RedisTemplate出现\xac\xed\x00\x05t\x00\x0f前缀解决

问题描叙

出现这种乱码前缀的原因是没有进行序列化,因此导致在传输过程出现乱码问题,存到数据库,发现 key,hash key/value 都有 \xAC\xED\x00\x05t\x00 前缀。RedisTemplate类中默认是没有设置序列化的。

解决方法

设置RedisTemplate的序列化方式

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
 
public class RedisConfig {
    @Bean(name = "redisTemplate")
    //配置redisTemplate
    //默认情况下redisTemplate只能支持redisTemplate<String,String>
    //需要自定义 RedisTemplate,设置序列化
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
        RedisTemplate<String,Object> template = new RedisTemplate <>();
        template.setConnectionFactory(factory);
 
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
 
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

注意:用了StringRedisSerializer之后,redis库的数据都是以String类型保存,所以int类型的整数,在保存时,会类型上升, 先变成double,然后再存成String,也就是1会存成"1.0",所以如果保存int数据时,需要自己在代码中单独处理下。上面修改全局配置不能兼容新老数据,新增配置可以兼容。

修改序列化方式后,数据不兼容报错

org.springframework.data.redis.serializer.SerializationException: Cannot deserialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is java.io.StreamCorruptedException: invalid stream header: E5A487E6

使用多个redisTemplate配置

@Configuration
public class RedisConfig {

    @Bean
    @Primary
    public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
        return redisTemplate;
    }

    @Bean(name = "ByteRedisTemplate")
    public RedisTemplate ByteRedisTemplate(
            RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(RedisSerializer.byteArray());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(RedisSerializer.byteArray());
        return redisTemplate;
    }
}

使用方法 

@RestController
public class PublishDemo {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    @Qualifier("ByteRedisTemplate")
    private RedisTemplate byteRedisTemplate;

    @RequestMapping("set")
    public void set() {
        String key1 = "t1";
        String value = "12345";
        String key2 = "t2";
        String value2 = "54321";
        redisTemplate.opsForValue().set(key1, value);
        byteRedisTemplate.opsForValue().set(key2, value2.getBytes());
    }

    @RequestMapping("get")
    public void get() {
        Object t1 = redisTemplate.opsForValue().get("t1");
        System.out.println("t1:" + t1.toString());
        Object o = byteRedisTemplate.opsForValue().get("t2");
        System.out.println("t2:" + Arrays.toString((byte[]) o));
    }
}

对象存hash

entityMap.forEach((field, value) -> redisTemplate.opsForHash().put(String.format(REDIS_CONN_KEY, entity.getId()), field, value != null ? String.valueOf(value) : ""));

redis判断连接状态,重连机制

if (!isset($this->redis) || !$this->redis->isConnected()) {
  $this->setRedis();
}
$this->redis->exists($key);

/**
 * 连接redis.
 */
private function setRedis()
{
    if (isset($this->redis) && $this->redis->isConnected()) {
        $this->redis->disconnect();
    }
    $this->redis = new RedisClient($this->container->getParameter('redis_mq')); //重连redis;
}

Redis工具类

/**
 * Redis工具类,提供了一系列操作Redis缓存的方法。
 */

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;

public class RedisUtil {

  /**
   * Redis模板对象,用于执行Redis操作。
   */
  private RedisTemplate<String, Object> redisTemplate;

  /**
   * 设置Redis模板对象。
   *
   * @param redisTemplate Redis模板对象。
   */
  public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
    this.redisTemplate = redisTemplate;
  }

  //=============================common============================

  /**
   * 为给定的键设置缓存失效时间。
   *
   * @param key  键
   * @param time 失效时间(秒)
   * @return 设置是否成功
   */
  public boolean expire(String key, long time) {
    try {
      if (time > 0) {
        redisTemplate.expire(key, time, TimeUnit.SECONDS);
      }
      return true;
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
  }

  /**
   * 获取给定键的缓存失效时间。
   *
   * @param key 键
   * @return 失效时间(秒),如果为0表示永不过期
   */
  public long getExpire(String key) {
    return redisTemplate.getExpire(key, TimeUnit.SECONDS);
  }

  /**
   * 检查给定键是否存在。
   *
   * @param key 键
   * @return 如果存在返回true,否则返回false
   */
  public boolean hasKey(String key) {
    try {
      return redisTemplate.hasKey(key);
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
  }

  /**
   * 删除一个或多个键的缓存。
   *
   * @param key 键,可以是多个
   */
  @SuppressWarnings("unchecked")
  public void del(String... key) {
    if (key != null && key.length > 0) {
      if (key.length == 1) {
        redisTemplate.delete(key[0]);
      } else {
        redisTemplate.delete(CollectionUtils.arrayToList(key));
      }
    }
  }

  //============================String=============================

  /**
   * 从缓存中获取字符串键的值。
   *
   * @param key 键
   * @return 值,如果键不存在则返回null
   */
  public Object get(String key) {
    return key == null ? null : redisTemplate.opsForValue().get(key);
  }

  /**
   * 保存数字到Redis中,以字符串形式存储。
   *
   * @param key   Redis中键的名称。
   * @param value 要保存的数字值。
   */
  public void saveNumber(String key, int value) {
    redisTemplate.opsForValue().set(key, String.valueOf(value));
  }

  /**
   * 从Redis中获取保存的数字。
   *
   * @param key Redis中键的名称。
   * @return 如果键存在,则返回对应的数字值;如果键不存在,返回0。
   */
  public int getNumber(String key) {
    int number = 0;
    Object value = get(key);
    if (value != null) {
      number = Integer.parseInt((String) value);
    }
    return number;
  }

  /**
   * 将值存储到缓存中。
   *
   * @param key   键
   * @param value 值
   * @return 存储是否成功
   */
  public boolean set(String key, Object value) {
    try {
      redisTemplate.opsForValue().set(key, value);
      return true;
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }

  }

  /**
   * 将值存储到缓存中,并设置过期时间。
   *
   * @param key   键
   * @param value 值
   * @param time  过期时间(秒)time要大于0 如果time小于等于0 将设置无限期
   * @return 存储是否成功
   */
  public boolean set(String key, Object value, long time) {
    try {
      if (time > 0) {
        redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
      } else {
        set(key, value);
      }
      return true;
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
  }

  /**
   * 尝试在Redis中设置给定的键值对,如果键不存在。redis的过期机制和分布式锁
   * <p>
   * 此方法尝试为给定的键设置一个值,只有在键当前不存在的情况下才成功。
   *
   * @param key   要设置的键,不能为null。
   * @param value 要设置的值,不能为null。
   * @param time  设置值的过期时间,单位为秒。
   * @return 如果设置成功(或键已存在),则返回true;如果发生异常,则返回false。
   */
  public boolean setIfAbsent(String key, Object value, long time) {
    try {
      // 检查键和值是否为null,如果是,则抛出运行时异常。
      if (key == null || value == null) {
        throw new RuntimeException("key/value不能为null");
      }
      // 如果键不存在,则设置键的值,并指定过期时间。
      return redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS);
    } catch (Exception e) {
      // 捕获任何异常,打印堆栈跟踪,并返回false。
      e.printStackTrace();
      return false;
    }
  }

  /**
   * 对给定键的值进行递增操作。
   *
   * @param key   键
   * @param delta 递增值,必须大于0
   * @return 增加后的值
   */
  public long incr(String key, long delta) {
    if (delta < 0) {
      throw new RuntimeException("递增因子必须大于0");
    }
    return redisTemplate.opsForValue().increment(key, delta);
  }

  /**
   * 对给定键的值进行递减操作。
   *
   * @param key   键
   * @param delta 递减值,必须大于0
   * @return 减少后的值
   */
  public long decr(String key, long delta) {
    if (delta < 0) {
      throw new RuntimeException("递减因子必须大于0");
    }
    return redisTemplate.opsForValue().increment(key, -delta);
  }

  //================================Map=================================

  /**
   * 从Hash中获取指定字段的值。
   *
   * @param key  键
   * @param item 字段
   * @return 值
   */
  public Object hget(String key, String item) {
    return redisTemplate.opsForHash().get(key, item);
  }

  /**
   * 获取Hash中所有字段和值。
   *
   * @param key 键
   * @return 字段-值的映射
   */
  public Map<Object, Object> hmget(String key) {
    return redisTemplate.opsForHash().entries(key);
  }

  /**
   * 向Hash中设置多个字段和值。
   *
   * @param key 键
   * @param map 字段-值的映射
   * @return 设置是否成功
   */
  public boolean hmset(String key, Map<String, Object> map) {
    try {
      redisTemplate.opsForHash().putAll(key, map);
      return true;
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
  }

  /**
   * 向Hash中设置多个字段和值,并设置过期时间。
   *
   * @param key  键
   * @param map  字段-值的映射
   * @param time 过期时间(秒)
   * @return 设置是否成功
   */
  public boolean hmset(String key, Map<String, Object> map, long time) {
    try {
      redisTemplate.opsForHash().putAll(key, map);
      if (time > 0) {
        expire(key, time);
      }
      return true;
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
  }

  /**
   * 向Hash中设置字段和值。
   *
   * @param key   键
   * @param item  字段
   * @param value 值
   * @return 设置是否成功
   */
  public boolean hset(String key, String item, Object value) {
    try {
      redisTemplate.opsForHash().put(key, item, value);
      return true;
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
  }

  /**
   * 向指定的hash中添加数据,如果key不存在则创建。
   *
   * @param key   Redis中的键。
   * @param item  Hash中的字段。
   * @param value Hash中字段对应的值。
   * @param time  过期时间,单位为秒。如果大于0,则设置过期时间;否则,不设置过期时间。
   * @return 如果操作成功返回true,否则返回false。
   */
  public boolean hset(String key, String item, Object value, long time) {
    try {
      redisTemplate.opsForHash().put(key, item, value);
      if (time > 0) {
        expire(key, time);
      }
      return true;
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
  }

  /**
   * 从hash中删除指定的字段。
   *
   * @param key  Redis中的键。
   * @param item 要删除的字段,可以是多个。
   */
  public void hdel(String key, Object... item) {
    redisTemplate.opsForHash().delete(key, item);
  }

  /**
   * 检查hash中是否存在指定字段。
   *
   * @param key  Redis中的键。
   * @param item 要检查的字段。
   * @return 如果字段存在返回true,否则返回false。
   */
  public boolean hHasKey(String key, String item) {
    return redisTemplate.opsForHash().hasKey(key, item);
  }

  /**
   * 为hash中的指定字段增加指定数值。
   *
   * @param key  Redis中的键。
   * @param item Hash中的字段。
   * @param by   要增加的数值。
   * @return 增加后的值。
   */
  public double hincr(String key, String item, double by) {
    return redisTemplate.opsForHash().increment(key, item, by);
  }

  /**
   * 为hash中的指定字段减少指定数值。
   *
   * @param key  Redis中的键。
   * @param item Hash中的字段。
   * @param by   要减少的数值。
   * @return 减少后的值。
   */
  public double hdecr(String key, String item, double by) {
    return redisTemplate.opsForHash().increment(key, item, -by);
  }

  //============================set=============================

  /**
   * 获取set中的所有成员。
   *
   * @param key Redis中的键。
   * @return set中的所有成员。
   */
  public Set<Object> sGet(String key) {
    try {
      return redisTemplate.opsForSet().members(key);
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  /**
   * 检查set中是否包含指定成员。
   *
   * @param key   Redis中的键。
   * @param value 要检查的成员。
   * @return 如果set包含指定成员返回true,否则返回false。
   */
  public boolean sHasKey(String key, Object value) {
    try {
      return redisTemplate.opsForSet().isMember(key, value);
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
  }

  /**
   * 向set中添加成员。
   *
   * @param key    Redis中的键。
   * @param values 要添加的成员,可以是多个。
   * @return 成功添加的成员数。
   */
  public long sSet(String key, Object... values) {
    try {
      return redisTemplate.opsForSet().add(key, values);
    } catch (Exception e) {
      e.printStackTrace();
      return 0;
    }
  }

  /**
   * 向set中添加成员并设置过期时间。
   *
   * @param key    Redis中的键。
   * @param time   过期时间,单位为秒。
   * @param values 要添加的成员,可以是多个。
   * @return 成功添加的成员数。
   */
  public long sSetAndTime(String key, long time, Object... values) {
    try {
      Long count = redisTemplate.opsForSet().add(key, values);
      if (time > 0) {
        expire(key, time);
      }
      return count;
    } catch (Exception e) {
      e.printStackTrace();
      return 0;
    }
  }

  /**
   * 获取Set集合的大小。
   *
   * @param key Set集合的键。
   * @return Set集合的大小。
   */
  public long sGetSetSize(String key) {
    try {
      return redisTemplate.opsForSet().size(key);
    } catch (Exception e) {
      e.printStackTrace();
      return 0;
    }
  }

  /**
   * 从Set集合中移除指定的值。
   *
   * @param key    Set集合的键。
   * @param values 要移除的值,可以是多个。
   * @return 被移除的元素数量。
   */
  public long setRemove(String key, Object... values) {
    try {
      Long count = redisTemplate.opsForSet().remove(key, values);
      return count;
    } catch (Exception e) {
      e.printStackTrace();
      return 0;
    }
  }

  //===============================list=================================

  /**
   * 获取List集合中指定范围的元素。
   *
   * @param key   List集合的键。
   * @param start 起始索引。
   * @param end   结束索引  0 到 -1代表所有值。
   * @return 指定范围内的元素列表。
   */
  public List<Object> lGet(String key, long start, long end) {
    try {
      return redisTemplate.opsForList().range(key, start, end);
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  /**
   * 获取List集合的大小。
   *
   * @param key List集合的键。
   * @return List集合的大小。
   */
  public long lGetListSize(String key) {
    try {
      return redisTemplate.opsForList().size(key);
    } catch (Exception e) {
      e.printStackTrace();
      return 0;
    }
  }

  /**
   * 获取List集合中指定索引的元素。
   *
   * @param key   List集合的键。
   * @param index 索引,index>=0时,0表示列表第一个元素,依次类推;index<0时,-1表示列表最后一个元素,依次类推。
   * @return 指定索引的元素。
   */
  public Object lGetIndex(String key, long index) {
    try {
      return redisTemplate.opsForList().index(key, index);
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  /**
   * 在List集合的尾部添加一个元素。
   *
   * @param key   List集合的键。
   * @param value 要添加的元素。
   * @return 是否添加成功。
   */
  public boolean lSet(String key, Object value) {
    try {
      redisTemplate.opsForList().rightPush(key, value);
      return true;
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
  }

  /**
   * 在List集合的尾部添加一个元素,并设置过期时间。
   *
   * @param key   List集合的键。
   * @param value 要添加的元素。
   * @param time  过期时间,单位为秒。
   * @return 是否添加成功。
   */
  public boolean lSet(String key, Object value, long time) {
    try {
      redisTemplate.opsForList().rightPush(key, value);
      if (time > 0) {
        expire(key, time);
      }
      return true;
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
  }

  /**
   * 将给定的列表值添加到指定键的列表末尾。如果列表不存在,会创建一个空列表。
   *
   * @param key   键
   * @param value 列表值
   * @return 如果操作成功返回true,否则返回false。
   */
  public boolean lSet(String key, List<Object> value) {
    try {
      redisTemplate.opsForList().rightPushAll(key, value);
      return true;
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
  }

  /**
   * 将给定的列表值添加到指定键的列表末尾,并设置过期时间。如果列表不存在,会创建一个空列表。
   *
   * @param key   键
   * @param value 列表值
   * @param time  过期时间,单位为秒
   * @return 如果操作成功返回true,否则返回false。
   */
  public boolean lSet(String key, List<Object> value, long time) {
    try {
      redisTemplate.opsForList().rightPushAll(key, value);
      if (time > 0) {
        expire(key, time);
      }
      return true;
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
  }

  /**
   * 更新列表中指定索引位置的元素值。
   *
   * @param key   键
   * @param index 索引位置
   * @param value 新的元素值
   * @return 如果操作成功返回true,否则返回false。
   */
  public boolean lUpdateIndex(String key, long index, Object value) {
    try {
      redisTemplate.opsForList().set(key, index, value);
      return true;
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
  }

  /**
   * 从列表中移除指定数量与给定值相等的元素。
   *
   * @param key   键
   * @param count 要移除的元素数量,可以是正数(从头部开始移除)或负数(从尾部开始移除)
   * @param value 要移除的元素值
   * @return 实际移除的元素数量。
   */
  public long lRemove(String key, long count, Object value) {
    try {
      Long remove = redisTemplate.opsForList().remove(key, count, value);
      return remove;
    } catch (Exception e) {
      e.printStackTrace();
      return 0;
    }
  }
  /**
   * 添加一个元素, zset与set最大的区别就是每个元素都有一个score,因此有个排序的辅助功能;  zadd
   *
   * @param key
   * @param value
   * @param score
   */
  public boolean zsetAdd(String key, Object value, Double score) {
    try {
      redisTemplate.opsForZSet().add(key, value, score);
      return true;
    } catch (Exception e) {
      e.printStackTrace();
      return false;
    }
  }

  /**
   * 删除元素 zrem
   *
   * @param key
   * @param value
   */
  public void remove(String key, String value) {
    redisTemplate.opsForZSet().remove(key, value);
  }

  /**
   * 删除zSet 中过期元素
   *
   * @param key
   * @param v1
   * @param v2
   */
  public void removeRangeByScore(String key, Double v1, Double v2) {
    redisTemplate.opsForZSet().removeRangeByScore(key, v1, v2);
  }

  /**
   * @param key
   * @param value
   * @param score
   */
  public Double incrScore(String key, String value, double score) {
    return redisTemplate.opsForZSet().incrementScore(key, value, score);
  }

  /**
   * 查询value对应的score   zscore
   *
   * @param key
   * @param value
   * @return
   */
  public Double score(String key, String value) {
    return redisTemplate.opsForZSet().score(key, value);
  }

  /**
   * 判断value在zset中的排名  zrank
   *
   * @param key
   * @param value
   * @return
   */
  public Long rank(String key, String value) {
    return redisTemplate.opsForZSet().rank(key, value);
  }

  /**
   * 返回集合的长度
   *
   * @param key
   * @return
   */
  public Long size(String key) {
    return redisTemplate.opsForZSet().zCard(key);
  }

  /**
   * 查询集合中指定顺序的值, 0 -1 表示获取全部的集合内容  zrange
   * <p>
   * 返回有序的集合,score小的在前面
   *
   * @param key
   * @param start
   * @param end
   * @return
   */
  public Set<Object> range(String key, int start, int end) {
    return redisTemplate.opsForZSet().range(key, start, end);
  }

  /**
   * 获取字符串类型
   *
   * @param key
   * @return
   */
  public String getString(String key) {
    Object value = redisTemplate.opsForValue().get(key);
    if (null != value) {
      return key == null ? null : String.valueOf(value);
    } else {
      return null;
    }
  }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值