1,什么是缓存
数据库发生改变,Redis还没及时更新,那么从缓存内取到的数据就会出错,就是数据一致性问题
2,添加查询商品详情缓存
我们通过这个接口查询到的数据有很多,我们希望在此做个Redis缓存数据,提供查询速度
2.1 pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.8.0</version>
</dependency>
<!--Redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.1</version>
</dependency>
<!--逆向工程需要模板引擎-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<!--<version>3.0.5</version>-->
<version>3.5.1</version>
</dependency>
<!-- 代码自动生成器依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
<!-- mysql连接 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.21</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2.2 配置文件
server:
port: 18083
spring:
application:
name: springboot_redis_cache
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/java_pro?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&serverTimezone=UTC
username: root
password: Root-123
redis:
host: 127.0.0.1
port: 6379
database: 0
lettuce:
pool:
max-active: 8 #最大连接数
max-idle: 8 #最大空闲连接
min-idle: 0 #最小空闲连接
#连接等待时间
max-wait: 100
mybatis-plus:
type-aliases-package: com.study.pojo
mapper-locations: classpath:mapper/*Mapper.xml
mybatis:
type-aliases-package: com.study.pojo
mapper-locations: classpath:mapper/*Mapper.xml
2.3 启动类
package com.study;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.study.mapper")
public class RedisCacheApplication {
public static void main(String[] args) {
SpringApplication.run(RedisCacheApplication.class, args);
}
}
2.4 完成公共类
- ResponseResult
package com.study.common;
import lombok.Data;
import java.io.Serializable;
@Data
public class ResponseResult<T> implements Serializable {
private Boolean success;//是否成功
private Integer code;//状态码
private String message;//返回消息
private T data;
public ResponseResult() {
}
public static <T> ResponseResult<T> ok(){
ResponseResult<T> responseResult = new ResponseResult<T>();
responseResult.setSuccess(true);
responseResult.setCode(ResultCode.SUCCESS);
responseResult.setMessage("执行成功");
return responseResult;
}
public static <T> ResponseResult<T> ok(T data){
ResponseResult<T> responseResult = new ResponseResult<T>();
responseResult.setSuccess(true);
responseResult.setCode(ResultCode.SUCCESS);
responseResult.setMessage("执行成功");
responseResult.setData(data);
return responseResult;
}
public static <T> ResponseResult<T> error(Integer code){
ResponseResult<T> responseResult = new ResponseResult<T>();
responseResult.setSuccess(false);
responseResult.setCode(code);
responseResult.setMessage("执行失败");
return responseResult;
}
public static <T> ResponseResult<T> error(){
ResponseResult<T> responseResult = new ResponseResult<T>();
responseResult.setSuccess(false);
responseResult.setCode(ResultCode.ERROR);
responseResult.setMessage("执行失败");
return responseResult;
}
public ResponseResult<T> success(Boolean success){
this.setSuccess(success);
return this;
}
public ResponseResult<T> code (Integer code){
this.setCode(code);
return this;
}
public ResponseResult<T> message (String message){
this.setMessage(message);
return this;
}
public static<T> ResponseResult<T> exist (String message){
ResponseResult<T> responseResult = new ResponseResult<T>();
responseResult.setSuccess(true);
responseResult.setCode(ResultCode.SUCCESS);
responseResult.setMessage(message);
responseResult.setSuccess(true);
return responseResult;
}
public static<T> ResponseResult<T> exist (){
ResponseResult<T> responseResult = new ResponseResult<T>();
responseResult.setSuccess(true);
responseResult.setCode(ResultCode.SUCCESS);
responseResult.setSuccess(true);
return responseResult;
}
}
- ResultCode
package com.study.common;
public class ResultCode {
public static final Integer SUCCESS = 200;
public static final Integer ERROR = 500;
public static final int NOT_LOGIN = 600;
public static final int NOT_AUTH = 700;
}
2.5 完成常量类
package com.study.constants;
public class RedisConstants {
public static final String LOGIN_CODE_KEY = "login:code:";
public static final Long LOGIN_CODE_TTL = 10L;
public static final String LOGIN_USER_KEY = "login:token:";
public static final Long LOGIN_USER_TTL = 36000L;
public static final Long CACHE_NULL_TTL = 2L;
public static final Long CACHE_SHOP_TTL = 30L;
public static final String CACHE_SHOP_KEY = "cache:shop:";
public static final String LOCK_SHOP_KEY = "lock:shop:";
public static final Long LOCK_SHOP_TTL = 10L;
public static final String SECKILL_STOCK_KEY = "seckill:stock:";
public static final String BLOG_LIKED_KEY = "blog:liked:";
public static final String FEED_KEY = "feed:";
public static final String SHOP_GEO_KEY = "shop:geo:";
public static final String USER_SIGN_KEY = "sign:";
public static final String CACHE_SHOPTYPE_KEY = "cache_shoptype_key";
public static final Long CACHE_SHOPTYPE_TTL = 36000L;
}
2.6 完成代码生成工具
package com.study.utils;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.querys.MySqlQuery;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.fill.Column;
import com.baomidou.mybatisplus.generator.keywords.MySqlKeyWordsHandler;
public class MysqlCodeGenerator {
private static final String URL = "jdbc:mysql://127.0.0.1:3306/java_pro?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&serverTimezone=UTC";
private static final String username = "root";
private static final String password = "Root-123";
private static final String author = "linailong";
public static void main(String[] args) {
packMybatisPlus("sys_product");
}
static void packMybatisPlus(String table) {
FastAutoGenerator.create(new DataSourceConfig.Builder(URL, username, password)
.dbQuery(new MySqlQuery())
.typeConvert(new MySqlTypeConvert())
.keyWordsHandler(new MySqlKeyWordsHandler()))
.globalConfig(builder -> {
// 设置作者
builder.author("".equals(author) ? System.getProperty("user.name") : author)
// 使用java8新的时间类型
.dateType(DateType.TIME_PACK)
// 指定日期格式化方式
.commentDate("yyyy-MM-dd hh:mm:ss")
.outputDir("E:\\a_linailong\\myProject\\redis\\Demo03_Redis_Cache\\src\\main\\java");
})
.packageConfig(builder -> {
// TODO 设置父包名
builder.parent("com.study")
// 设置父包模块名
// .moduleName("system")
.entity("pojo")
.service("service")
.serviceImpl("service.impl")
.mapper("mapper")
.xml("mapper.xml")
.controller("controller");
})
.strategyConfig(builder -> {
builder
// 设置需要生成的表名 不设置表名则生成全部表
.addInclude(table)
// 开启跳过视图
.enableSkipView();
// 设置实体类策略
builder.entityBuilder()
// 开启链式模型
.enableChainModel()
// 开启lombok
// .enableLombok()
// 移除boolean的is前缀
.enableRemoveIsPrefix()
// 从数据库中生成字段注解
.enableTableFieldAnnotation()
// 命名规则下划线转驼峰
.naming(NamingStrategy.underline_to_camel)
// 数据库表字段映射到实体的命名策略
.columnNaming(NamingStrategy.underline_to_camel)
// 设置表字段自动填充
.addTableFills(new Column("create_time", FieldFill.INSERT))
.addTableFills(new Column("update_time", FieldFill.INSERT_UPDATE));
// mapper 生成策略
builder.mapperBuilder()
// 开启@Mapper注解
.enableMapperAnnotation();
// service 生成策略
builder.serviceBuilder()
.formatServiceFileName("%sService")
.formatServiceImplFileName("%sServiceImpl");
// controller 生成策略
builder.controllerBuilder()
// 开启驼峰连字符
.enableHyphenStyle()
// 开始@RestController
.enableRestStyle();
})
// 使用Freemarker引擎模板,默认的是Velocity引擎模板
.templateEngine(new FreemarkerTemplateEngine())
.execute();
}
}
2.7 完成实体类
package com.study.pojo;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
/**
* <p>
* 商品表
* </p>
*
* @author linailong
* @since 2023-02-07 01:59:33
*/
@TableName("sys_product")
@Data
public class SysProduct implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId("ID")
private String id;
@TableField("`NAME`")
private String name;
@TableField("INTRODUCE")
private String introduce;
@Override
public String toString() {
return "SysProduct{" +
"id=" + id +
", name=" + name +
", introduce=" + introduce +
"}";
}
}
2.8 完成mapper
package com.study.mapper;
import com.study.pojo.SysProduct;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* 商品表 Mapper 接口
* </p>
*
* @author linailong
* @since 2023-02-07 01:59:33
*/
@Mapper
public interface SysProductMapper extends BaseMapper<SysProduct> {
}
2.9 完成service以及实现
package com.study.service;
import com.study.common.ResponseResult;
import com.study.pojo.SysProduct;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 商品表 服务类
* </p>
*
* @author linailong
* @since 2023-02-07 01:59:33
*/
public interface SysProductService extends IService<SysProduct> {
/**
* 根据id查询商品
* @param id
* @return
*/
ResponseResult getProById(String id);
}
package com.study.service.impl;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.study.common.ResponseResult;
import com.study.constants.RedisConstants;
import com.study.pojo.SysProduct;
import com.study.mapper.SysProductMapper;
import com.study.service.SysProductService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.management.Query;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* <p>
* 商品表 服务实现类
* </p>
*
* @author linailong
* @since 2023-02-07 01:59:33
*/
@Slf4j
@Service
public class SysProductServiceImpl extends ServiceImpl<SysProductMapper, SysProduct> implements SysProductService {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private SysProductMapper sysProductMapper;
/**
* 根据id查询商品
* @param id
* @return
*/
@Override
public ResponseResult getProById(String id) {
//1.从Redis内查询商品缓存
String shopJson = stringRedisTemplate.opsForValue().get(RedisConstants.CACHE_SHOP_KEY + id);
if(StrUtil.isNotBlank(shopJson)){
//手动反序列化
SysProduct pro = JSONUtil.toBean(shopJson, SysProduct.class);
return ResponseResult.ok(pro);
}
//2.不存在就根据id查询数据库
SysProduct sysProduct = sysProductMapper.selectById(id);
if(sysProduct==null){
return ResponseResult.error().message("商品不存在!");
}
//3.数据库数据写入Redis
//手动序列化
String shopStr = JSONUtil.toJsonStr(sysProduct);
stringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY + id,shopStr,RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);
return ResponseResult.ok(shopStr);
}
}
2.10 完成controller
package com.study.controller;
import com.study.common.ResponseResult;
import com.study.pojo.SysProduct;
import com.study.service.SysProductService;
import com.study.service.impl.SysProductServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* <p>
* 商品表 前端控制器
* </p>
*
* @author linailong
* @since 2023-02-07 01:59:33
*/
@RestController
@ResponseBody
@RequestMapping("/sysProduct")
public class SysProductController {
@Resource
public SysProductService sysProductService;
/**
* 根据id查询商品
*/
@GetMapping("/getProById")
private ResponseResult getProById(@RequestParam("id") String id){
return sysProductService.getProById(id);
}
}
2.11 测试
http://localhost:18083/sysProduct/getProById?id=1
3,添加批量查询商品缓存
3.1 完成service以及实现
/**
* 查询所以商品
* @return
*/
ResponseResult getProList();
/**
* 查询所以商品
* @return
*/
@Override
public ResponseResult getProList() {
//1.从Redis中查询
String key = RedisConstants.CACHE_SHOPTYPE_KEY;
List<String> list = stringRedisTemplate.opsForList().range(key, 0, -1);
if(!list.isEmpty()){
//手动反序列化
List<SysProduct> productList = new ArrayList<>();
for (String s : list) {
SysProduct product = JSONUtil.toBean(s, SysProduct.class);
productList.add(product);
}
return ResponseResult.ok(productList);
}
//2.从数据库内查询
QueryWrapper<SysProduct> wrapper = new QueryWrapper<>();
List<SysProduct> proList = sysProductMapper.selectList(wrapper);
if(proList.isEmpty()){
return ResponseResult.error().message("没有商品!");
}
//序列化
for (SysProduct shopType : proList) {
String s = JSONUtil.toJsonStr(shopType);
list.add(s);
}
//3.存入缓存
stringRedisTemplate.opsForList().rightPushAll(key,list);
stringRedisTemplate.expire(key,RedisConstants.CACHE_SHOPTYPE_TTL, TimeUnit.MINUTES);
return ResponseResult.ok(list);
}
3.2 完成controller
/**
* 查询所以商品
* @return
*/
@GetMapping("/getProList")
private ResponseResult getProList(){
return sysProductService.getProList();
}