前言:

经过前面linux,redis,git的学习介绍,我们至此进入项目优化阶段

1. 项目问题  1-1

用户数量多,系统访问量大

频繁访问数据库,系统性能下降,用户体验差

缓存优化_验证码

2. 环境搭建  1-2

将我们的项目推送远程仓库   1-2

缓存优化_redis_02

在创建一个新的分支,在次分支上进行项目优化

缓存优化_spring cache_03

缓存优化_spring cache_04

将v1.0 分支也推送远程仓库

缓存优化_spring cache_05

缓存优化_缓存_06

2.1 maven 坐标   1-3

在项目的pom. xml文件中导入spring data redis 的maven坐标: 

pom.xml

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

在项目的application. yml中加入redis相关配置:

application.yml 

spring:
   redis:
    host: localhost
    port: 6379
    #password: 123456
    database: 0 #操作的是0号数据库

redis序列化器  RedisConfig  1-3

package com.itheima.reggie.config;

import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

//redis序列化器   1-3
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        //默认的Key序列化器为:JdkSerializationRedisSerializer
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setConnectionFactory(connectionFactory);
        return redisTemplate;
    }
}

接着添加,提交

缓存优化_验证码_07

缓存优化_spring cache_08

推送成功

缓存优化_redis_09

3. 缓存短信验证码   1-4

3.1 实现思路

前面我们已经实现了移动端手机验证码登录,随机生成的验证码我们是保存在HttpSession中的。现在需要改造为将验证码缓存在Redis中,具体的实现思路如下:

1、在服务端UserController中注入RedisTemplate对象,用于操作Redis

2、在服务端UserController的sendMsg方法中,将随机生成的验证码缓存到Redis中,并设置有效期为5分钟

3、在服务端UserController的login方法中,从Redis中获取缓存的验证码,如果登录成功则删除Redis中的验证码

3.2 代码改造  1-4

UserController

//注入redisTemplate对象    优化1-4
    @Autowired
    private RedisTemplate redisTemplate;

获取手机验证码  优化  sendMsg   优1-4

//获取手机验证码  优化1-4
    @PostMapping("/sendMsg")
    public R<String> sendMsg(@RequestBody User user,HttpSession session){

        //获取手机号
        String phone = user.getPhone();
        if(StringUtils.isNotEmpty(phone)){//手机号不为空
            //生成随机的4为验证码
            String code = ValidateCodeUtils.generateValidateCode(4).toString();
            log.info("code={}",code);

            //调用阿里云提供的短信服务AP完成发送短信
            //SMSUtils.sendMessage("瑞吉外卖","",phone,code);
            //需要将验证码保存到session
            //session.setAttribute(phone,code);

            //将生成的验证码缓存到Redis中,并且设置有效期为5分钟    优1-4
            redisTemplate.opsForValue().set(phone,code,5, TimeUnit.MINUTES);

            return R.success("手机验证码发送成功");
        }

        return R.error("短信发送失败");
    }

移动端用户登录 优化     login  优1-4

/**
     * 移动端用户登录 优化   优1-4
     * @param map
     * @param session
     * @return
     */
    @PostMapping("/login")
    public R<User> login(@RequestBody Map map, HttpSession session){
        log.info(map.toString());

        //获取手机号
        String phone = map.get("phone").toString();

        //获取验证码
        String code = map.get("code").toString();

        //从Session中获取保存的验证码
        //Object codeInSession = session.getAttribute(phone);

        //从redis中获取缓存的验证码  优1-4
        Object codeInSession = redisTemplate.opsForValue().get(phone);

        //进行验证码的比对(页面提交的验证码和Session中保存的验证码比对)
        if(codeInSession != null && codeInSession.equals(code)){
            //如果能够比对成功,说明登录成功

            //判断当前手机号对应的用户是否为新用户,如果是新用户就自动完成注册
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            //构造查询条件,将手机号作为查询条件
            queryWrapper.eq(User::getPhone,phone);

            User user = userService.getOne(queryWrapper);
            if(user == null){
                //是新用户就自动完成注册
                user = new User();
                user.setPhone(phone);
                user.setStatus(1);
                userService.save(user);
            }

            //将用户id放入session
            session.setAttribute("user",user.getId());

            //如果用户登录成功,删除Redis中缓存的验证码   优1-4
            redisTemplate.delete(phone);

            return R.success(user);
        }
        return R.error("登录失败");
    }

