From 7131d381e058ab6b601b3f5da9052625f111340e Mon Sep 17 00:00:00 2001 From: Kirk Lin Date: Wed, 23 Jun 2021 16:56:23 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=89=E7=BA=A7=E8=8F=9C=E5=8D=95=E5=8A=A0?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E5=8F=8A=E5=A2=9E=E5=8A=A0=E6=9C=AC=E5=9C=B0?= =?UTF-8?q?=E9=94=81=E6=96=B9=E6=A1=88=EF=BC=88=E9=80=82=E7=94=A8=E4=BA=8E?= =?UTF-8?q?=E5=8D=95=E4=BD=93=E5=BA=94=E7=94=A8=E6=A8=A1=E5=BC=8F=E3=80=82?= =?UTF-8?q?=E4=B8=8D=E9=80=82=E5=90=88=E5=88=86=E5=B8=83=E5=BC=8F=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/application.yml | 2 +- kkmall-product/pom.xml | 5 + .../service/impl/CategoryServiceImpl.java | 92 +++++++++++++++++++ .../src/main/resources/application.yml | 9 +- .../KkmallProductApplicationTests.java | 16 ++++ 5 files changed, 122 insertions(+), 2 deletions(-) diff --git a/kkmall-gateway/src/main/resources/application.yml b/kkmall-gateway/src/main/resources/application.yml index 70323b0..e113b93 100644 --- a/kkmall-gateway/src/main/resources/application.yml +++ b/kkmall-gateway/src/main/resources/application.yml @@ -54,7 +54,7 @@ spring: filters: - RewritePath=/api/(?.*), /renren-fast/$\{segment} - # 任何以mall.com结尾的域名转发到mall-product + # 任何以kkmall.com结尾的域名转发到kkmall-product # 注意!!!Nginx代理给网关的时候,会丢失请求Host - id: kkmall_route uri: lb://kkmall-product diff --git a/kkmall-product/pom.xml b/kkmall-product/pom.xml index 453dc91..85725b5 100644 --- a/kkmall-product/pom.xml +++ b/kkmall-product/pom.xml @@ -42,6 +42,11 @@ org.springframework.boot spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-data-redis + org.springframework.boot spring-boot-devtools diff --git a/kkmall-product/src/main/java/name/lkk/kkmall/product/service/impl/CategoryServiceImpl.java b/kkmall-product/src/main/java/name/lkk/kkmall/product/service/impl/CategoryServiceImpl.java index 10b133b..945dca8 100644 --- a/kkmall-product/src/main/java/name/lkk/kkmall/product/service/impl/CategoryServiceImpl.java +++ b/kkmall-product/src/main/java/name/lkk/kkmall/product/service/impl/CategoryServiceImpl.java @@ -1,5 +1,7 @@ package name.lkk.kkmall.product.service.impl; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; @@ -12,10 +14,15 @@ import name.lkk.kkmall.product.service.CategoryService; import name.lkk.kkmall.product.vo.Catalog3Vo; import name.lkk.kkmall.product.vo.Catelog2Vo; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -25,6 +32,9 @@ public class CategoryServiceImpl extends ServiceImpl params) { IPage page = this.page( @@ -102,9 +112,47 @@ public class CategoryServiceImpl extends ServiceImpl item.getParentCid().equals(parentCid)).collect(Collectors.toList()); } + /** + * 从缓存中查询CatelogJson + * @return + */ @Override public Map> getCatelogJson() { + + /** + * 1.空结果缓存 解决缓存穿透 + * 2.设置过期时间(加随机值) 解决缓存雪崩 + * 3.加锁 解决缓存击穿 + */ + ValueOperations operations = stringRedisTemplate.opsForValue(); + Map> catelogJson; + // 缓存中没有数据 + String catelogJSON = operations.get("catelogJSON"); + if (ObjectUtils.isEmpty(catelogJSON)) { + + catelogJson = getCatelogJsonFromDBWithLocalLock(); + } else { + System.out.println("缓存命中。。。查询缓存"); + catelogJson = JSON.parseObject(catelogJSON, new TypeReference>>() { + }); + } + return catelogJson; + } + + /** + * 从数据库中查询CatelogJson + * @return + */ + public Map> getCatelogJsonFormDB() { + + String catelogJSON = stringRedisTemplate.opsForValue().get("catelogJSON"); + if (!ObjectUtils.isEmpty(catelogJSON)) { + return JSON.parseObject(catelogJSON, new TypeReference>>() { + }); + } + // 优化:将查询变为一次 List entityList = baseMapper.selectList(null); + // 查询所有一级分类 List level1 = getCategoryEntities(entityList, 0L); Map> parent_cid = level1.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> { @@ -126,9 +174,53 @@ public class CategoryServiceImpl extends ServiceImpl> getCatelogJsonFromDBWithLocalLock() { + String catelogJSON = stringRedisTemplate.opsForValue().get("catelogJSON"); + if (!ObjectUtils.isEmpty(catelogJSON)) { + return JSON.parseObject(catelogJSON, new TypeReference>>() { + }); + } + synchronized (this) { + System.out.println("缓存未命中。。。查询数据库。[本地锁解决方案]"); + + // 优化:将查询变为一次 + List entityList = baseMapper.selectList(null); + + // 查询所有一级分类 + List level1 = getCategoryEntities(entityList, 0L); + Map> parent_cid = level1.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> { + // 拿到每一个一级分类 然后查询他们的二级分类 + List entities = getCategoryEntities(entityList, v.getCatId()); + List catelog2Vos = null; + if (entities != null) { + catelog2Vos = entities.stream().map(l2 -> { + Catelog2Vo catelog2Vo = new Catelog2Vo(v.getCatId().toString(), l2.getName(), l2.getCatId().toString(), null); + // 找当前二级分类的三级分类 + List level3 = getCategoryEntities(entityList, l2.getCatId()); + // 三级分类有数据的情况下 + if (level3 != null) { + List catalog3Vos = level3.stream().map(l3 -> new Catalog3Vo(l3.getCatId().toString(), l3.getName(), l2.getCatId().toString())).collect(Collectors.toList()); + catelog2Vo.setCatalog3List(catalog3Vos); + } + return catelog2Vo; + }).collect(Collectors.toList()); + } + return catelog2Vos; + })); + // 优化:查询到数据库就在锁还没结束之前放入缓存 + stringRedisTemplate.opsForValue().set("catelogJSON", JSON.toJSONString(parent_cid), 1, TimeUnit.DAYS); + return parent_cid; + } + } + /** * 递归收集所有节点 */ diff --git a/kkmall-product/src/main/resources/application.yml b/kkmall-product/src/main/resources/application.yml index 166867f..bb4d0f3 100644 --- a/kkmall-product/src/main/resources/application.yml +++ b/kkmall-product/src/main/resources/application.yml @@ -5,7 +5,6 @@ spring: url: jdbc:mysql://localhost:3306/kkmall_pms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 66CcFf!! - cloud: nacos: discovery: @@ -19,6 +18,10 @@ spring: static-path-pattern: /static/** thymeleaf: cache: true + redis: + host: localhost + port: 6379 + password: linkekun mybatis-plus: mapper-locations: classpath:/mapper/**/*.xml @@ -32,3 +35,7 @@ mybatis-plus: #log-impl: org.apache.ibatis.logging.stdout.StdOutImpl server: port: 10000 + +logging: + level: + name.lkk.kkmall: error \ No newline at end of file diff --git a/kkmall-product/src/test/java/name/lkk/kkmall/product/KkmallProductApplicationTests.java b/kkmall-product/src/test/java/name/lkk/kkmall/product/KkmallProductApplicationTests.java index 66ee163..52de044 100644 --- a/kkmall-product/src/test/java/name/lkk/kkmall/product/KkmallProductApplicationTests.java +++ b/kkmall-product/src/test/java/name/lkk/kkmall/product/KkmallProductApplicationTests.java @@ -2,9 +2,14 @@ package name.lkk.kkmall.product; import name.lkk.kkmall.product.entity.BrandEntity; import name.lkk.kkmall.product.service.impl.BrandServiceImpl; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.ValueOperations; + +import java.util.UUID; @SpringBootTest class KkmallProductApplicationTests { @@ -12,6 +17,17 @@ class KkmallProductApplicationTests { @Autowired BrandServiceImpl brandService; + @Autowired + StringRedisTemplate stringRedisTemplate; + + @Test + @DisplayName("测试StringRedisTemplate") + public void TestStringRedisTemplate(){ + ValueOperations stringStringValueOperations = stringRedisTemplate.opsForValue(); + stringStringValueOperations.set("Hello","KK"+ UUID.randomUUID()); + System.out.println(stringStringValueOperations.get("Hello")); + } + @Test void contextLoads() { // BrandEntity brandEntity = new BrandEntity();