业务需求:当前端发送 shop/id 的请求时,我们需要向前端响应对应 id 的详细数据给前端
直接查询 mysql 效率比较低,我们可以使用 redis 作为中间件进行数据的缓存,先查询 redis ,若redis 中未查询到,则在 mysql 中查询,并在查询后写入 redis 中
第一步,Controller
@RestController
@RequestMapping("/shop")
public class ShopController {
@Resource
public ShopService shopService;
//接收携带店铺id的请求,响应店铺信息
@GetMapping("/{id}")
public Result queryShopById(@PathVariable("id") Long id){
return shopService.getById(id);
}
}
实现店铺信息的封装 Pojo 类
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tb_shop")
public class Shop implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 商铺名称
*/
private String name;
/**
* 商铺类型的id
*/
private Long typeId;
/**
* 商铺图片,多个图片以','隔开
*/
private String images;
/**
* 商圈,例如陆家嘴
*/
private String area;
/**
* 地址
*/
private String address;
/**
* 经度
*/
private Double x;
/**
* 维度
*/
private Double y;
/**
* 均价,取整数
*/
private Long avgPrice;
/**
* 销量
*/
private Integer sold;
/**
* 评论数量
*/
private Integer comments;
/**
* 评分,1~5分,乘10保存,避免小数
*/
private Integer score;
/**
* 营业时间,例如 10:00-22:00
*/
private String openHours;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
@TableField(exist = false)
private Double distance;
}
第二步,Service 及其实现类
public interface ShopService {
Result getById(Long id);
}
@Service
public class ShopServiceImpl implements ShopService {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private ShopMapper shopMapper;
@Override
public Result getById(Long id) {
//先在 redis 中查询
String shopJson = stringRedisTemplate.opsForValue().get("cache:shop:" + id);
if(StrUtil.isNotBlank(shopJson)){
return Result.ok(JSONUtil.toBean(shopJson,Shop.class));
}
//redis 中没有,在 mysql 中查询
Shop shop = shopMapper.selectById(id);
if(shop == null){
//mysql 中也没有,返回未查询到
return Result.fail("未查询到该店铺ToT");
}
//查到了,将查询到的数据写入 redis
stringRedisTemplate.opsForValue().set("cache:shop:" + id,JSONUtil.toJsonStr(shop));
return Result.ok(shop);
}
}
最后一步,将店铺查询的拦截放开,进行测试
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//添加更新 token 拦截器,默认先添加先拦截,也可以通过.order() 来设置拦截的先后顺序
registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate));
//添加判断是否登录的拦截器
registry.addInterceptor(new LoginInterceptor())
.excludePathPatterns("/user/code", "/user/login","/shop/**");
}
第一次访问时耗时两秒多,存入 redis 缓存后,第二次查询效率显著提升,从 2s 优化到了 12ms
类比店铺查询,我们将店铺类型查询加入缓存
店铺查询的请求为:shop-type/list ,请求成功后会向前端返回一个集合
Pojo:
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tb_shop_type")
public class ShopType implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 类型名称
*/
private String name;
/**
* 图标
*/
private String icon;
/**
* 顺序
*/
private Integer sort;
/**
* 创建时间
*/
@JsonIgnore
private LocalDateTime createTime;
/**
* 更新时间
*/
@JsonIgnore
private LocalDateTime updateTime;
}
controller:
@RestController
@RequestMapping("/shop-type")
public class ShopTypeController {
@Resource
private ShopTypeService shopTypeService;
@GetMapping("/list")
public Result getShopType(){
return shopTypeService.getList();
}
}
Mapper:
@Mapper
public interface ShopMapper extends BaseMapper<Shop> {
}
Service:
public interface ShopTypeService {
Result getList();
}
@Service
public class ShopTypeServiceImpl implements ShopTypeService {
@Autowired
private ShopTypeMapper shopTypeMapper;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public Result getList() {
//先从 redis 中查询
String listJson = stringRedisTemplate.opsForValue().get("shop-type:list:");
if(listJson != null){
return Result.ok(JSONUtil.toList(listJson, ShopType.class));
}
//redis 中没有,在 mysql 中查询
List<ShopType> shopTypes = shopTypeMapper.selectAll();
if(shopTypes == null)return Result.fail("查询出现错误");
stringRedisTemplate.opsForValue().set("shop-type:list:", JSONUtil.toJsonStr(shopTypes));
return Result.ok(shopTypes);
}
}
拦截器放行:
//使用注解的方式代替配置文件,指定拦截器
@Configuration
public class MvcConfig implements WebMvcConfigurer {
//该配置类被 Spring 容器管理,可以从这里拿到容器中的 stringRedisTemplate 传递给过滤器
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//添加更新 token 拦截器,默认先添加先拦截,也可以通过.order() 来设置拦截的先后顺序
registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate));
//添加判断是否登录的拦截器
registry.addInterceptor(new LoginInterceptor())
.excludePathPatterns("/user/code", "/user/login","/shop/**","/shop-type/list");
}
}
测试: