Spring Boot實戰(zhàn)之緩存redis

spring boot對常用的數(shù)據(jù)庫支持外拍谐,對nosql 數(shù)據(jù)庫也進行了封裝自動化。

redis介紹

Redis是目前業(yè)界使用最廣泛的內(nèi)存數(shù)據(jù)存儲辆飘。相比memcached,Redis支持更豐富的數(shù)據(jù)結構赞厕,例如hashes, lists, sets等,同時支持數(shù)據(jù)持久化定硝。除此之外皿桑,Redis還提供一些類數(shù)據(jù)庫的特性,比如事務喷斋,HA唁毒,主從庫⌒亲Γ可以說Redis兼具了緩存系統(tǒng)和數(shù)據(jù)庫的一些特性浆西,因此有著豐富的應用場景。本文介紹Redis在Spring Boot中兩個典型的應用場景顽腾。

如何使用

1近零、引入 spring-boot-starter-redis

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-redis</artifactId>
</dependency>

2、添加配置文件

# REDIS (RedisProperties)
# Redis數(shù)據(jù)庫索引(默認為0)
spring.redis.database=0
# Redis服務器地址
spring.redis.host=192.168.0.58
# Redis服務器連接端口
spring.redis.port=6379
# Redis服務器連接密碼(默認為空)
spring.redis.password=
# 連接池最大連接數(shù)(使用負值表示沒有限制)
spring.redis.pool.max-active=8
# 連接池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.pool.max-wait=-1
# 連接池中的最大空閑連接
spring.redis.pool.max-idle=8
# 連接池中的最小空閑連接
spring.redis.pool.min-idle=0
# 連接超時時間(毫秒)
spring.redis.timeout=0

