CREATE TABLE `crm_banner` ( `id` char(19) NOT NULL DEFAULT ‘‘ COMMENT ‘ID‘, `title` varchar(20) DEFAULT ‘‘ COMMENT ‘标题‘, `image_url` varchar(500) NOT NULL DEFAULT ‘‘ COMMENT ‘图片地址‘, `link_url` varchar(500) DEFAULT ‘‘ COMMENT ‘链接地址‘, `sort` int(10) unsigned NOT NULL DEFAULT ‘0‘ COMMENT ‘排序‘, `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT ‘0‘ COMMENT ‘逻辑删除 1(true)已删除, 0(false)未删除‘, `gmt_create` datetime NOT NULL COMMENT ‘创建时间‘, `gmt_modified` datetime NOT NULL COMMENT ‘更新时间‘, PRIMARY KEY (`id`), UNIQUE KEY `uk_name` (`title`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=‘首页banner表‘; # # Data for table "crm_banner" # INSERT INTO `crm_banner` VALUES (‘1194556896025845762‘,‘test1‘,‘https://online-teach-file.oss-cn-beijing.aliyuncs.com/cms/2019/11/14/297acd3b-b592-4cfb-a446-a28310369675.jpg‘,‘/course‘,1,0,‘2019-11-13 18:05:32‘,‘2019-11-18 10:28:22‘),(‘1194607458461216769‘,‘test2‘,‘https://online-teach-file.oss-cn-beijing.aliyuncs.com/cms/2019/11/13/8f80790d-d736-4842-a6a4-4dcb0d684d4e.jpg‘,‘/teacher‘,2,0,‘2019-11-13 21:26:27‘,‘2019-11-14 09:12:15‘);
(2)使用代码生成器生成代码
# 服务端口 server.port=8004 # 服务名 spring.application.name=service-cms # mysql数据库连接 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/guli?characterEncoding=UTF-8&serverTimezone=GMT%2B8 spring.datasource.username=root spring.datasource.password=123 #返回json的全局时间格式 spring.jackson.date-format=yyyy-MM-dd HH:mm:ss spring.jackson.time-zone=GMT+8 #配置mapper xml文件的路径 mybatis-plus.mapper-locations=classpath:com/atguigu/educms/mapper/xml/*.xml #mybatis日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl # nacos服务地址 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 #开启熔断机制 feign.hystrix.enabled=true # 设置hystrix超时时间,默认1000ms hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=6000
@MapperScan("com.atguigu.educms.mapper") @ComponentScan({"com.atguigu"}) //指定扫描位置 @EnableDiscoveryClient //nacos注册 @EnableFeignClients @SpringBootApplication public class CmsApplication { public static void main(String[] args){ SpringApplication.run(CmsApplication.class, args); } }
创建BannerAdminController类,编写banner后台分页查询、添加、修改、删除接口
@RestController @RequestMapping("/educms/banneradmin") @CrossOrigin public class BannerAdminController { @Autowired private CrmBannerService bannerService; //1 分页查询banner @ApiOperation(value = "分页查询Banner") @GetMapping("pageBanner/{page}/{limit}") public R pageBanner(@PathVariable long page,@PathVariable long limit) { Page<CrmBanner> pageParam = new Page<>(page, limit); bannerService.page(pageParam,null); return R.ok().data("items", pageParam.getRecords()).data("total", pageParam.getTotal()); } @ApiOperation(value = "获取Banner") @GetMapping("get/{id}") public R get(@PathVariable String id) { CrmBanner banner = bannerService.getById(id); return R.ok().data("item", banner); } @ApiOperation(value = "新增Banner") @PostMapping("save") public R save(@RequestBody CrmBanner banner) { bannerService.save(banner); return R.ok(); } @ApiOperation(value = "修改Banner") @PutMapping("update") public R updateById(@RequestBody CrmBanner banner) { bannerService.updateById(banner); return R.ok(); } @ApiOperation(value = "删除Banner") @DeleteMapping("remove/{id}") public R remove(@PathVariable String id) { bannerService.removeById(id); return R.ok(); } }
创建BannerFrontController类,编写获取banner前台数据接口
@RestController @RequestMapping("/educms/bannerfront") @CrossOrigin public class BannerFrontController { @Autowired private CrmBannerService bannerService; @ApiOperation(value = "获取首页banner") @GetMapping("getAllBanner") public R index() { List<CrmBanner> list = bannerService.selectAllBanner(); return R.ok().data("bannerList", list); } }
@Service public class CrmBannerServiceImpl extends ServiceImpl<CrmBannerMapper, CrmBanner> implements CrmBannerService { //查询所有banner @Cacheable(value = "banner", key = "‘selectIndexList‘") @Override public List<CrmBanner> selectAllBanner() { //根据id进行降序排列,显示排列之后前两条记录 QueryWrapper<CrmBanner> wrapper = new QueryWrapper<>(); wrapper.orderByDesc("id"); //last方法,拼接sql语句 wrapper.last("limit 2"); List<CrmBanner> list = baseMapper.selectList(null); return list; } }
在service-edu模块的Controller目录下创建front/IndexFrontController.class类
@RestController @RequestMapping("/eduservice/indexfront") @CrossOrigin public class IndexFrontController { @Autowired private EduCourseService courseService; @Autowired private EduTeacherService teacherService; //查询前8条热门课程,查询前4条名师 @GetMapping("index") public R index() { //查询前8条热门课程 List<EduCourse> eduList = courseService.selectEightCurse(); //查询前4条名师 List<EduTeacher> teacherList = teacherService.selectFourTeacher(); return R.ok().data("eduList",eduList).data("teacherList",teacherList); } }
在EduCourseServiceImpl实现类中实现selectEightCourse方法
//查询前8条热门课程 @Cacheable(value = "course", key = "‘selectCourseList‘") @Override public List<EduCourse> selectEightCurse() { QueryWrapper<EduCourse> wrapper = new QueryWrapper<>(); wrapper.orderByDesc("id"); wrapper.last("limit 8"); List<EduCourse> eduList = baseMapper.selectList(wrapper); return eduList; }
在EduTeacherServiceImpl实现类中实现selectFourTeacher方法
//查询前4条名师 @Cacheable(value = "teacher", key = "‘selectTeacherList‘") @Override public List<EduTeacher> selectFourTeacher() { QueryWrapper<EduTeacher> wrapper = new QueryWrapper<>(); wrapper.orderByDesc("id"); wrapper.last("limit 4"); List<EduTeacher> teacherList = baseMapper.selectList(wrapper); return teacherList; }
import request from ‘@/utils/request‘ export default { //查询前两条Bannner信息 getListBanner() { return request({ url: `/educms/bannerfront/getAllBanner`, method: ‘get‘ }) } }
import request from ‘@/utils/request‘ export default { //查询前8条热门课程和前4条名师 getIndexData() { return request({ url: `/eduservice/indexfront/index`, method: ‘get‘ }) } }
<script> import banner from ‘@/api/banner‘ import index from ‘@/api/index‘ export default { data () { return { swiperOption: { //配置分页 pagination: { el: ‘.swiper-pagination‘//分页的dom节点 }, //配置导航 navigation: { nextEl: ‘.swiper-button-next‘,//下一页dom节点 prevEl: ‘.swiper-button-prev‘//前一页dom节点 } }, //banner数组 bannerList:[], eduList:[], teacherList:[] } }, created() { //调用查询banner方法 this.getBannerList() this.getHotCourseTeacher() this.getBannerList() }, methods:{ //查询热门课程和名师 getHotCourseTeacher(){ index.getIndexData() .then(response=>{ this.eduList = response.data.data.eduList this.teacherList = response.data.data.teacherList }) }, //查询banner数据 getBannerList(){ banner.getListBanner() .then(response=>{ this.bannerList = response.data.data.bannerList }) } } } </script>
<template> <div> <!-- 幻灯片 开始 --> <div v-swiper:mySwiper="swiperOption"> <div class="swiper-wrapper"> <div v-for="banner in bannerList" :key="banner.id" class="swiper-slide" style="background: #040B1B;"> <a target="_blank" href="banner.linkUrl"> <img :src="banner.imageUrl" alt="banner.title"> </a> </div> </div> <div class="swiper-pagination swiper-pagination-white"></div> <div class="swiper-button-prev swiper-button-white" slot="button-prev"></div> <div class="swiper-button-next swiper-button-white" slot="button-next"></div> </div> <!-- 幻灯片 结束 --> <div id="aCoursesList"> <!-- 网校课程 开始 --> <div> <section class="container"> <header class="comm-title"> <h2 class="tac"> <span class="c-333">热门课程</span> </h2> </header> <div> <article class="comm-course-list"> <ul class="of" id="bna"> <li v-for="course in eduList" :key="course.id"> <div class="cc-l-wrap"> <section class="course-img"> <img :src="course.cover" class="img-responsive" :alt="course.title" > <div class="cc-mask"> <a href="#" title="开始学习" class="comm-btn c-btn-1">开始学习</a> </div> </section> <h3 class="hLh30 txtOf mt10"> <a href="#" :title="course.title" class="course-title fsize18 c-333">{{course.title}}</a> </h3> <section class="mt10 hLh20 of"> <span class="fr jgTag bg-green" v-if="Number(course.price) === 0"> <i class="c-fff fsize12 f-fA">免费</i> </span> <span class="fl jgAttr c-ccc f-fA"> <i class="c-999 f-fA">9634人学习</i> | <i class="c-999 f-fA">9634评论</i> </span> </section> </div> </li> </ul> <div class="clear"></div> </article> <section class="tac pt20"> <a href="#" title="全部课程" class="comm-btn c-btn-2">全部课程</a> </section> </div> </section> </div> <!-- /网校课程 结束 --> <!-- 网校名师 开始 --> <div> <section class="container"> <header class="comm-title"> <h2 class="tac"> <span class="c-333">名师大咖</span> </h2> </header> <div> <article class="i-teacher-list"> <ul class="of"> <li v-for="teacher in teacherList" :key="teacher.id"> <section class="i-teach-wrap"> <div class="i-teach-pic"> <a href="/teacher/1" :title="teacher.name"> <img :alt="teacher.name" :src="teacher.avatar"> </a> </div> <div class="mt10 hLh30 txtOf tac"> <a href="/teacher/1" :title="teacher.name" class="fsize18 c-666">{{teacher.name}}</a> </div> <div class="hLh30 txtOf tac"> <span class="fsize14 c-999">{{teacher.career}}</span> </div> <div class="mt15 i-q-txt"> <p class="c-999 f-fA" >{{teacher.intro}}</p> </div> </section> </li> </ul> <div class="clear"></div> </article> <section class="tac pt20"> <a href="#" title="全部讲师" class="comm-btn c-btn-2">全部讲师</a> </section> </div> </section> </div> <!-- /网校名师 结束 --> </div> </div> </template>
Redis是当前比较热门的NOSQL系统之一,它是一个开源的使用ANSI c语言编写的key-value存储系统(区别于MySQL的二维表格的形式存储。)。和Memcache类似,但很大程度补偿了Memcache的不足。和Memcache一样,Redis数据都是缓存在计算机内存中,不同的是,Memcache只能将数据缓存到内存中,无法自动定期写入硬盘,这就表示,一断电或重启,内存清空,数据丢失。所以Memcache的应用场景适用于缓存无需持久化的数据。而Redis不同的是它会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,实现数据的持久化。
Redis的特点:
1)Redis读取的速度是110000次/s,写的速度是81000次/s;
2)原子 。Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
3)支持多种数据结构:string(字符串);list(列表);hash(哈希),set(集合);zset(有序集合)
4)持久化,集群部署
5)支持过期时间,支持事务,消息订阅
由于redis缓存是公共应用,所以我们把依赖与配置添加到了common模块下面,在common模块pom.xml下添加以下依赖
<!-- redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- spring2.X集成redis所需common-pool2--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.6.0</version> </dependency>
RedisConfig.java
@EnableCaching @Configuration public class RedisConfig extends CachingConfigurerSupport { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); RedisSerializer<String> redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); template.setConnectionFactory(factory); //key序列化方式 template.setKeySerializer(redisSerializer); //value序列化 template.setValueSerializer(jackson2JsonRedisSerializer); //value hashmap序列化 template.setHashValueSerializer(jackson2JsonRedisSerializer); return template; } @Bean public CacheManager cacheManager(RedisConnectionFactory factory) { RedisSerializer<String> redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); //解决查询缓存转换异常的问题 ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); // 配置序列化(解决乱码的问题),过期时间600秒 RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofSeconds(600)) .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) .disableCachingNullValues(); RedisCacheManager cacheManager = RedisCacheManager.builder(factory) .cacheDefaults(config) .build(); return cacheManager; } }
由于首页数据变化不是很频繁,而且首页访问量相对较大,所以我们有必要把首页接口数据缓存到redis缓存中,减少数据库压力和提高访问速度。
改造service-cms模块首页banner接口,首页课程与讲师接口类似
根据方法对其返回结果进行缓存,下次请求时,如果缓存存在,则直接读取缓存数据返回;如果缓存不存在,则执行方法,并把返回的结果存入缓存中。一般用在查询方法上。
查看源码,属性值如下:
属性/方法名 | 解释 |
value | 缓存名,必填,它指定了你的缓存存放在哪块命名空间 |
cacheNames | 与 value 差不多,二选一即可 |
key | 可选属性,可以使用 SpEL 标签自定义缓存的key |
使用该注解标志的方法,每次都会执行,并将结果存入指定的缓存中。其他方法可以直接从响应的缓存中读取缓存数据,而不需要再去查询数据库。一般用在新增方法上。
查看源码,属性值如下:
属性/方法名 | 解释 |
value | 缓存名,必填,它指定了你的缓存存放在哪块命名空间 |
cacheNames | 与 value 差不多,二选一即可 |
key | 可选属性,可以使用 SpEL 标签自定义缓存的key |
使用该注解标志的方法,会清空指定的缓存。一般用在更新或者删除方法上
查看源码,属性值如下:
属性/方法名 | 解释 |
value | 缓存名,必填,它指定了你的缓存存放在哪块命名空间 |
cacheName | 与 value 差不多,二选一即可 |
key | 可选属性,可以使用 SpEL 标签自定义缓存的key |
allEntries | 是否清空所有缓存,默认为 false。如果指定为 true,则方法调用后将立即清空所有的缓存 |
beforeInvocation | 是否在方法执行前就清空,默认为 false。如果指定为 true,则在方法执行前就会清空缓存 |
参考:https://www.cnblogs.com/ldh-up/p/14624642.html
(1)关闭liunx防火墙
systemctl disable firewalld
chkconfig iptables off
(2)找到redis配置文件
注释一行配置
修改 protected-mode yes
改为
protected-mode no
spring.redis.host=192.168.44.132 spring.redis.port=6379 spring.redis.database= 0 spring.redis.timeout=1800000 spring.redis.lettuce.pool.max-active=20 spring.redis.lettuce.pool.max-wait=-1 #最大阻塞等待时间(负数表示没限制) spring.redis.lettuce.pool.max-idle=5 spring.redis.lettuce.pool.min-idle=0
注意:host要换成你的虚拟机ip地址(查看方法:ifconfig命令)
(2)修改CrmBannerServiceImpl,添加redis缓存注解@Cacheable(上面已添加)
@Cacheable(value = "banner", key = "‘selectIndexList‘") @Override public List<CrmBanner> selectIndexList() { List<CrmBanner> list = baseMapper.selectList(new QueryWrapper<CrmBanner>().orderByDesc("sort")); return list; }
(3)刷新前端首页即可在redis添加了key
(4)通过源码查看到key生成的规则
原文:https://www.cnblogs.com/ldh-up/p/14627367.html