<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【文末送書】還在直接操作Redis?你Out啦!

          共 7031字,需瀏覽 15分鐘

           ·

          2021-08-05 08:10

          有些人還在直接用Jedis操作Redis數(shù)據(jù)庫,但這種方式非常不方便,而且很不靈活。用Spring Boot整合Redis之后,既能非常方便地操作Redis數(shù)據(jù)庫,Spring Boot又可以自由地在LettuceJedis等技術(shù)之間自由切換。
          目前Jedis操作Redis已經(jīng)趨于淘汰,而是應(yīng)該使用LettuceSpring Data Redis模塊默認(rèn)使用Lettuce。
          由于Spring Data是高層次的抽象,而SpringData Redis只是屬于底層的具體實(shí)現(xiàn),因此Spring Data Redis也提供了與前面Spring Data完全一致的操作。
          歸納起來,Spring Data Redis大致包括如下幾方面功能。
          • DAO接口只需繼承CrudRepository,Spring Data Redis能為DAO組件提供實(shí)現(xiàn)類。

          • Spring Data Redis支持方法名關(guān)鍵字查詢,只不過Redis查詢的屬性必須是被索引過的。

          • Spring Data Redis同樣支持DAO組件添加自定義的查詢方法—通過添加額外的接口,并為額外的接口提供實(shí)現(xiàn)類,Spring Data Redis就能將該實(shí)現(xiàn)類中的方法“移植”到DAO組件中。

          • Spring Data Redis同樣支持Example查詢。

          需要說明的是,Spring Data Redis支持的方法名關(guān)鍵字查詢功能不如JPA強(qiáng)大,這是由Redis底層決定的—Redis不支持任何查詢,它是一個簡單的key-value數(shù)據(jù)庫,它獲取數(shù)據(jù)的唯一方式就是根據(jù)key獲取value。因此它不能支持GreaterThan、LessThan、Like等復(fù)雜關(guān)鍵字,它只能支持如下簡單的關(guān)鍵字。
          • And比如在接口中可以定義“findByNameAndAge”。

          • Or比如“findByNameOrAge”。

          • Is、Equals比如“findByNameIs”“findByName”“findByNameEquals”。這種表示相同或相等的關(guān)鍵字不加也行。

          • TopFirst比如“findFirst5Name”“findTop5ByName”,實(shí)現(xiàn)查詢前5條記錄。

          那問題來了,Spring Data操作的是數(shù)據(jù)類(對JPA則是持久化類),那么它怎么處理數(shù)據(jù)類與Redis之間的映射關(guān)系呢?其實(shí)很簡單,SpringData Redis提供了如下兩個注解。
          • @RedisHash該注解指定將數(shù)據(jù)類映射到RedisHash對象。

          • @TimeToLive該注解修飾一個數(shù)值類型的屬性,用于指定該對象的超時時長。

          此外,Spring Data Redis還提供了如下兩個索引化注解。
          • @Indexed指定對普通類型的屬性建立索引,索引化后的屬性可用于查詢。

          • @GeoIndexed指定對Geo數(shù)據(jù)(地理數(shù)據(jù))類型的屬性建立索引。

          在理解了Spring Data Redis的設(shè)計之后,接下來通過示例來介紹Spring Data Redis的功能和用法。首先依然是創(chuàng)建一個Maven項(xiàng)目,然后讓其pom.xml文件繼承spring-boot-starter-parent,并添加spring-boot-starter-data-redis.jar依賴和commons-pool2.jar依賴。由于本例使用SpringBoot的測試支持來測試DAO組件,因此還添加了spring-boot-starter-test.jar依賴。具體可以參考本例的pom.xml文件。
          先為本例定義application.properties文件,用來指定Redis服務(wù)器的連接信息。
          程序清單
          spring.redis.host=localhostspring.redis.port=6379# 指定連接Redis的DB1數(shù)據(jù)庫spring.redis.database=1# 連接密碼spring.redis.password=32147# 指定連接池中最大的活動連接數(shù)為20spring.redis.lettuce.pool.maxActive = 20# 指定連接池中最大的空閑連接數(shù)為20spring.redis.lettuce.pool.maxIdle=20# 指定連接池中最小的空閑連接數(shù)為2spring.redis.lettuce.pool.minIdle = 2
          下面定義本例用到的數(shù)據(jù)類。
          程序清單
          @RedisHash("book")public class Book{    // 標(biāo)識屬性,可用于查詢    @Id    privateInteger id;    // 帶@Indexed注解的屬性被稱為“二級索引”,可用于查詢    @Indexed    privateString name;    @Indexed    privateString description;    privateDouble price;    // 定義它的超時時長    @TimeToLive(unit = TimeUnit.HOURS)    Longtimeout;    // 省略getter、setter方法和構(gòu)造器    ...}
          上面的Book類使用了@RedisHash("book")修飾,這意味著將該類的實(shí)例映射到Redis中的key都會增加book前綴。
          上面的id實(shí)例變量使用了@Id修飾,這表明它是一個標(biāo)識屬性,這一點(diǎn)和所有Spring Data的設(shè)計都是一樣的。
          上面的namedescription兩個實(shí)例變量使用了@Indexed修飾,這表明它們將會被“索引化”—其實(shí)就是為它們創(chuàng)建對應(yīng)的key,后面會看到詳細(xì)示例。
          接下來定義本例中DAO組件的接口。
          程序清單:
          public interface BookDaoextends CrudRepository<Book, Integer>,       QueryByExampleExecutor<Book>{   List<Book> findByName(Stringname);   List<Book> findByDescription(StringsubDesc);}
          正如從上面代碼所看到的,該DAO接口繼承了CrudRepository,這是Spring DataDAO組件的通用要求。此外,該DAO接口還繼承了QueryByExampleExecutor,這意味著它也可支持Example查詢。
          下面為該DAO組件定義測試用例,該測試用例的代碼如下。
          程序清單:
          @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)public class BookDaoTest{    @Autowired    private BookDaobookDao;     @Test    publicvoid testSaveWithId(){        varbook = new Book("瘋狂Python",               "系統(tǒng)易懂的Python圖書,覆蓋數(shù)據(jù)分析、爬蟲等熱門內(nèi)容", 118.0);        // 顯式設(shè)置id,通常不建議設(shè)置       book.setId(2);        book.setTimeout(5L); // 設(shè)置超時時長        bookDao.save(book);    }    @Test    publicvoid testUpdate(){        // 更新id為2的Book對象        bookDao.findById(2)               .ifPresent(book -> {                   book.setName("瘋狂Python講義");                   bookDao.save(book);               });    }    @Test    publicvoid testDelete(){        // 刪除id為2的Book對象        bookDao.deleteById(2);    }    @ParameterizedTest    @CsvSource({"瘋狂Java講義, 最全面深入的Java圖書, 129.0",           "SpringBoot終極講義,無與倫比的SpringBoot圖書, 119.0"})    publicvoid testSave(String name, String description, Double price){        varbook = new Book(name, description, price);        bookDao.save(book);    }    @ParameterizedTest    @ValueSource(strings= {"瘋狂Java講義"})    publicvoid testFindByName(String name){        bookDao.findByName(name).forEach(System.out::println);    }    @ParameterizedTest    @ValueSource(strings= {"最全面深入的Java圖書"})    publicvoid testFindByDescription(String description){        bookDao.findByDescription(description).forEach(System.out::println);    }    @ParameterizedTest    @CsvSource({"瘋狂Java講義, 最全面深入的Java圖書"})    publicvoid testExampleQuery1(String name, String description){        // 創(chuàng)建樣本對象(probe)        vars = new Book(name, description, 1.0);        // 不使用ExampleMatcher,創(chuàng)建默認(rèn)的Example        bookDao.findAll(Example.of(s)).forEach(System.out::println);    }    @ParameterizedTest    @ValueSource(strings= {"SpringBoot終極講義"})    publicvoid testExampleQuery2(String name){        // 創(chuàng)建matchingAll的ExampleMatcher        ExampleMatchermatcher = ExampleMatcher.matching()               // 忽略null屬性,該方法可以省略               //.withIgnoreNullValues()               .withIgnorePaths("description"); // 忽略description屬性        // 創(chuàng)建樣本對象(probe)        vars = new Book(name, "test", 1.0);        bookDao.findAll(Example.of(s,matcher)).forEach(System.out::println);    }}
          雖然上面DAO組件中只定義了兩個方法,但由于DAO接口繼承了CrudRepositoryQueryByExampleExecutor,它們?yōu)?/span>DAO接口提供了大量方法。
          運(yùn)行上面的testSaveWithId()方法,該方法測試BookDaosave()方法,該方法運(yùn)行完成后看不到任何輸出。但打開AnotherRedis DeskTop Manager連接DB1,則可看到圖1所示的數(shù)據(jù)。

          1  通過save()方法保存的數(shù)據(jù)
          從圖1可以看到,雖然程序只保存了一個Book對象,但Redis底層生成了大量key-value對,由于前面在Book類上增加了@RedisHash("book")注解,因此這些key的名字都以“book”開頭。
          先看名為“book”的key,圖1顯示了該key的內(nèi)容,該key對應(yīng)一個Set,該Set中的元素就是每個Book對象的標(biāo)識屬性值。由于此時系統(tǒng)中僅有一個Book對象,因此該key對應(yīng)的Set中只有一個元素。
          再看名為“book:標(biāo)識屬性值”(此處就是book:2)的key,圖2顯示了該key的內(nèi)容。

          2  實(shí)際保存的對象
          從圖2可以看到,“book:標(biāo)識屬性值”key所對應(yīng)的是一個Hash,它完整地保存了整個Book對象的所有數(shù)據(jù),這就是Redis性能非常好的原因—當(dāng)程序要根據(jù)id獲取某個Book對象時,Redis直接獲取key為“book:id值”的value,這樣就得到了該Book對象的全部數(shù)據(jù)。
          前面還為Book對象的name、description屬性添加了@Indexed注解,因此Spring Data還會為它們創(chuàng)建對應(yīng)的key,從而實(shí)現(xiàn)高速查找。接下來看名為“book:name:瘋狂Python”的key的內(nèi)容,可以看到如圖3所示的數(shù)據(jù)。

          3  被索引的屬性
          從圖2可以看到,“book:name:瘋狂Pythonkey對應(yīng)的是一個Set,該Set的成員就是Book對象的id。此處為何要用Set呢?因?yàn)楫?dāng)程序保存多個Book對象時,完全有可能多個Book對象的name屬性值都是“瘋狂Python”,此時它們的id都需要由“book:name:瘋狂Pythonkey所對應(yīng)的Set負(fù)責(zé)保存,因此該key對應(yīng)的是一個Set
          由此可見,當(dāng)對數(shù)據(jù)類的某個屬性使用@Indexed注解修飾之后,在保存該數(shù)據(jù)對象時就會為它保存一個名為“類映射名:屬性名:屬性值”的key,在該key對應(yīng)的Set中將會添加該對象的標(biāo)識屬性。
          最后來看key為“book:標(biāo)識屬性值:idx”的內(nèi)容,可以看到如圖4所示的數(shù)據(jù)。

          保存對象額外的key
          從圖4可以看到,key為“book:標(biāo)識屬性值:idx”的內(nèi)容也是Set,它保存該對象所有額外的key
          假如程序要查找name(假設(shè)name@Indexed修飾)為“瘋狂Python”的圖書,Spring Data Redis底層會怎么做呢?Spring Data Redis會直接獲取“book:name:瘋狂Pythonkey對應(yīng)的Set,該Set中包含了所有name為“瘋狂Python”的Book對象的id,然后遍歷該Set的每個元素—每個元素都是一個id,接下來Spring Data Redis再獲取“book:id值”對應(yīng)的Hash對象,這樣就獲得了所有符合條件的Book對象。在這個過程中,Spring Data Redis的兩次操作都是通過key來獲取value的,因此效率非常高,這都得益于SpringData Redis的優(yōu)良設(shè)計和Redis的高效性能。
          如果要保存一個所有屬性都不用@Indexed修飾的Book對象,則只需要改變兩個key。
          • book:在該key對應(yīng)的Set中添加新Book對象的id。

          • book:id:該key保存該Book對象的全部數(shù)據(jù)。

          如果要保存一個有N個屬性使用@Indexed修飾的Book對象,則需要改變?nèi)缦?/span>key。
          • book:在該key對應(yīng)的Set中添加新Book對象的id。

          • book:id:該key對應(yīng)的Hash對象保存了該Book對象的全部數(shù)據(jù)。

          • book:id:idx:該key對應(yīng)的Set保存了該Book對象所有額外的key。

          • Nbook:屬性名:屬性值:該key對應(yīng)的Set保存了所有該屬性都具有相同屬性值的Book對象的id值。


          以上內(nèi)容摘自《瘋狂Spring Boot終極講義》,閱讀此書,寫出自己的學(xué)習(xí)開發(fā)自己的自動配置和Starter。此書正在京東、當(dāng)當(dāng)限時5折促銷,歡迎掃碼下單:

            



          小哈又來送書辣,這次是送5本《瘋狂Spring Boot終極講義》,直接在小程序抽獎助手上抽,小伙伴們在公眾號回復(fù)關(guān)鍵字【抽獎】即可獲取參與二維碼,明晚八點(diǎn)開獎,包郵到家哦,感謝大家這么長時間以來對公眾號的支持,感激感激哦~

          瀏覽 12
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  清清草在线视频 | 婷婷色色五月天 | 亚洲天堂男人天堂 | 日本精品在线 | 黑人操亚洲人 |