最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

利用spring-data-redis實(shí)現(xiàn)incr自增的操作

 更新時(shí)間:2020年11月24日 10:40:57   作者:快樂崇拜234  
這篇文章主要介紹了利用spring-data-redis實(shí)現(xiàn)incr自增的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧

應(yīng)該有不少人在使用spring-data-redis時(shí)遇到各種各樣的問題。反正我是遇到了。

由于是隔了一段時(shí)間才寫的本篇博客,也懶得去重現(xiàn)哪些錯(cuò)誤場(chǎng)景了,下面憑著記憶寫了幾個(gè)我遇到的問題:

redis.clients.jedis.exceptions.JedisDataException: ERR value is not an integer or out of range

使用的RedisTemplate,做讀寫操作時(shí)候,都是要經(jīng)過序列化和反序列化。

這時(shí)你使用redisTemplate.opsForValue().increment()就可能報(bào)錯(cuò)redis.clients.jedis.exceptions.JedisDataException: ERR value is not an integer or out of range了。

valueOper.get(key) 獲取不到自增的值。

于是我去看了一下redis的官方文檔,找到一個(gè)解決方法

使用spring-data-redis實(shí)現(xiàn)incr自增
/**
   *
   * @param key
   * @param liveTime
   * @return
   */
  public Long incr(String key, long liveTime) {
    RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
    Long increment = entityIdCounter.getAndIncrement();

    if ((null == increment || increment.longValue() == 0) && liveTime > 0) {//初始設(shè)置過期時(shí)間
      entityIdCounter.expire(liveTime, TimeUnit.SECONDS);
    }

    return increment;
  }

這樣,上面的increment就是自增后的新知值,然后中間通過entityIdCounter.expire(liveTime, TimeUnit.SECONDS);設(shè)置過期時(shí)間。

當(dāng)然這里比較討厭,spring沒有在創(chuàng)建RedisAtomicLong對(duì)象的時(shí)候一起設(shè)置過期時(shí)間。

可以看看其源碼,new RedisAtomicLong最終調(diào)用的是這個(gè)方法:

private RedisAtomicLong(String redisCounter, RedisConnectionFactory factory, Long initialValue) {
    Assert.hasText(redisCounter, "a valid counter name is required");
    Assert.notNull(factory, "a valid factory is required");

    RedisTemplate<String, Long> redisTemplate = new RedisTemplate<String, Long>();
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    redisTemplate.setValueSerializer(new GenericToStringSerializer<Long>(Long.class));
    redisTemplate.setExposeConnection(true);
    redisTemplate.setConnectionFactory(factory);
    redisTemplate.afterPropertiesSet();

    this.key = redisCounter;
    this.generalOps = redisTemplate;
    this.operations = generalOps.opsForValue();

    if (initialValue == null) {
      if (this.operations.get(redisCounter) == null) {
        set(0);
      }
    } else {
      set(initialValue);
    }
  }

可以看到,初始值是0。

然后根進(jìn)set方法

public void set(long newValue) {
    operations.set(key, newValue);
  }

可以看到,他是采用的operations.set(key, newValue);但是明明還有一個(gè)重載的方法void set(K key, V value, long timeout, TimeUnit unit);可以設(shè)置過期時(shí)間,為啥spring不提供呢。

為了解決這個(gè)問題,我們可以自己模擬RedisAtomicLong方法,去實(shí)現(xiàn)一個(gè)帶有過期時(shí)間的自增方法。比較簡(jiǎn)單,讀者自行擼代碼吧,這里就不寫出了。

補(bǔ)充知識(shí):關(guān)于spring boot使用redis的increment()方法自增問題

需求是限制IP頻繁訪問某接口,用的方案是使用redis記錄訪問IP的值,先設(shè)定好初始值,每次訪問自增,達(dá)到某限定值后,進(jìn)行阻止。

用的是自定義工具類,使用spring封裝的spring-data-redis進(jìn)行操作,在對(duì)某key進(jìn)行increment()方法時(shí),報(bào)錯(cuò):

redis ERR value is not an integer or out of range

代碼邏輯如下:

Integer count = (Integer) redisUtil.get(ipAddress);//取得key的value
    if (count == null){
      redisUtil.set(ipAddress,1,10);
      return false;
    }else if(count == 3){
      return false;
    }else {
      redisUtil.incr(ipAddress,1);
      return false;
    }

第一次進(jìn)來,如果沒有redis中沒有數(shù)據(jù),則設(shè)置key,value和time,key是ip, value初始值為1,有效時(shí)長(zhǎng)為10秒。

如果沒達(dá)到限制次數(shù),則對(duì)key自增1。

redisUtil.incr()方法實(shí)現(xiàn)如下:

@Resource
  private RedisTemplate<String, Object> redisTemplate; //這里使用的是redisTemplate

  public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
    this.redisTemplate = redisTemplate;
  }

  /**
   * 遞增
   * @param key 鍵
//   * @param by 要增加幾(大于0)
   * @return
   */
  public long incr(String key, long delta){
    if(delta<0){
      throw new RuntimeException("遞增因子必須大于0");
    }
    return redisTemplate.opsForValue().increment(key, delta);
  }

開始以為是incr方法接受的參數(shù)是long型,但我傳入的是INTEGER類型,但轉(zhuǎn)換后還是沒有解決問題,問題不是出在這,后來通過查找資料發(fā)現(xiàn),Spring對(duì)Redis序列化的策略有兩種,分別是StringRedisTemplate和RedisTemplate,其中StringRedisTemplate用于操作字符串,RedisTemplate使用的是JDK默認(rèn)的二進(jìn)制序列化。

大家都知道redis序列化是將key,value值先轉(zhuǎn)換為流的形式,再存儲(chǔ)到redis中。