3.3 测试   1-4

运行程序

缓存优化_验证码_10

查看redis

缓存优化_验证码_11

登录成功

缓存优化_缓存_12

再次查看redis

缓存优化_redis_13

ps 还可以使用redis图形化界面

E:\java\tools\Redis\ziliao\redisclient-win32.x86_64.2.0.jar   :    Redis图形界面客户端

执行方式: 在这个文件所在的目录cmd,  执行 java -jar redisclient-win32.x86_64.2.0.jar

缓存优化_redis_14

启动成功后,点击左上角的Server---add 

缓存优化_缓存_15

缓存优化_缓存_16

发送验证码,redis里出现手机号

缓存优化_redis_17

登录成功后手机号被删除

缓存优化_spring cache_18

4. 缓存菜品数据   1-5

4.1 实现思路  1-5

前面我们已经实现了移动端菜品查看功能T对应的服务端方法为DishController的list方法,此方法会根据前端提交的查询条件进行数据库查询操作。在高并发的情况下,频繁查询数据库会导致系统性能下降,服务端响应时间增长。现在需要对此方法进行缓存优化,提高系统的性能。具体的实现思路如下:

1、改造DishController的list方法,先从Redis中获取菜品数据,如果有则直接返回,无需查询数据库;如果没有则查询数据库,并将查询到的菜品数据放入Redis。

2、改造DishController的save和update方法, 加入清理缓存的逻辑

缓存优化_缓存_19

4.2 代码实现   1-6

DishController

//注入redisTemplate对象    优化1-6
    @Autowired
    private RedisTemplate redisTemplate;

根据菜品分类的id查询菜品改造  优化  list

//根据菜品分类的id查询菜品改造  优化  优1-6
    @GetMapping("/list")
    public R<List<DishDto>> list(Dish dish){

        List<DishDto> dishDtoList = null;

        //动态构造key,使用分类的id和售卖状态可以保证唯一性
        //dish_1397844391040167938_1
        String key = "dish_" + dish.getCategoryId() + "_" + dish.getStatus();

        //先从redis中获取缓存数据
        dishDtoList = (List<DishDto>) redisTemplate.opsForValue().get(key);

        if(dishDtoList != null){
            //如果存在,直接返回,无需查询数据库
            return R.success(dishDtoList);
        }

        //构造查询条件对象
        LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(dish.getCategoryId()!=null,Dish::getCategoryId,dish.getCategoryId());
        //添加条件,查询状态为1的也就是起售的菜品
        queryWrapper.eq(Dish::getStatus,1);

        //添加排序条件
        queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);

        List<Dish> list = dishService.list(queryWrapper);

        //遍历list集合
        dishDtoList = list.stream().map((item)->{
            DishDto dishDto = new DishDto();
            //将普通数据赋值进dishDto
            BeanUtils.copyProperties(item,dishDto);

            //设置菜品分类名
            Long categoryId = item.getCategoryId();//分类id
            //根据id查询分类对象
            Category category = categoryService.getById(categoryId);

            if(category != null){
                String categoryName = category.getName();
                dishDto.setCategoryName(categoryName);
            }

            //设置菜品口味
            Long id = item.getId();
            LambdaQueryWrapper<DishFlavor> dishFlavorLambdaQueryWrapper = new LambdaQueryWrapper<>();
            dishFlavorLambdaQueryWrapper.eq(DishFlavor::getDishId,id);
            List<DishFlavor> dishFlavorList = dishFlavorService.list(dishFlavorLambdaQueryWrapper);

            dishDto.setFlavors(dishFlavorList);

            return dishDto;

        }).collect(Collectors.toList());

        //如果不存在,需要查询数据库,将查询到的菜品数据缓存到Redis
        //这里解释opsForValue不是村String类型的嘛,为啥可以存list
        //opsForValue()是操作String类型数据的方法,但是可以通过一些转
        //换操作,将List类型的元素转换为字符串进行存储,比如序列化操作或转换为JSON格式再存储。
        //在读取时,我们也需要将其转换为List类型,但是在读取时需要进行一些转换操作,
        // 并且这种方式不能很好地支持对List中的元素进行增删改查等操作。因此,在某些情况下,
        // 建议使用Redis中专门的List数据结构来存储List类型的数据,以获得更好的性能和支持更多的操作方式。
        redisTemplate.opsForValue().set(key,dishDtoList,60, TimeUnit.MINUTES);

        return R.success(dishDtoList);
    }

