文章目录

  • 一.课程分类树状展示
    • 1.前端(略过不用管)
    • 2.后台课程服务编写查询方法
      • 1.CourseTypeController编写查询方法
      • 2.CourseTypeService实现方法
      • 3.实现查询逻辑
      • 4.记得zuul需要配置跨域
  • 二.课程分类缓存
    • 1.思考问题
      • 1.为什么要做课程分类缓存
      • 2.为什么要使用分布式缓存
      • 4.搭建缓存项目结构
    • 2.hrm-redis-server-2030搭建
      • 1.导入依赖
      • 2.完成基本配置:配置类,配置文件
      • 3.集成Redis
      • 4.测试jedis是否集成成功
    • 3.编写hrm-redis-feign模块
      • 1.导入包
      • 2.编写Fiegn接口
    • 4.课程集成Feign实现课程缓存
      • 1.集成Feign
      • 2.课程分类缓存
        • ①导入json转换的包
        • ②抽取Redis的key到常量类
        • ③实现代码
      • 3.测试缓存功能是否实现
      • 4.缓存更新(解决数据不同步)
        • ①重置缓存(添加,修改,删除的时候)
        • ②测试缓存更新

一.课程分类树状展示

1.前端(略过不用管)

  • 搭建前端项目结构
hrm-websites-------空项目hrm-system-website-------static web(静态模块)拷贝前端项目中的内容进来,运行npm run devhrm-course-website-------static web(静态模块)

需要注意请求的基本路径,以及资源路径是否正确

2.后台课程服务编写查询方法