3抄肖、添加cache的配置類

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport{

    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }

    @SuppressWarnings("rawtypes")
    @Bean
    public CacheManager cacheManager(RedisTemplate redisTemplate) {
        RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
        //設置緩存過期時間
//        rcm.setDefaultExpiration(10);//秒
        return rcm;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate(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);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

好了久信,接下來就可以直接使用了

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
public class TestRedis {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void test() throws Exception {
        stringRedisTemplate.opsForValue().set("aaa", "111");
        Assert.assertEquals("111", stringRedisTemplate.opsForValue().get("aaa"));
    }

    @Test
    public void testObj() throws Exception {
        User user=new User("aa@126.com", "aa", "aa123456", "aa","123");
        ValueOperations<String, User> operations=redisTemplate.opsForValue();
        operations.set("com.neox", user);
        operations.set("com.neo.f", user,1,TimeUnit.SECONDS);
        Thread.sleep(1000);
        //redisTemplate.delete("com.neo.f");
        boolean exists=redisTemplate.hasKey("com.neo.f");
        if(exists){
            System.out.println("exists is true");
        }else{
            System.out.println("exists is false");
        }
       // Assert.assertEquals("aa", operations.get("com.neo.f").getUserName());
    }
}

以上都是手動使用的方式,如何在查找數(shù)據(jù)庫的時候自動使用緩存呢漓摩,看下面裙士;

4、創(chuàng)建個JavaBean對象管毙。


@Entity  
public class Post implements Serializable{  
    @Id  
    @GeneratedValue(strategy = GenerationType.AUTO)  
    private Integer id;  
  
    private String content;  
  
    private Integer weight;  
  
    public Integer getId() {  
        return id;  
    }  
  
    public void setId(Integer id) {  
        this.id = id;  
    }  
  
    public String getContent() {  
        return content;  
    }  
  
    public void setContent(String content) {  
        this.content = content;  
    }  
  
    public Integer getWeight() {  
        return weight;  
    }  
  
    public void setWeight(Integer weight) {  
        this.weight = weight;  
    }  
}  
5腿椎、創(chuàng)建個repository
@CacheConfig(cacheNames = "post")  
public interface PostRepository extends PagingAndSortingRepository<Post, Integer> {  
    @Cacheable(key = "#p0")  
    Post findById(int id);  
  
    /** 
     * 新增或修改時 
     */  
    @CachePut(key = "#p0.id")  
    @Override  
    Post save(Post post);  
  
    @Transactional  
    @Modifying  
    @CacheEvict(key = "#p0")
    int deleteById(int id);  
} 

這個里面有個CacheConfig,配置了cacheNames夭咬。

  • 我在findById方法時加了個@Cacheable(key= "#p0")啃炸,#p0代表第一個參數(shù),也就是id卓舵。這句話加上之后南用,當你在調用findById時,就會先從redis的post緩存對象里去查詢key等于傳過來的id的值。如果沒有裹虫,就去查表肿嘲。
  • 在save方法上加了個CachePut,代表往緩存里添加值恒界,key為參數(shù)post的id屬性睦刃,這樣當我們save一個Post對象時,redis就會新增一個以id為key的Post對象十酣;如果是update操作,那同樣际长,redis會覆蓋id相同的Post對象的值耸采,也完成一次更新。
  • 在delete方法上加了個@CacheEvict(key = "#p0")工育,當調用deleteById時虾宇,就會同時清空指定key的緩存。
  • 更多標簽如绸,請看cache標簽用法

這樣嘱朽,在對post的增、刪怔接、改搪泳、查時都會自動緩存到redis里。下面來驗證一下扼脐。

加個service

@Service  
public class PostService {  
    @Autowired  
    private PostRepository postRepository;  
  
    public Post findById(int id) {  
        return postRepository.findById(id);  
    }  
  
    public Post save(Post post) {  
        return postRepository.save(post);  
    }  
  
    public int delete(int id) {  
        return postRepository.deleteById(id);  
    }  
}  

來個controller

@RestController  
public class PostController {  
    @Autowired  
    private PostService postService;  
  
    @RequestMapping("/query/{id}")  
    public Object query(@PathVariable int id) {  
        return postService.findById(id);  
    }  
  
    @RequestMapping("/save")  
    public Object save(@ModelAttribute Post post) {  
        return postService.save(post);  
    }  
  
    @RequestMapping("/delete/{id}")  
    public Object delete(@PathVariable int id) {  
        return postService.delete(id);  
    }  
  
    @RequestMapping("/queryPage")  
    public Object query(String name, int pageNum, int count) {  
        //根據(jù)weight倒序分頁查詢  
//        Pageable pageable = new PageRequest(pageNum, count, Sort.Direction.DESC, "weight");  
//        return userRepository.findByName(name, pageable);  
        return null;  
    }  
}  

然后啟動Application岸军,在啟動之前需要加上@EnableCaching注解,緩存才能正常工作瓦侮。

@SpringBootApplication  
@EnableCaching  
public class Demo0Application {  
  
    public static void main(String[] args) {  
        SpringApplication.run(Demo0Application.class, args);  
    }  
}  

啟動后訪問 http://localhost:8080/save?content=1&weight=1


如果想看是否能夠正常緩存艰赞,有三種方法:

  • 在加緩存控制的地方打系統(tǒng)日志,比如
     /** 
     * 新增或修改時 
     */  
    @CachePut(key = "#p0.id")  
    @Override  
    Post save(Post post){
       system.out.println("命中緩存時將不會打印此日志");
    }

} 
  • 在applcation.properties中設置sql控制臺日志打開
show-sql: true
  • 登陸redis服務器查看響應的key值是否有增肚吏、刪方妖、改的變化。
# 獲取所有keys
keys *  
# 查看目標key是否有效
get <key>
# 查詢操作時罚攀,使用的是get方法党觅;新增和更新時,用的set方法坞生;刪除時仔役,用的del方法,可以分別查看響應操作時是己,對應的key是否發(fā)生了正確的變化

然后訪問查詢又兵,http://localhost:8080/query/1
會發(fā)現(xiàn)查詢到了id為1的這條記錄,并且控制臺沒有走select查詢語句,也就是根本沒訪問數(shù)據(jù)庫沛厨,直接從redis緩存拿的值宙地。

下面做一個更新操作,看看是否會同步到redis里逆皮。http://localhost:8080/save?content=1&weight=2&id=1
把weight改為2宅粥,訪問地址看看結果。
控制臺打印了兩條語句

hibernate: select post0_.id as id1_0_0_, post0_.content as content2_0_0_, post0_.weight as weight3_0_0_ from post post0_ where post0_.id=?Hibernate: update post set content=?, weight=? where id=?