4.2.1 测试  1-6

运行登录,果然在redis中发现了菜品缓存

缓存优化_spring cache_20

4.3 更改save和update  1-7

修改菜品 优化  update   1-7

更新菜品我们采取,删除redis中所有以dish_开头的key方法,简单粗暴

//修改菜品 优化  优1-7
    @PutMapping
    public R<String> update(@RequestBody DishDto dishDto){
        log.info(dishDto.toString());

        //调用我们自己实现的方法因为DishDto中包含了dish_flavor表
        dishService.updateWithFlavor(dishDto);

        //清理所有的菜品的缓存数据
        Set keys = redisTemplate.keys("dish_*");
        redisTemplate.delete(keys);


        return R.success("新增菜品成功");
    }

测试更新菜品,发现更新前菜品缓存还在,更新某一个菜品后,所有的菜品缓存被删除

缓存优化_redis_21

缓存优化_spring cache_22

我们再采用精确处理的方法,指清理某个特定被更新的菜品缓存

//修改菜品 优化  优1-7
    @PutMapping
    public R<String> update(@RequestBody DishDto dishDto){
        log.info(dishDto.toString());

        //调用我们自己实现的方法因为DishDto中包含了dish_flavor表
        dishService.updateWithFlavor(dishDto);

        //清理所有的菜品的缓存数据   y1-7
//        Set keys = redisTemplate.keys("dish_*");
//        redisTemplate.delete(keys);

        //清理某个分类下面的菜品缓存数据   y1-7
        //思路就在于我们需要知道被更新菜品缓存的key是多少
        String key = "dish_" + dishDto.getCategoryId() + "_1";
        redisTemplate.delete(key);


        return R.success("新增菜品成功");
    }

再更新前缓存优4条,更新后剩下3条,说明我们只删除了被更新菜品的缓存

缓存优化_缓存_23

缓存优化_redis_24

新增菜品   save 优化  y1-7

/**
     * 新增菜品 优化  y1-7
     * @param dishDto
     * @return
     */
    @PostMapping
    public R save(@RequestBody DishDto dishDto){
        log.info(dishDto.toString());

        //调用我们自己在DishServiceImpl实现的方法   4-11
        //我们在DishServiceImpl业务层中自己实现数据保存(因为涉及两张
        // 表操作框架无法为我们提供合适的方法)
        dishService.saveWithFlavor(dishDto);

        //清理所有的菜品的缓存数据   y1-7
//        Set keys = redisTemplate.keys("dish_*");
//        redisTemplate.delete(keys);

        //清理某个分类下面的菜品缓存数据   y1-7
        //思路就在于我们需要知道被更新菜品缓存的key是多少
        String key = "dish_" + dishDto.getCategoryId() + "_1";
        redisTemplate.delete(key);

        return R.success("新增菜品成功");
    }

和更新菜品一样的套路,新增前3条,新增后2条

缓存优化_redis_25

缓存优化_spring cache_26

