From c7fc2f8d1fadca86e4ee10265876c3df2631dbf7 Mon Sep 17 00:00:00 2001 From: Nice-2-CU <81088875+Nice-2-CU@users.noreply.github.com> Date: Sun, 12 Feb 2023 08:45:23 +0800 Subject: [PATCH] =?UTF-8?q?add:=E6=95=B4=E5=90=88Redis=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 12 ++ .../exception/GlobalExceptionHandler.java | 4 +- .../common/util/redis/RedisCacheUtil.java | 115 ++++++++++++++++++ .../common/util/redis/RedisConstants.java | 18 +++ .../common/util/redis/RedisData.java | 16 +++ .../service/Impl/ItemTypeServiceImpl.java | 51 ++++++-- src/main/resources/application.yaml | 39 +++++- 7 files changed, 241 insertions(+), 14 deletions(-) create mode 100644 src/main/java/com/ctgu/lost_found/common/util/redis/RedisCacheUtil.java create mode 100644 src/main/java/com/ctgu/lost_found/common/util/redis/RedisConstants.java create mode 100644 src/main/java/com/ctgu/lost_found/common/util/redis/RedisData.java diff --git a/pom.xml b/pom.xml index 4075fb5..79c3dc6 100644 --- a/pom.xml +++ b/pom.xml @@ -143,6 +143,18 @@ hutool-all 5.7.17 + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + org.apache.commons + commons-pool2 + diff --git a/src/main/java/com/ctgu/lost_found/common/exception/GlobalExceptionHandler.java b/src/main/java/com/ctgu/lost_found/common/exception/GlobalExceptionHandler.java index 6f667a6..caae731 100644 --- a/src/main/java/com/ctgu/lost_found/common/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/ctgu/lost_found/common/exception/GlobalExceptionHandler.java @@ -35,7 +35,7 @@ public class GlobalExceptionHandler { log.info("\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); log.error("未登录异常: " + e.getMessage()); log.info("\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); - return Result.error("未登录, 请登录后访问"); + return Result.notLogin(); } /** @@ -46,7 +46,7 @@ public class GlobalExceptionHandler { log.info("\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); log.error("角色权限异常: " + e.getMessage()); log.info("\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); - return Result.error("不具备访问该接口的权限"); + return Result.noPermissions("不具备访问该接口的权限"); } /** diff --git a/src/main/java/com/ctgu/lost_found/common/util/redis/RedisCacheUtil.java b/src/main/java/com/ctgu/lost_found/common/util/redis/RedisCacheUtil.java new file mode 100644 index 0000000..e161d66 --- /dev/null +++ b/src/main/java/com/ctgu/lost_found/common/util/redis/RedisCacheUtil.java @@ -0,0 +1,115 @@ +package com.ctgu.lost_found.common.util.redis; + + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; + +/** + * @author ZMC + *

+ * Redis缓存工具类 + */ +@Slf4j +@Component +public class RedisCacheUtil { + + private final StringRedisTemplate stringRedisTemplate; + + public RedisCacheUtil(StringRedisTemplate stringRedisTemplate) { + this.stringRedisTemplate = stringRedisTemplate; + } + + /** + * 向redis缓存进行写请求: 插入任意Java类型的数据,并设置随机TTL + * TTL: 缓存有效期,过期后缓存自动删除 + * 随机设置TTL: 使缓存的TTL尽可能均匀,可以在一定程度上避免缓存雪崩 + * + * @param key 数据的key + * @param value 数据 + * @param time 过期时间 + * @param unit 时间单位 + */ + public void setWithTTL(String key, Object value, Long time, TimeUnit unit) { + // 随机获取[1,100] * [0.1, 1.1]的浮点数 + Random random = new Random(); + int randomInt = (int) ((random.nextInt(100) + 1) * (random.nextFloat() + 0.1)) + 1; + // 手动序列化: JSONUtil.toJsonStr(value) + stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value), randomInt * time, unit); + } + + /** + * 向redis缓存进行写请求: 插入任意Java类型的数据,并设置逻辑过期时间 + * + * @param key 数据的key + * @param value 数据 + * @param time 过期时间 + * @param unit 时间单位 + */ + public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit) { + // 设置逻辑过期 + RedisData redisData = new RedisData(); + redisData.setData(value); + // redis缓存里的TTL数值单位为s,因此逻辑过期时间单位使用s更合适 + redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time))); + // 手动序列化: JSONUtil.toJsonStr(value) + stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData)); + } + + /** + * 按key删除redis缓存 + * + * @param key 要删除的数据的key + */ + public void deleteByKey(String key) { + stringRedisTemplate.delete(key); + } + + /** + * 向redis缓存进行读请求: 使用 缓存空对象 解决 缓存穿透问题 + * + * @param keyPrefix key的前缀 + * @param id 数据库表的主键id(或unique字段) + * @param type 实体类的Class + * @param dbFallback 查询MySQL数据库的方法 + * @param time 过期时间 + * @param unit 时间单位 + */ + public R queryWithPassThrough(String keyPrefix, ID id, Class type, Function dbFallback, Long time, TimeUnit unit) { + String key = keyPrefix + id; + // 1.查询redis缓存 + String json = stringRedisTemplate.opsForValue().get(key); + // 判断json是否是空串(因为采用缓存空对象解决缓存穿透的问题) + if ("".equals(json)) { + // 返回错误信息 + return null; + } + // 2.判断缓存是否命中,并且是合理数据 + if (StrUtil.isNotBlank(json)) { + // 3.缓存命中,手动反序列化,返回正确数据 + return JSONUtil.toBean(json, type); + } + // 4.缓存未命中(json为null),根据id查询MySQL数据库 + R r = dbFallback.apply(id); + // 5.MySQL数据库中不存在 + if (r == null) { + // 缓存空字符串,设置TTL为3分钟: 解决缓存穿透的问题 + stringRedisTemplate.opsForValue().set(key, "", RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES); + // 返回错误信息 + return null; + } + // 6.MySQL数据库存在,写入redis缓存 + this.setWithTTL(key, r, time, unit); + // 7.返回正确数据 + return r; + } + + +} diff --git a/src/main/java/com/ctgu/lost_found/common/util/redis/RedisConstants.java b/src/main/java/com/ctgu/lost_found/common/util/redis/RedisConstants.java new file mode 100644 index 0000000..cbda344 --- /dev/null +++ b/src/main/java/com/ctgu/lost_found/common/util/redis/RedisConstants.java @@ -0,0 +1,18 @@ +package com.ctgu.lost_found.common.util.redis; + + +/** + * @author ZMC + *

+ * Redis缓存常量池 + */ +public class RedisConstants { + + public static final Long CACHE_NULL_TTL = 3L; + + public static final String CACHE_ITEM_TYPE_KEY = "cache:ItemType:"; + + public static final Long CACHE_ITEM_TYPE_TTL = 24L; + + +} diff --git a/src/main/java/com/ctgu/lost_found/common/util/redis/RedisData.java b/src/main/java/com/ctgu/lost_found/common/util/redis/RedisData.java new file mode 100644 index 0000000..9d631c2 --- /dev/null +++ b/src/main/java/com/ctgu/lost_found/common/util/redis/RedisData.java @@ -0,0 +1,16 @@ +package com.ctgu.lost_found.common.util.redis; + +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * @author ZMC + *

+ * Redis缓存---逻辑过期传输对象 + */ +@Data +public class RedisData { + private LocalDateTime expireTime; + private Object data; +} diff --git a/src/main/java/com/ctgu/lost_found/service/Impl/ItemTypeServiceImpl.java b/src/main/java/com/ctgu/lost_found/service/Impl/ItemTypeServiceImpl.java index 5679e0b..4f78d81 100644 --- a/src/main/java/com/ctgu/lost_found/service/Impl/ItemTypeServiceImpl.java +++ b/src/main/java/com/ctgu/lost_found/service/Impl/ItemTypeServiceImpl.java @@ -2,6 +2,8 @@ package com.ctgu.lost_found.service.Impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.ctgu.lost_found.common.util.Result; +import com.ctgu.lost_found.common.util.redis.RedisCacheUtil; +import com.ctgu.lost_found.common.util.redis.RedisConstants; import com.ctgu.lost_found.dao.ItemTypeDao; import com.ctgu.lost_found.entity.ItemType; import com.ctgu.lost_found.service.ItemTypeService; @@ -9,6 +11,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.concurrent.TimeUnit; /** * @author ZMC @@ -19,8 +22,11 @@ public class ItemTypeServiceImpl implements ItemTypeService { private final ItemTypeDao itemTypeDao; - public ItemTypeServiceImpl(ItemTypeDao itemTypeDao) { + private final RedisCacheUtil redisCacheUtil; + + public ItemTypeServiceImpl(ItemTypeDao itemTypeDao, RedisCacheUtil redisCacheUtil) { this.itemTypeDao = itemTypeDao; + this.redisCacheUtil = redisCacheUtil; } /** @@ -45,9 +51,12 @@ public class ItemTypeServiceImpl implements ItemTypeService { */ @Override public Result deleteItemType(Integer id) { + // 1.先更新MySQL if (itemTypeDao.deleteById(id) == 0) { return Result.error("删除失败,物品类型id不存在"); } + // 2.再更新(删除)Redis缓存 + redisCacheUtil.deleteByKey(RedisConstants.CACHE_ITEM_TYPE_KEY + id); return Result.success("删除成功"); } @@ -59,9 +68,12 @@ public class ItemTypeServiceImpl implements ItemTypeService { */ @Override public Result updateItemType(ItemType itemType) { + // 1.先更新MySQL if (itemTypeDao.updateById(itemType) == 0) { return Result.error("修改失败,可能是传入数据不符合数据库约束或者物品类型id不存在"); } + // 2.再更新(删除)Redis缓存 + redisCacheUtil.deleteByKey(RedisConstants.CACHE_ITEM_TYPE_KEY + itemType.getId()); return Result.success("修改成功"); } @@ -73,11 +85,21 @@ public class ItemTypeServiceImpl implements ItemTypeService { */ @Override public Result getItemTypeList(Integer id) { - List itemTypeList = itemTypeDao.getItemTypeList(id); - if (itemTypeList.isEmpty()) { - return Result.error("查询失败,物品类型不存在"); + if (id == null) { + //查询所有物品类型 + List itemTypeList = itemTypeDao.getItemTypeList(null); + if (itemTypeList == null || itemTypeList.isEmpty()) { + return Result.error("查询失败,物品类型不存在"); + } + return Result.success("200", itemTypeList); + } else { + //按id查询单个物品类型 + ItemType itemType = redisCacheUtil.queryWithPassThrough(RedisConstants.CACHE_ITEM_TYPE_KEY, id, ItemType.class, this::getItemTypeById, RedisConstants.CACHE_ITEM_TYPE_TTL, TimeUnit.HOURS); + if (itemType == null) { + return Result.error("查询失败,物品类型不存在"); + } + return Result.success("200", itemType); } - return Result.success(itemTypeList); } /** @@ -88,17 +110,32 @@ public class ItemTypeServiceImpl implements ItemTypeService { */ @Override public Result fuzzyItemTypeList(String type) { - if(type == null){ + if (type == null) { type = ""; } QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.like("type", type); List itemTypeList = itemTypeDao.selectList(queryWrapper); queryWrapper.clone(); - if (itemTypeList.isEmpty()) { + if (itemTypeList == null || itemTypeList.isEmpty()) { return Result.error("查询失败,物品类型不存在"); } return Result.success(itemTypeList); } + /** + * 按id查询单个物品类型 + * + * @param id 主键id + * @return ItemType + */ + public ItemType getItemTypeById(Integer id) { + List itemTypeList = itemTypeDao.getItemTypeList(id); + if (itemTypeList == null || itemTypeList.isEmpty()) { + return null; + } + return itemTypeList.get(0); + } + + } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 8e87a0d..197d3df 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,9 +1,9 @@ spring: # 配置MySQL连接与Druid数据源 datasource: - username: "zmc1_mysql" - password: "zmc1_mysql@ctgu" - url: "jdbc:mysql://rm-bp1yq8zn4da1x08scho.mysql.rds.aliyuncs.com:3306/lost_found?useUnicode=true&characterEncoding=utf-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=Asia/Shanghai" + username: "xxxxxxx" + password: "xxxxxxx" + url: "jdbc:mysql://xxxxxxx:3306/lost_found?useUnicode=true&characterEncoding=utf-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=Asia/Shanghai" # 数据库驱动 driver-class-name: com.mysql.cj.jdbc.Driver # 使用Druid数据源 @@ -13,7 +13,7 @@ spring: druid: initialsize: 5 minIdle: 5 - maxActive: 20 + maxActive: 200 maxwait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 @@ -55,15 +55,43 @@ spring: config: drop-table-allow: false + + # 配置Redis + redis: + # Redis服务器地址,默认为localhost + host: xxxxxxx + # Redis服务器连接端口,默认为6379 + port: 9736 + # Redis数据库索引,默认为0(使用0号数据库) + database: 0 + # Redis服务器连接密码,默认为空 + password: xxxxxxx + # 连接超时时间 + timeout: 10s + + lettuce: + # 配置连接池 + pool: + # 连接池最大连接数 + max-active: 200 + # 连接池中的最大空闲连接 + max-idle: 10 + # 连接池中的最小空闲连接 + min-idle: 0 + # 连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: 100ms + + mvc: pathmatch: # 解决因springboot版本过高而无法运行Swagger的问题 matching-strategy: ant_path_matcher + #配置邮件任务 mail: username: 2869581855@qq.com - password: icbcjgclczfjdegd + password: xxxxxxx default-encoding: utf-8 port: 587 host: smtp.qq.com @@ -75,6 +103,7 @@ spring: enable: true required: true + # 设置项目名称 application: name: lost_found -- Gitee