1.CourseTypeController编写查询方法
@RequestMapping(value="/treeData",method= RequestMethod.GET)
public List<CourseType> treeData(){return courseTypeService.treeData();
}
2.CourseTypeService实现方法
@Overridepublic List<CourseType> treeData() {//查询出所有课程类型List<CourseType> courseTypes = List<CourseType> courseTypes = baseMapper.selectList(null);//用来封装一级分类的集合,List<CourseType> PrimaryCourseTypes = new ArrayList<>();//找出所有一级分类for (CourseType courseType : courseTypes) {if(courseType.getPid().longValue()==0){PrimaryCourseTypes.add(courseType);}else{//不是一级分类的//定义当前分类的父分类容器CourseType CurrentParentCourseType = null;for (CourseType pcourseType : courseTypes) {if(courseType.getPid().longValue()==pcourseType.getId()){CurrentParentCourseType = pcourseType;break;}}if(CurrentParentCourseType!=null){CurrentParentCourseType.getChildren().add(courseType);}}}return PrimaryCourseTypes;}
3.实现查询逻辑

因为前台tree组件需要的结构数据如下,

[{label: '一级 1',children: [{label: '二级 1-1',children: [{label: '三级 1-1-1'}]}]
},...]

所以我们我们在后台查询到课程类型的数据时,要将它搭建成这种结构的list集合,返回给前台
因为课程分类之间通过pid(父id)和id的关系,本身courseType对象之间就有层级关系,所以我们只需要返回所有一级分类的对象,就可以了,因为一级对象内就有其对应的子分类们

1.记得要在CourseType增加Children属性,打注解 说明和表中字段不一样

@TableField(exist = false)  //告诉Mybatis-plus 表中没有这个字段private List<CourseType> children = new ArrayList<>();

2.遍历所有分类,我们先筛选出所有的一级分类,条件 pid=0
3. 遍历所有分类,所有分类和所有不是一级分类的分类进行比较

  • 条件:当前分类的pid=遍历分类的id,遍历分类就是当前分类的父分类
  • 满足条件的遍历分类就是当前分类的父分类,
  • 把当前对象添加到满足条件的遍历分类的children中,到最后所有的子分类封装到了对应的父分类中
4.记得zuul需要配置跨域

拷贝一个配置类,配置允许访问的域

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;@Configuration
public class GlobalCorsConfig {@Beanpublic CorsFilter corsFilter() {//1.添加CORS配置信息CorsConfiguration config = new CorsConfiguration();//1) 允许的域,不要写*,否则cookie就无法使用了config.addAllowedOrigin("http://127.0.0.1:6001");config.addAllowedOrigin("http://localhost:6001");//2) 是否发送Cookie信息config.setAllowCredentials(true);//3) 允许的请求方式config.addAllowedMethod("OPTIONS");config.addAllowedMethod("HEAD");config.addAllowedMethod("GET");config.addAllowedMethod("PUT");config.addAllowedMethod("POST");config.addAllowedMethod("DELETE");config.addAllowedMethod("PATCH");// 4)允许的头信息config.addAllowedHeader("*");//2.添加映射路径,我们拦截一切请求UrlBasedCorsConfigurationSource configSource = newUrlBasedCorsConfigurationSource();configSource.registerCorsConfiguration("/**", config);//3.返回新的CorsFilter.return new CorsFilter(configSource);}
}

到最后我们就可以测试一下,访问前端课程分类页面

二.课程分类缓存

1.思考问题

1.为什么要做课程分类缓存
  • 为了提高查询效率
    像课程分类这类数据,是基本不会发生太大改变的,而且经常被访问,所以我们可以为课程分类的查询做缓存,提高查询效率
2.为什么要使用分布式缓存
  • 本地缓存在分布式±(集群)项目中会有缓存不同步问题见图:Redis作为中央缓存原理.jpg

- 分布式缓存是独立的服务,不会抢占项目本身的内存见图:Redis缓存服务项目结构分析.jpg

4.搭建缓存项目结构
hrm-redis-parenthrm-redis-feign------feign模块,用于其他服务访问redis服务hrm-redis-server-2030------用于做缓存,集成jedis操作redis

2.hrm-redis-server-2030搭建

  • 注册到注册中心
  • 集成配置中心客户端
  • 集成Jedis操作Redis
  • zuul配置微服务的路由
1.导入依赖

导入基本jar包

  • Eureka客户端
  • Config客户端包
  • web包
  • jedis包
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--  集成Web的jar包-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-config</artifactId>
</dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.2.0</version>
</dependency><!--需要用到项目公共工具模块的AjaxResult对象-->
<dependency><groupId>cn.itsource</groupId><artifactId>hrm-basic-utils</artifactId>
</dependency>
2.完成基本配置:配置类,配置文件
  • 记得zuul配置redis路由
3.集成Redis
  • 拷贝工具类 RedisUtils(全部资料/工具类)
    RedisUtils
package cn.itsource.hrm.util;import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;import java.io.IOException;
import java.util.Properties;/*** 获取连接池对象*/
public enum RedisUtils {INSTANCE;static JedisPool jedisPool = null;static {//1 创建连接池配置对象JedisPoolConfig config = new JedisPoolConfig();//2 进行配置-四个配置config.setMaxIdle(1);//最小连接数config.setMaxTotal(11);//最大连接数config.setMaxWaitMillis(10 * 1000L);//最长等待时间config.setTestOnBorrow(true);//测试连接时是否畅通//3 通过配置对象创建连接池对象Properties properties = null;try {properties = new Properties();properties.load(RedisUtils.class.getClassLoader().getResourceAsStream("redis.properties"));} catch (IOException e) {e.printStackTrace();}String host = properties.getProperty("redis.host");String port = properties.getProperty("redis.port");String password = properties.getProperty("redis.password");String timeout = properties.getProperty("redis.timeout");jedisPool = new JedisPool(config, host, Integer.valueOf(port),Integer.valueOf(timeout), password);}//获取连接public Jedis getSource() {return jedisPool.getResource();}//关闭资源public void closeSource(Jedis jedis) {if (jedis != null) {jedis.close();}}/*** 设置字符值** @param key* @param value*/public void set(String key, String value) {Jedis jedis = getSource();jedis.set(key, value);closeSource(jedis);}/*** 设置字符值** @param key*/public String get(String key) {Jedis jedis = getSource();try {return jedis.get(key);} catch (Exception e) {e.printStackTrace();} finally {closeSource(jedis);}return null;}/*** 设置* @param key* @param value*/public void set(byte[] key, byte[] value) {Jedis jedis = getSource();jedis.set(key, value);closeSource(jedis);}/**** @param key* @return*/public byte[]  get(byte[] key) {Jedis jedis = getSource();try {return jedis.get(key);} catch (Exception e) {e.printStackTrace();} finally {closeSource(jedis);}return null;}
}
  • 创建redis.properties(可以去笔记拷贝)
redis.host=127.0.0.1
redis.port=6379
redis.password=123456
redis.timeout=10000

密码是指redis安装目录中redis.windows.conf配置文件中的

  • 编写RedisController:实现set,和get方法
@RestController
@RequestMapping("/redis")
public class RedisController {//添加设置数据@PostMapping("/set")public AjaxResult set(@RequestParam("key") String key, @RequestParam("value") String value){RedisUtils.INSTANCE.set(key,value);return AjaxResult.me();}//获取key所对应的value@GetMapping("/get/{key}")public AjaxResult get(@PathVariable("key") String key){//获取key值对应的valueString result = RedisUtils.INSTANCE.get(key);//将结果值添加到AjaxResult结果对象return AjaxResult.me().setResultObj(result);}
}
4.测试jedis是否集成成功
  • 测试set、get方法是否成功

3.编写hrm-redis-feign模块

hrm-redis-feign是抽取了,feign的接口
哪个微服务需要使用feign,就开启feign的客户端

1.导入包
  • 导入Feign的包
  • 导入basic-utils的包 接口方法返回值要用AjaxResult
<!--集成Feign-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency><dependency><groupId>cn.itsource</groupId><artifactId>hrm-basic-utils</artifactId>
</dependency>
2.编写Fiegn接口
@FeignClient(value = "redis-server")//写远程调用的服务名
public interface RedisFeignClient {//写上要调用的远程的方法,不要方法体@PostMapping("/redis/set")public AjaxResult set(@RequestParam("key") String key, @RequestParam("value") String value);@GetMapping("/redis/get/{key}")public AjaxResult get(@PathVariable("key") String key);
}

注意访问路径,改为/redis/get/{key} 和/redis/set

4.课程集成Feign实现课程缓存

1.集成Feign
  • 依赖Feign的模块
<!--依赖hrm-redis-feign模块-->
<dependency><groupId>cn.itsource</groupId><artifactId>hrm-redis-feign</artifactId>
</dependency>
  • 配置类开启Feign

课程服务主配置类打上@EnableFeignClients(“扫描的接口包”)-----开启Feign客户端

@EnableFeignClients("cn.itsource.hrm.feignclients") //开启Feign客户端
public class CourseServerApplication2020
{...
}
2.课程分类缓存
  • TreeData方法:查询课程分类的时候先查询Redis
  • 如果Redis有数据就组装数据,直接返回(使用Fastjson操作json)
  • redis如果没有数据就从数据库中查询,并同步一份到redis中,之后组装数据、返回数据
①导入json转换的包
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.62</version></dependency>
②抽取Redis的key到常量类

抽取到hrm-basic-common模块

public class RedisKeyConstants {public static final String COURSE_TYPE = "course_type";
}
③实现代码
@Autowired
private RedisFeignClient redisFeignClient;@Override
public List<CourseType> treeData() {//查询出所有课程类型List<CourseType> courseTypes = null;//查询课程分类前先查询redisAjaxResult ajaxResult = redisFeignClient.get(RedisKeyConstants.COURSE_TYPE);//判断redis中是否能拿到数据if(ajaxResult.isSuccess()&& ajaxResult.getResultObj()!=null){//说明redis中有数据//直接返回数据String jsonFromRedis = ajaxResult.getResultObj().toString();//需要将json字符串格式对象转为ListcourseTypes =  JSON.parseArray(jsonFromRedis,CourseType.class);}else{//说明redis中没有数据,需要到数据库中查找//查找到数据之后,同步一份到redis中//数据库中查找所有课程类型List<CourseType> courseTypes = baseMapper.selectList(null);//查找到数据之后,同步一份到redis中redisFeignClient.set(RedisKeyConstants.COURSE_TYPE, JSON.toJSONString(courseTypes));}//用来封装一级分类的集合,List<CourseType> PrimaryCourseTypes = new ArrayList<>();//找出所有一级分类for (CourseType courseType : courseTypes) {if(courseType.getPid().longValue()==0){PrimaryCourseTypes.add(courseType);}else{//不是一级分类的//定义当前分类的父分类容器CourseType CurrentParentCourseType = null;for (CourseType pcourseType : courseTypes) {if(courseType.getPid().longValue()==pcourseType.getId()){CurrentParentCourseType = pcourseType;break;}}if(CurrentParentCourseType!=null){CurrentParentCourseType.getChildren().add(courseType);}}}return PrimaryCourseTypes;
}
3.测试缓存功能是否实现
  • 开启注册中心、配置中心、课程、缓存微服务
  • 开启redis服务端redis-server.exe redis.windows.conf
  • 开启redis客户端redis-cli.exe
    auth 密码-----验证密码
    flushdb清空redis数据
  • swagger访问课程服务的treeData方法
  • redis客户端keys * 查看是否有更新的key
  • get key的值 ,查看是否有数据获取到
  • key和value都能查到,说明redis之前清空后,因为访问数据库,同步了一份给redis,表示redis缓存实现了
4.缓存更新(解决数据不同步)

我们已经把课程分类查询的缓存实现了,但是我们还存在一个问题。这个问题就是如果我执行对课程父类的添加、删除、修改等操作,数据库中的数据更新了,但是redis缓存中的数据还是以前的,导致了数据库和redis缓存数据不同步的问题。

解决方式: 在执行添加、删除、修改操作后,重置redis中的数据

步骤:

①重置缓存(添加,修改,删除的时候)

service层重写添加、修改、删除方法
在执行添加、删除、修改操作后,将重新查询Mysql中的数据,重置redis中的数据

//抽取的重置redis的方法
public List<CourseType> resetRedisforCourseType(){//数据库中查找所有课程类型List<CourseType> courseTypes = baseMapper.selectList(null);//查找到数据之后,同步一份到redis中redisFeignClient.set(RedisKeyConstants.COURSE_TYPE, JSON.toJSONString(courseTypes));return courseTypes;
}@Override
public boolean insert(CourseType entity) {boolean insertSuccess = super.insert(entity);//添加数据后重置redis//从数据库查询最新数据放入redis中resetRedisforCourseType();return insertSuccess;
}@Override
public boolean deleteById(Serializable id) {boolean deleteSuccess = super.deleteById(id);//添加数据后重置redis//从数据库查询最新数据放入redis中resetRedisforCourseType();return deleteSuccess;
}@Override
public boolean updateById(CourseType entity) {boolean updateSuccess = super.updateById(entity);//添加数据后重置redis//从数据库查询最新数据放入redis中resetRedisforCourseType();return updateSuccess;
}
②测试缓存更新
  • 先执行treeData方法,确保redis有数据
  • 执行修改方法,修改某课程分类数据
  • 再次执行treeData,查看数据是否修改
  • 若数据已修改,表示缓存更新已经实现

hrm项目-day02相关推荐

  1. 前端与移动开发乐淘项目-day02

    1.实现mui区域滚动: A.编写区域滚动需要的dom结构: <div class="mui-scroll-wrapper"><div class="m ...

  2. JSD-2204-创建csmall项目-Day02

    1.创建csmall项目 我们要搭建一个项目,用于学习各种微服务知识 搭建的过程有很多新的标准,需要我们掌握和学习 发给大家的3个csmall的项目 csmall-finish.zip:这个项目是当前 ...

  3. hrm项目-day01

    一.使用代码生成器给系统管理微服务生成代码 1.创建代码生成器的模块 hrm-code-generate 2.导入jar包 : mybatis-plus-boot-starter , velocity ...

  4. 2021-08-30 黑马移动端头条项目-day02

    目录 目录 登录注册 准备 API文档 创建组件并配置路由 实现基本登录功能 登录状态提示 表单验证 验证码处理 发送验证码前先)验证手机号 使用倒计时组件 添加发送按钮的loading 存储用户To ...

  5. CGB2009-京淘项目day02

    1. SpringBoot 高级用法 1.1 关于POM.xml文件说明 1.1.1 关于POM文件说明 <build><!--插件的作用maven插件: 当程序在打包编译等mave ...

  6. 传智健康项目day02

    第2章 预约管理-检查项管理 1. 需求分析 传智健康管理系统是一款应用于健康管理机构的业务系统,实现健康管理机构工作内容可视化.患者管理专业化.健康评估数字化.健康干预流程化.知识库集成化,从而提高 ...

  7. 乐优商场项目day02——总结

    一.架构的演变 传统架构 → 水平拆分 → 垂直拆分(最早的分布式) → soa(dubbo) → 微服务(SpringCloud) 二.远程调用技术 rpc协议:自定义数据格式,限定技术,传输速度快 ...

  8. 聊天室项目day02

    客户端连接 package socket;import java.net.Socket;public class Client {/** java.net.Socket* Socket封装了TCP协议 ...

  9. 一个西二旗码农的自白

    What does not kill me, makes me stronger.  凡不能毁灭我的,必使我强大. --尼采 朋友在 javaeye 老炮群转发<一个出身寒门的状元之死>时 ...

最新文章

  1. db2case语句_DB2 常用的SQL语句
  2. mac邮件过滤器SpamSieve,支持任意类型的任意数量的电子邮件帐户
  3. SAP MM 库存初始化和批量扩充物料仓位
  4. python如何使用多线程_python多线程应用中的详细介绍
  5. opengl加载显示3D模型blend类型文件
  6. 结对编程(黄金点游戏)
  7. luogu4389 付公主的背包
  8. 阿里大数据神预测 胜率仅5.9%中国却1:0胜韩国
  9. 连接hadoop java.io.IOException:Could not locate executable null\bin\winutils.exe in the Hadoop binarie
  10. 289B. Polo the Penguin and Matrix
  11. 【机器学习】Softmax和Sigmoid
  12. summit网页上的smt打不开 提示无法启动应用程序 请与应用程序供应商联系
  13. [其他] 10种技巧可提升Android应用运行效果
  14. python哪些是可变对象_python 中的可变对象与不可变对象
  15. 阶段3 1.Mybatis_06.使用Mybatis完成DAO层的开发_7 Mybatis中使用代理Dao的执行过程分析...
  16. Duplicate key
  17. rk3288问题总结!
  18. 02)高淇java300集学习笔记
  19. 新型智慧城市的技术诠释
  20. [数据采集笔记04]——网页解析——lxml、bs4、正则

热门文章

  1. AcWing 365. 圆桌骑士
  2. A-Level经济例题解析及练习 Identifying a firm‘s profit
  3. wpf 导航菜单_WPF:精简导航菜单
  4. Perl的opendir
  5. 联想电脑开机卡在logo界面
  6. 如何快速读懂别人的项目
  7. Javascript获取元素的xpath
  8. HTML大文件上传解决方案实例代码
  9. 版式设计中的点线面总结
  10. 联想禁止 win10 更新 v2.11 免费版