4.4 提交远程 并合并主分支 1-8

提交

缓存优化_spring cache_27

缓存优化_redis_28

合并

切回主分支

缓存优化_redis_29

点击右下角master

缓存优化_redis_30

查看代码合并成功,因为出现了我们注释的代码

缓存优化_redis_31

5. Spring Cache   1-9

5.1 spring cache介绍   1-9

Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一-个注解,就能实现缓存功能。

Spring Cache提供了一层抽象,底层可以切换不同的cache实现。具体就是通过CacheManager接口来统一不同的缓存技术。

CacheManager是Spring提供的各种缓存技术抽象接口。

针对不同的缓存技术需要实现不同的CacheManager: 

缓存优化_缓存_32

5.2 spring cahche常用注解   y1-9

缓存优化_redis_33

在spring boot项目中,使用缓存技术只需在项目中导入相关缓存技术的依赖包,并在启动类.上使用@EnableCaching开启缓存支持即可。

例如,使用Redis作为缓存技术,只需要导入Spring data Redis的maven坐标即可。

5.3 spring cache 使用方法    y1-10

项目在E:\java学习\瑞吉外卖\course2\cache_demo

新建数据库

缓存优化_spring cache_34

缓存优化_spring cache_35

缓存优化_缓存_36

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.itheima</groupId>
    <artifactId>cache_demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>

        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.23</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.4.5</version>
            </plugin>
        </plugins>
    </build>
</project>

applicationg.yml

server:
  port: 8080
spring:
  application:
    #应用的名称,可选
    name: cache_demo
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/cache_demo?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
      username: root
      password: lzl
mybatis-plus:
  configuration:
    #在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      id-type: ASSIGN_ID

实体类User

package com.itheima.entity;

import lombok.Data;
import java.io.Serializable;

@Data
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    private String name;

    private int age;

    private String address;

}

持久层接口UserMapper

package com.itheima.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.entity.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper<User>{
}

业务层接口  UserService

package com.itheima.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.entity.User;

public interface UserService extends IService<User> {
}

业务层接口实现类  UserServiceImpl

package com.itheima.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.entity.User;
import com.itheima.mapper.UserMapper;
import com.itheima.service.UserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {
}

启动类CacheDemoApplication

package com.itheima;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@Slf4j
@SpringBootApplication
@EnableCaching  //开启缓存注解的功能  y1-10
public class CacheDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(CacheDemoApplication.class,args);
        log.info("项目启动成功...");
    }
}

控制层 UserController

package com.itheima.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.itheima.entity.User;
import com.itheima.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;


//y1-10
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

    @Autowired
    private CacheManager cacheManager;

    @Autowired
    private UserService userService;

}

5.3.1  保存方法 CachePut:将方法返回值放入缓存   y1-10

/**
     * CachePut:将方法返回值放入缓存   y1-10
     * value:缓存的名称,每个缓存名称下面可以有多个key
     * key:缓存的key
     */
    @CachePut(value = "userCache",key = "#user.id")
    @PostMapping
    public User save(User user){
        userService.save(user);
        return user;
    }

使用postman进行测试

缓存优化_验证码_37

数据库插入数据成功

缓存优化_redis_38

通过debug查看缓存数据查看缓存成功

缓存优化_缓存_39

5.3.2 删除方法 CacheEvict:清理指定缓存   y1-11

/**
     * CacheEvict:清理指定缓存   y1-11
     * value:缓存的名称,每个缓存名称下面可以有多个key
     * key:缓存的key
     */
    @CacheEvict(value = "userCache",key = "#p0")
    //@CacheEvict(value = "userCache",key = "#root.args[0]")
    //@CacheEvict(value = "userCache",key = "#id")
    @DeleteMapping("/{id}")
    public void delete(@PathVariable Long id){
        userService.removeById(id);
    }

测试

现在缓存中存入a,b

缓存优化_spring cache_40

缓存优化_验证码_41