RedisTemplate是使用的JdkSerializationRedisSerializer序列化,序列化后的值包含了對(duì)象信息,版本號(hào),類信息等,是一串字符串,所以無法進(jìn)行數(shù)值自增操作。

而StringRedisTemplate序列化策略是字符串的值直接轉(zhuǎn)為字節(jié)數(shù)組,所以存儲(chǔ)到redis中是數(shù)值,所以可以進(jìn)行自增操作。

StringRedisSerializer源碼:

public class StringRedisSerializer implements RedisSerializer<String> {
  private final Charset charset;

  public StringRedisSerializer() {
    this(StandardCharsets.UTF_8);
  }

  public StringRedisSerializer(Charset charset) {
    Assert.notNull(charset, "Charset must not be null!");
    this.charset = charset;
  }

  public String deserialize(@Nullable byte[] bytes) {
    return bytes == null ? null : new String(bytes, this.charset);
  }

  public byte[] serialize(@Nullable String string) {
    return string == null ? null : string.getBytes(this.charset); //注意這里是字節(jié)數(shù)組
  }
}

所以問題出在這里,我們需要自定義序列化策略,在application啟動(dòng)類中添加如下:

 @Bean
  public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
    StringRedisTemplate template = new StringRedisTemplate(factory);
    //定義key序列化方式
    //RedisSerializer<String> redisSerializer = new StringRedisSerializer();//Long類型會(huì)出現(xiàn)異常信息;需要我們上面的自定義key生成策略,一般沒必要
    //定義value的序列化方式
    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);

    // template.setKeySerializer(redisSerializer);
    template.setValueSerializer(jackson2JsonRedisSerializer);
    template.setHashValueSerializer(jackson2JsonRedisSerializer);
    template.afterPropertiesSet();
    return template;

費(fèi)了2多小時(shí)才成功解決問題,RedisUtil.incr()能夠成功對(duì)key進(jìn)行自增了,如有錯(cuò)誤之處請(qǐng)歡迎指出。

以上這篇利用spring-data-redis實(shí)現(xiàn)incr自增的操作就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Spring?Data?Elasticsearch使用方式(Elasticsearch)

    Spring?Data?Elasticsearch使用方式(Elasticsearch)

    這篇文章主要介紹了Spring?Data?Elasticsearch使用方式(Elasticsearch),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • 如何編寫javascript的gulp插件

    如何編寫javascript的gulp插件

    本文主要介紹了使用PMD進(jìn)行代碼審查的方法,具有很好的參考價(jià)值,下面跟著小編一起來看下吧
    2017-02-02
  • 如何使用SpringBootCondition更自由地定義條件化配置

    如何使用SpringBootCondition更自由地定義條件化配置

    這篇文章主要介紹了如何使用SpringBootCondition更自由地定義條件化配置,幫助大家更好的理解和學(xué)習(xí)使用springboot框架,感興趣的朋友可以了解下
    2021-04-04
  • 解讀@RequestBody與post請(qǐng)求的關(guān)系

    解讀@RequestBody與post請(qǐng)求的關(guān)系

    這篇文章主要介紹了解讀@RequestBody與post請(qǐng)求的關(guān)系,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • Spring Boot集成Swagger接口分類與各元素排序問題

    Spring Boot集成Swagger接口分類與各元素排序問題

    這篇文章主要介紹了Spring Boot集成Swagger接口分類與各元素排序問題,首先我們需要對(duì)Swagger中的接口也就是以Controller 層作為第一級(jí)梯度進(jìn)行組織的,Controller在我們實(shí)際開發(fā)中,與其他具體接口之間是存在一對(duì)多的關(guān)系,本文給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2023-10-10
  • Java解決約瑟夫問題代碼實(shí)例

    Java解決約瑟夫問題代碼實(shí)例

    這篇文章主要介紹了Java解決約瑟夫(環(huán))問題的代碼實(shí)例,決約瑟問題貌似經(jīng)常出現(xiàn)在面試題中,需要的朋友可以參考下
    2014-03-03
  • Java Socket編程實(shí)例(一)- TCP基本使用

    Java Socket編程實(shí)例(一)- TCP基本使用

    這篇文章主要講解Java Socket編程中TCP的基本使用,希望能給大家做一個(gè)參考。
    2016-06-06
  • JAVA如何獲取工程下的文件

    JAVA如何獲取工程下的文件

    這篇文章主要介紹了JAVA如何獲取工程下的文件,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-08-08
  • 解決springboot集成swagger碰到的坑(報(bào)404)

    解決springboot集成swagger碰到的坑(報(bào)404)

    這篇文章主要介紹了解決springboot集成swagger碰到的坑(報(bào)404),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • 一篇文章大家徹底學(xué)會(huì)Java之格式化輸出

    一篇文章大家徹底學(xué)會(huì)Java之格式化輸出

    Java提供了多種格式化輸出的方式,包括String.format()、System.out.printf()和Formatter類,這些方法支持?jǐn)?shù)字、日期、字符串等多種格式化操作,這篇文章主要介紹了Java格式化輸出的相關(guān)資料,需要的朋友可以參考下
    2025-04-04

最新評(píng)論

黄冈市| 临沧市| 德化县| 集安市| 聂荣县| 广宁县| 昌都县| 溧水县| 济南市| 上杭县| 嘉义县| 米脂县| 桃园市| 凯里市| 常德市| 天长市| 左权县| 泽库县| 陆河县| 建德市| 嘉义县| 杭锦后旗| 山东省| 海林市| 板桥市| 临桂县| 施甸县| 正宁县| 南漳县| 景宁| 偃师市| 奉贤区| 绵阳市| 泰来县| 田林县| 宁晋县| 桂东县| 万宁市| 介休市| 湾仔区| 宿松县|