1.内容介绍
1. 分布式缓存方案(掌握)
2. 缓存服务搭建(掌握)
3. 课程分类缓存(掌握)
4. 课程添加(掌握)
2.课程分类缓存
2.1.分布式缓存方案
2.1.1.什么是缓存
通常将数据从数据库中同步一份到内存中,客户端直接从内存中查询数据,减少了和数据库的交互次数,提高查询性能(内存读写很快),减轻数据库的压力
2.1.2.那些数据适合缓存
1)经常查询的热点数据
2)不经常变的数据(数据变化会导致缓存中的数据跟着变,如果比较频繁,性能开销比较大)
2.1.3.缓存的流程
1 第一次查询,先看缓存是否有数据,如果没有,去数据库查询数据,查询完成后,json化并保存到redis
2 如果有数据,则说明有缓存,直接走缓存
3 返回数据
1)注意:数据库数据被修改,缓存要清空,或者重置;(查询)当将需要缓存的数据保存到 缓存中时,不需要关注是否抛异常
2.1.4.为什么要缓存课程分类
门户首页需要展示课程分类,首页并发比较高,导致可能分类查询机率非常大,并且课程分类的数据不会经常变,没必要每次访问都重新去Mysql查询一次课程分类,我们可以考虑做缓存。
对于门户网站的首页,几乎全是静态html的展示形式。
理由:静态html是可以部署到 nginx服务器。
nginx服务有2大特点:1.只支持静态资源部署;2.支持的并发量非常高,大概是tomcat峰值并发量的100倍
2.1.5.传统缓存方案及流程
2.1.6.传统缓存方案的优缺点
1) 在集群环境中,每个应用都有一个本地缓存,当缓存发生修改会造成缓存不同步问题
2) 本地缓存本身要占用应用的内存空间
2.1.7.分布式缓存方案
使用Redis作为共享缓存 ,解决缓存不同步问题,Redis是独立的服务,缓存不用占用应用本身的内存空间。
2.2.课程集成Redis
SpringBoot提供了整合Redis的方案,我们使用spring-boot-starter-data-redis包就可以很方便的操作Redis了
2.2.1.导入依赖
<!-- 缓存redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.2.2.配置redis
spring:
redis:
host: localhost
port: 6379
timeout: 5000 #连接超时 毫秒
jedis:
pool:
maxActive: 30
maxIdle: 30
minIdle: 10
maxWait: -1 #连接池最大等行时间 -1没有限制
password: 123456
2.2.3.自定义redis的操作工具类
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class RedisService {
@Autowired
private StringRedisTemplate stringRedisTemplate;
//下面的4个重载的方法---保存所有value的数据类型。并都以json格式存储
public void setStringKeyAndValue(String key, Object value) {
stringRedisTemplate.opsForValue().set(key, JSONObject.toJSONString(value));
}
public void setStringKeyAndValue(String key, String value) {
stringRedisTemplate.opsForValue().set(key, value);
}
public void setStringKeyAndValue(String key, Object value, long minute) {
stringRedisTemplate.opsForValue().set(key, JSONObject.toJSONString(value), minute, TimeUnit.MINUTES);
}
public void setStringKeyAndValue(String key, String value, long minute) {
stringRedisTemplate.opsForValue().set(key, value, minute, TimeUnit.MINUTES);
}
/**
* 功能说明 取出对象并转换为对象类型
* @param key
* @param clazz
* @return T
* @author caiwen
* @date 2020/8/22
*/
public <T> T getKeyObjectValue(String key, Class<T> clazz) {
T t = null;
String s = stringRedisTemplate.opsForValue().get(key);
if (StringUtils.equalsIgnoreCase(clazz.getName(), "java.util.lang.string")) {
t = (T) s;
} else if (StringUtils.isNotBlank(s)) {
t = JSONObject.parseObject(s, clazz);
}
return t;
}
}
2.3.课程分类实现缓存
2.3.1.课程分类缓存逻辑
先思考技术有什么特点?有什么用?能解决我什么问题
我要怎么使用?它提供了哪些功能?有哪些常用的API方法?
实战—解决实际的问题?
1.查询Redis中是否有课程分类
2. 如果有就取Redis数据,进行TreeData处理并返回
3.如果没有就从Mysql查询课程分类
4.把课程分类存储到Redis
5.把课程分类进行TreeData处理并返回
代码略
2.3.2.课程分类缓存清除
数据库中的课程分类被修改,删除,添加,Redis中的课程分类需要清空,或者重置,不然会造成缓存数据和数据库数据不一致问题。例如:
@Override
//删除缓存
public boolean insert(CourseType entity) {
boolean insert = super.insert(entity);
redisTemplate.delete(RedisConstants.KEY_COURSE_TYPE_TREE);
return insert;
}
2.4.使用SpringCache实现缓存
对于缓存声明,Spring的缓存提供了一组java注解:
@Cacheable:触发缓存写入。
@CacheEvict:触发缓存清除。
@CachePut:更新缓存(不会影响到方法的运行)。
@Caching:重新组合要应用于方法的多个缓存操作。
@CacheConfig:设置类级别上共享的一些常见缓存设置。
具体使用方式见文档《Springboot整合Cache基于Redis实现缓存.docx》
2.4.1.开启SpringCache
//缓存服务
@SpringBootApplication
@EnableCaching
public class CacheApplication {
public static void main(String[] args) {
SpringApplication.run(CacheApplication.class) ;
}
}
2.4.2.课程分类缓存
@Override
@Cacheable(cacheNames = RedisConstants.KEY_COURSE_TYPE_TREE,key = "'ALL'")
public List<CourseType> treeData() {
log.debug("从数据
库查询课程分类..........................");
return treeDataMethodThree();
}
2.4.3.缓存的删除
@Override
@CacheEvict(cacheNames = RedisConstants.KEY_COURSE_TYPE_TREE,key = "'ALL'")
public boolean insert(CourseType entity) {
return super.insert(entity);
}
@Override
@CacheEvict(cacheNames = RedisConstants.KEY_COURSE_TYPE_TREE,key = "'ALL'")
public boolean deleteById(Serializable id) {
return super.deleteById(id);
}
@Override
@CacheEvict(cacheNames = RedisConstants.KEY_COURSE_TYPE_TREE,key = "'ALL'")
public boolean updateById(CourseType entity) {
return super.updateById(entity);
}
3.课程管理
3.1.课程设计
3.1.1.课程页面设计
- 课程列表
课程新增,录入课程数据到数据库
课程营销,配置课程的价格等营销信息
上传资源,添加课程的相册信息
课程发布,也叫课程上线,只有发布之后的课程才能在门户网站显示,基于ES实现
课程下线,取消课程发布的课程,不能给门户显示
- 课程新增
录入课程基本信息
录入课程营销
录入课程详情
这里的数据需要保存到三张表 ,基本信息保存到t_course ,营销信息保存到t_course_marker ,课程详情保存到t_course_detail
3.1.2.课程表设计
按照不同的维度对课程垂直分表
T_course保存课程基本信息
T_course_resource保存课程资源,比如相册
T_course_market保存课程营销信息
T_course_detail保存课程详情
3.2.课程基本管理
3.2.1.基础代码生成
3.2.2.课程CRUD实现
3.3.课程添加
上面我们已经分析过添加弹窗的布局 , 我们需要把一个表单的数据,添加到三张表
t_course :课程表
t_course_detail : 课程详情表
t_course_market :课程营销表
t_course_resource :课程资源表,通过 “上传资源”按钮单独上传课程的资源(课程相册)
补充:CourseType的children字段增加一个
@JsonInclude(JsonInclude.Include.NON_EMPTY):转JSON的时候不处理空值字段
3.3.1.前段提交添加
参数格式
var param = {
course:{
courseTypeId:this.addForm.courseTypeId,
name:this.addForm.name,
users:this.addForm.users,
grade:this.addForm.gradeId,
gradeName:gradeName,
pic:this.addForm.pic,
startTime:this.addForm.startTime,
endTime:this.addForm.endTime
},
courseDetail:{
description:this.addForm.description,
intro:this.addForm.intro
},
courseMarket:{
charge:this.addForm.chargeId,
qq:this.addForm.qq,
price:this.addForm.price,
priceOld:this.addForm.priceOld,
expires:this.addForm.expires
}
};
3.3.2.参数DTO封装
//封装课程添加的参数
public class CourseSaveDto {
private Course course;
private CourseDetail courseDetail;
private CourseMarket courseMarket;
3.3.3.课程添加controller
/**
* 保存和修改公用的
* @return Ajaxresult转换结果
*/
@RequestMapping(value="/saveOrUpdate",method= RequestMethod.POST)
public AjaxResult saveOrUpdate(@RequestBody CourseSaveDto courseDto){
try {
courseService.saveOrUpdate(courseDto);
return AjaxResult.me();
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.me().setSuccess(false).setMessage("保存对象失败!"+e.getMessage());
}
}
3.3.4.课程添加service
@Override
public void saveOrUpdate(CourseSaveDto courseDto) {
Course course = courseDto.getCourse();
CourseDetail courseDetail = courseDto.getCourseDetail();
CourseMarket courseMarket = courseDto.getCourseMarket();
//1.基本判断
//2.保存course
course.setStatus(Course.STATUS_OFFLINE);
course.setUserId(42l);
course.setUserName("yhptest1");
course.setTenantId(26l);
course.setTenantName("源码时代");
baseMapper.insert(course);
//3.保存详情
courseDetail.setId(course.getId());
courseDetailMapper.insert(courseDetail);
//4.保存营销
courseMarket.setId(course.getId());
courseMarketMapper.insert(courseMarket);
}
注意:课程营销courseMarket和课程详情courseDetail的ID使用课程course的ID ,创建课程的用户和机构暂时写死,因为还没做登录。
4.课程总结
4.1.重点
1.Redis缓存
2.课程添加
4.2.面试题
1.缓存的查询流程