现在删除a的缓存,但是我们在操作之前先存入c的缓存,以便区分

缓存中只剩b了,成功

缓存优化_spring cache_42

5.3.3 更新方法 CacheEvict:清理指定缓存   y1-11

//更新方法  CacheEvict:清理指定缓存   y1-11
    //@CacheEvict(value = "userCache",key = "#p0.id")
    //@CacheEvict(value = "userCache",key = "#user.id")
    //@CacheEvict(value = "userCache",key = "#root.args[0].id")
    @CacheEvict(value = "userCache",key = "#result.id")
    @PutMapping
    public User update(User user){
        userService.updateById(user);
        return user;
    }

测试,我们往缓存中加入d,e,f

缓存优化_验证码_43

缓存优化_验证码_44

指定修改d

缓存优化_验证码_45

数据库更新成功

缓存优化_redis_46

查看缓存,再没更新之前时三条数据,执行之后剩下两条d缓存被删除

缓存优化_验证码_47

缓存优化_验证码_48

5.3.4 查询方法  Cacheable    y1-12

Cacheable:在方法执行前spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放到缓存中

/**
     * Cacheable:在方法执行前spring先查看缓存中是否有数据,如果有数据,则直接返回缓
     * 存数据;若没有数据,调用方法并将方法返回值放到缓存中
     * value:缓存的名称,每个缓存名称下面可以有多个key
     * key:缓存的key
     * condition:条件,满足条件时才缓存数据
     * unless:满足条件则不缓存
     */
    
    //这里解释condition = "#result != null",当返回值不为空时才缓存数据,这是为了防止,我们查不到数据
    //也被缓存产生垃圾数据的现象
    //unless = "#result == null"时当返回值为空时就不缓存,他和condition是正好相反
    //@Cacheable(value = "userCache",key = "#id",condition = "#result != null")
    @Cacheable(value = "userCache",key = "#id",unless = "#result == null")
    @GetMapping("/{id}")
    public User getById(@PathVariable Long id){
        User user = userService.getById(id);
        return user;
    }

测试

缓存优化_验证码_49

第一次查询了数据库,因为缓存中没有数据

缓存优化_验证码_50

第二次再次查询,不走数据库了,直接走了缓存,因为缓存中有了数据

缓存优化_验证码_51

和上述查询一样的原理,不做测试了

//条件查询,如果id不为空就根据id查,如果name不为空就根据name查,都不为空就一起查    y1-12
    @Cacheable(value = "userCache",key = "#user.id + '_' + #user.name")
    @GetMapping("/list")
    public List<User> list(User user){
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(user.getId() != null,User::getId,user.getId());
        queryWrapper.eq(user.getName() != null,User::getName,user.getName());
        List<User> list = userService.list(queryWrapper);
        return list;
    }

5.4 使用redis缓存  y1-13

缓存优化_redis_52

pom.xml

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

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

application.yml

缓存优化_验证码_53

server:
  port: 8080
spring:
  application:
    #应用的名称,可选
    name: cache_demo
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/cache_demo?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
      username: root
      password: lzl
  #加入redis   y1-13
  redis:
    host: localhost
    port: 6379
    #password: 123456
    #操作的是0号数据库
    database: 0
  cache:
    redis:
      time-to-live: 1800000 #设置缓存数据的过期时间
mybatis-plus:
  configuration:
    #在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      id-type: ASSIGN_ID

测试查询方法

@Cacheable(value = "userCache",key = "#id",unless = "#result == null")
    @GetMapping("/{id}")
    public User getById(@PathVariable Long id){
        User user = userService.getById(id);
        return user;
    }

测试缓存成功

缓存优化_redis_54

6. 缓存套餐数据 y1-14

6.1 实现思路

前面我们已经实现了移动端套餐查看功能,对应的服务端方法为SetmealController的list方法,此方法会根据前端提交的查询条件进行数据库查询操作。在高并发的情况下,频繁查询数据库会导致系统性能下降,服务端响应时间增长。现在需要对此方法进行缓存优化,提高系统的性能。

