SpringBoot Redis入门(二)——数据库查询结果缓存

  • 学习SpringBoot提供的Cacheable注解的使用;

我们使用SpringBoot自带的Lettuce缓存框架,通过上一节已经完成了对Lettuce的集成,测试只是做了手动写代码将我们的对象写入到redis中了,也读了出来,缓存实际也就是干存取的工作。你项目中要这么使用也没毛病。
但牛逼的程序猿就是要把懒发挥到极致,Cacheable就是这么产生的。

Cacheable.java源码:


public @interface Cacheable {
	//缓存的名称
    @AliasFor("cacheNames")
    String[] value() default {};
    @AliasFor("value")
    String[] cacheNames() default {};

	// key值,可以从参数中获取
	//key属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。当我们没有指定该属性时,Spring将使用默认策略生成key
    String key() default "";

    String keyGenerator() default "";

    String cacheManager() default "";

    String cacheResolver() default "";

    String condition() default "";

    String unless() default "";

    boolean sync() default false;
}

通过在方法上添加 @Cacheable 注解,框架可以自动帮我们把查询结果缓存起来,我们也无需特地去使用缓存api去取。存和读操作透明了,

  • value:属性是必须指定的,其表示当前方法的返回值是会被缓存在哪个Cache上的,对应Cache的名称。其可以是一个Cache也可以是多个Cache,当需要指定多个Cache时其是一个数组
  • key:属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。当我们没有指定该属性时,Spring将使用默认策略生成key
  • condition:属性指定发生的条件。有的时候我们可能并不希望缓存一个方法所有的返回结果。通过condition属性可以实现这一功能。condition属性默认为空,表示将缓存所有的调用情形。其值是通过SpringEL表达式来指定的,当为true时表示进行缓存处理;当为false时表示不进行缓存处理,即每次调用该方法时该方法都会执行一次。
  • sync:缓存的同步 。在多线程环境下,某些操作可能使用相同参数同步调用。默认情况下,缓存不锁定任何资源,可能导致多次计算,而违反了缓存的目的。对于这些特定的情况,属性 sync 可以指示底层将缓存锁住,使只有一个线程可以进入计算,而其他线程堵塞,直到返回结果更新到缓存中。

key:内置了root对象,可获取方法调用相关参数作为 key值:

名称描述示例
method当前方法#root.method.name
target当前被调用的对象#root.target
targetClass当前被调用的对象的class#root.targetClass
args当前方法参数组成的数组#root.args[0]
caches当前被调用的方法使用的Cache#root.caches[0].name
methodName当前方法名#root.methodName

在这里插入图片描述

不使用Cacheable时缓存的用法是这样的:


	 /**
     * 这是手动存取缓存,存和取缓存占了很大代码量,而且入侵了我们的代码,非常不友好。
     * @param username
     * @return
     */
	public Object getUserInfo(String username) {
        if (redisTemplate.opsForValue().get(username) == null) {
            System.out.println("未获取到缓存,新建用户信息.............");
            Map<String, Object> user = new HashMap<>();
            user.put("username", username);
            user.put("usercode", "zhangsan");
            user.put("sex", "男");
            user.put("createtime", new Date());
            redisTemplate.opsForValue().set(username, user);
        }
        return redisTemplate.opsForValue().get(username);
    }

key的用法


	/**
     * 使用注解时,只有一行代码,业务代码也没有被入侵,简直不要太爽。
     * @param username
     * @return
     */
    @Cacheable(value = "USER_INFO", key = "#username")
    public Object getUserInfo3(String username) {
        System.out.println("未获取到缓存,从数据库获取.............");
        Map<String, Object> user = new HashMap<>();
        user.put("username", username);
        user.put("usercode", "zhangsan");
        user.put("sex", "男");
        user.put("createtime", DateUtils.format(new Date()));
        return user;
    }
    /**
     * 参数为自定义对像时,获取对象值为key
     *
     * @param user
     * @return
     */
//    @Cacheable(value = "USER_INFO", key = "#user.userId")
//    @Cacheable(value = "USER_INFO", key = "#p0.userId")
    @Cacheable(value = "USER_INFO", key = "#p0.userId.concat('-').concat(#p0.username)")
    public Object getUserInfo3(User user) {
        System.out.println("未获取到缓存,从数据库获取.............");
        Map<String, Object> userMap = new HashMap<>();
        userMap.put("userId", user.getUserId());
        userMap.put("username", "张三");
        userMap.put("usercode", "zhangsan");
        userMap.put("sex", "男");
        userMap.put("createtime", DateUtils.format(new Date()));
	} 

Redis服务端存储结构:
在这里插入图片描述

condition用法:

    /**
     * condition 的用法
     *
     * @param userId
     * @return
     */
    @Cacheable(value = {"USER_INFO"}, key = "#userId", condition = "#userId%2==0")
    public Object conditionTest(Long userId) {
        System.out.println("未获取到缓存,从数据库获取.............");
        Map<String, Object> userMap = new HashMap<>();
        userMap.put("userId", userId);
        userMap.put("username", "张三" + userId);
        userMap.put("usercode", "zhangsan" + userId);
        userMap.put("sex", "男");
        userMap.put("createtime", DateUtils.format(new Date()));
        return userMap;
    }
    @Test
    public void conditionTest() {
        System.out.println(userService.conditionTest(22L));
        System.out.println(userService.conditionTest(22L));
        System.out.println(userService.conditionTest(23L));
        System.out.println(userService.conditionTest(23L));
    }

输出结果:
id=22时,第二次是从缓存中获取结果;
id=23时,每次都是新建对象。
在这里插入图片描述

sync用法

    /**
     * sync 的用法
     *
     * @param userId
     * @return
     */
    @Cacheable(value = {"USER_INFO"}, key = "#userId", sync = true)
    public Object syncTest(Long userId)  {
        System.out.println("未获取到缓存,从数据库获取.............");
        Map<String, Object> userMap = new HashMap<>();
        userMap.put("userId", userId);
        userMap.put("username", "张三" + userId);
        userMap.put("usercode", "zhangsan" + userId);
        userMap.put("sex", "男");
        userMap.put("createtime", DateUtils.format(new Date()));
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000L);
                System.out.println("处理中...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return userMap;
    }
       @Test
    public void testSync() {
        new Thread(()->{
            System.out.println(userService.syncTest(15L));
        }).start();
        new Thread(()->{
            System.out.println(userService.syncTest(15L));
        }).start();
        try {
            Thread.sleep(10000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

输出结果:
1、但多线程情况下,查询参数相同的情况下,由于设置了sync=true,所以出现以线程等待查询结果返回之后,才从缓存中获取值。如此便节省了服务器计算资源。
在这里插入图片描述

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wolf犭良

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值