說明數(shù)據(jù)已經(jīng)被更新了电谣。然后再查詢http://localhost:8080/query/1
發(fā)現(xiàn)查到的數(shù)據(jù)已經(jīng)改變秽梅,并且控制臺沒有走select語句,說明在update時剿牺,redis已經(jīng)更新了企垦。

下面做刪除操作,可以直接在數(shù)據(jù)庫里刪這條記錄晒来,或者通過瀏覽器訪問來刪除钞诡。http://localhost:8080/delete/1
控制臺走了刪除delete語句,redis也一并刪除了湃崩,這時執(zhí)行查詢將會看不到響應的數(shù)據(jù)荧降。

這樣我們就完成了一個最簡單的整合redis的demo。包括了對單個對象的增刪改查的緩存攒读。

那么下面來講幾個疑問:

  • 為什么不用配置redis的地址朵诫,port,密碼什么的整陌?
    上面的那些默認的對redis的操作拗窃,來源于Springboot里整合的RedisTemplate,template里會默認使用一個JedisConnectionFactory來做默認的連接屬性配置泌辫。



    這里面已經(jīng)對jedis的連接地址和jedisPool做了初始化操作了随夸,都是默認值。系統(tǒng)就會使用這些默認值來操作redis震放。后面我們會對Connection進行自定義宾毒,設置value的序列化方式,還有修改連接地址殿遂,那時就會使用自定義的配置了诈铛。

  • 能否用上面的方法來存儲集合?譬如所有的Post集合墨礁,當新增時集合也隨之改變幢竹?
    不行的,假如給List<Post> findAll做了個緩存恩静,那下次查詢時確實不用查表了焕毫,但是當你新增蹲坷、修改、刪除任何一個對象時邑飒,這個緩存的集合都是不變的循签。
    除非你在所有的能修改對象的地方,都加上CacheEvict疙咸,key為集合的key县匠,這樣任何修改,都是刪除整個集合對象的緩存撒轮,下次再查時才能緩存起來乞旦。而大部分時候,集合對象都是在不停變化的腔召,除了一些不變的如城市列表之類的杆查,其他的都不適合用這種緩存方式。會導致頻繁創(chuàng)建大對象臀蛛,而且大部分時候也不需要查整個集合,而是分頁崖蜜。

  • 怎么用redis來做集合查詢浊仆,分頁查詢,甚至于條件分頁查詢

參考文檔

http://blog.csdn.net/tianyaleixiaowu/article/details/70314277
https://yq.aliyun.com/articles/91595?t=t1
http://blog.csdn.net/tianyaleixiaowu/article/details/70595073

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末豫领,一起剝皮案震驚了整個濱河市抡柿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌等恐,老刑警劉巖洲劣,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異课蔬,居然都是意外死亡囱稽,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進店門二跋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來战惊,“玉大人,你說我怎么就攤上這事扎即⊥袒瘢” “怎么了?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵谚鄙,是天一觀的道長各拷。 經(jīng)常有香客問我,道長闷营,這世上最難降的妖魔是什么烤黍? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上蚊荣,老公的妹妹穿的比我還像新娘初狰。我一直安慰自己,他們只是感情好互例,可當我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布奢入。 她就那樣靜靜地躺著,像睡著了一般媳叨。 火紅的嫁衣襯著肌膚如雪腥光。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天糊秆,我揣著相機與錄音武福,去河邊找鬼。 笑死痘番,一個胖子當著我的面吹牛捉片,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播汞舱,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼伍纫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了昂芜?” 一聲冷哼從身側響起莹规,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎泌神,沒想到半個月后良漱,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡欢际,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年母市,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片幼苛。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡窒篱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出舶沿,到底是詐尸還是另有隱情墙杯,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布括荡,位于F島的核電站高镐,受9級特大地震影響,放射性物質發(fā)生泄漏畸冲。R本人自食惡果不足惜嫉髓,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一观腊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧算行,春花似錦梧油、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至量淌,卻和暖如春骗村,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背呀枢。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工胚股, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人裙秋。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓琅拌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親摘刑。 傳聞我的和親對象是個殘疾皇子财忽,可洞房花燭夜當晚...
    茶點故事閱讀 44,652評論 2 354

推薦閱讀更多精彩內(nèi)容