具体的实现思路如下:

1、导入Spring Cache和Redis相关maven坐标.

2、在application.yml中 配置缓存数据的过期时间

3、在启动类上加入@EnableCaching注解,开启缓存注解功能

4、在SetmealController的list方法上加入@Cacheable注解

5、在SetmealController的save和delete方法.上加入CacheEvict注解

6.2 代码实现    y1-14

导入Spring Cache和Redis相关maven坐标.

pom.xml

<!--redis依赖   y1-3-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!--spring cache依赖  y1-14-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

在application.yml中 配置缓存数据的过期时间    y1-14

缓存优化_redis_55

application.yml

cache:
    redis:
      time-to-live: 1800000 #设置缓存数据的过期时间

在启动类上加入@EnableCaching注解,开启缓存注解功能   y1-14

ReggieApplication

package com.itheima.reggie;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.transaction.annotation.EnableTransactionManagement;

//启动类  5
@Slf4j  //这个注解是lombok提供的 日志文件
@SpringBootApplication
@ServletComponentScan //目的是让我们定义的过滤器生效
@EnableTransactionManagement  //使我们的事务注解起作用  4-10
@EnableCaching //开启spring Cache
public class ReggieApplication {
    public static void main(String[] args) {
        SpringApplication.run(ReggieApplication.class,args);
        log.info("项目启动成功。。。。!!!***~####");
    }
}

在SetmealController的list方法上加入@Cacheable注解   y1-14

SetmealController

/**
     * 根据条件查询套餐数据优化  y1-14
     * @param setmeal
     * @return
     */
    @Cacheable(value = "setmealCache",key = "#setmeal.categoryId+'_'+#setmeal.status")
    @GetMapping("/list")
    public R<List<Setmeal>> list(Setmeal setmeal){
        LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(setmeal.getCategoryId() != null,Setmeal::getCategoryId,setmeal.getCategoryId());
        queryWrapper.eq(setmeal.getStatus() != null,Setmeal::getStatus,setmeal.getStatus());
        queryWrapper.orderByDesc(Setmeal::getUpdateTime);

        List<Setmeal> list = setmealService.list(queryWrapper);

        return R.success(list);
    }

点击套餐,测试报错,是因为我们的返回值是R>而R是我们自己定义的,没有序列化,需要将其继承序列化接口

缓存优化_缓存_56

缓存优化_验证码_57

改造R

缓存优化_redis_58

再次重启测试

正常展示

缓存优化_redis_59

查看缓存

缓存优化_缓存_60

在SetmealController的save和delete方法.上加入CacheEvict注解 y1-14

SetmealController

//新增套餐  优化  y1-14
    @CacheEvict(value = "setmealCache",allEntries = true)
    @PostMapping
    public R<String> save(@RequestBody SetmealDto setmealDto){
        log.info("套餐信息 {}",setmealDto);

        setmealService.saveWithDish(setmealDto);

        return R.success("新增套餐成功");
    }
 /**
     * 删除套餐  优化  y1-14
     * @param ids
     * @return
     */
    //allEntries = true 表示删除setmealCache底下的所有缓存数据
    @CacheEvict(value = "setmealCache",allEntries = true)
    @DeleteMapping
    public R<String> delete(@RequestParam List<Long> ids){
        log.info("ids:{}",ids);

        setmealService.removeWithDish(ids);

        return R.success("套餐数据删除成功");
    }

测试再新增之前套餐缓存还在,新增之后就没了

缓存优化_spring cache_61

缓存优化_spring cache_62

新增的套餐

缓存优化_spring cache_63

6.3 提交代码和合并  y1-15

提交

缓存优化_缓存_64

缓存优化_redis_65

合并  y1-15

切换回master,然后合并

缓存优化_缓存_66

随便找一个修改过的代码,合并成功

缓存优化_spring cache_67