spring定时器入门
配置
- 在启动类上贴上注解@EnableScheduling 开启定时事务,默认是不开启的
定时器使用模板套路
- 自建一个job包,统一存放这些定时器类
- cron表达式自动生成链接:https://cron.qqe2.com/
@Component
public class StrategyRankStatisJob {
/**
* Cron表达式是一个字符串.字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式:
* (1) Seconds Minutes Hours DayofMonth Month DayofWeek Year
* 秒 分 时 几号 月 周几 年
* (2) Seconds Minutes Hours DayofMonth Month DayofWeek spring支持
* 秒 分 时 几号 月 周几
*/
// 定时任务标签, cron:任务计划表达式
@Scheduled(cron = "0/10 * * * * ?")
public void doWork() {
//这里进行定时要执行的操作
}
}
实现分时统计
排行榜
分析与效果图
- 要实现一个排行榜,如果数据库表数据过多,查询后再排序得出的排行榜数据,这样的查询效果会特别低!!!
- 如果使用分时统计的方式 大大提高了查询效率
- 分时统计:从对应的表查询出数据,将这些数据存放在
分时统计表
中,类似于一个临时表,以后想要拿排行榜的数据,直接从这个表拿即可,查询效率将会特别得快! - 使用定时器的方式,设置每隔一段时间,分时统计表将会从所需的数据库表中获得数据存放进来,这样即可保证页面的排行榜数据是实时的(定时更新过的)
- 效果图
- 主题推荐模块,直接显示后端传过来的排行榜数据
- 黑体的是
攻略主题
, 灰色部分的是此主题对应的目的地名集合
准备工作
数据库表结构
-
攻略明细数据表(分时统计表从这里拿所需的数据)
-
分时统计表
-
必须要有statis_time字段,用于表示统计录入时间,便于作为用户查询条件拿到最新的数据!
实体类
- 攻略明细实体类,画红线才是此演示所需属性
- 分时统计表对应的实体类
- 因为此演示中,分时统计表中的目的地名会有多个值,对应实体类destNames和destIds, 会显示成类似于
1,2,3,4
,所以定义了一个pareseXXX()方法,将这些字符串分割成集合,便于后续封装数据操作
@Getter
@Setter
@ToString
@TableName("strategy_theme_rank")
public class StrategyThemeRank extends BaseDomain {
private Long themeId;
private String themeName;
private String destNames;
private String destIds;
private Long count;
private Date statisTime;
public List<Long> parseDestIds(){
List<Long> ids = new ArrayList<>();
if(StringUtils.hasLength(destIds)){
String[] split = destIds.split(",");
if(split != null && split.length > 0){
for (int i = 0;i <split.length; i++) {
ids.add(Long.parseLong(split[i]));
}
}
}
return ids;
}
public List<String> parseDestNames(){
List<String> names = new ArrayList<>();
if(StringUtils.hasLength(destNames)){
String[] split = destNames.split(",");
if(split != null && split.length > 0){
for (int i = 0;i <split.length; i++) {
names.add(split[i]);
}
}
}
return names;
}
}
vo类
- 真正要返回给前端的数据
- 实现流程就是,先从
攻略明细表
拿到所需的数据放进分时统计表
中,在此表再将数据封装进vo里返回给前端 - 所以前端是直接从
分时统计表
拿数据,这些数据封装成vo数据 - ps: 有些情况可以无需用到vo类封装数据,直接拿
分时统计表
对应的数据就可以返回给页面了!(具体看需求)
分时统计 实现流程
控制器层
-
注入
分时统计表
的业务层对象
-
控制器接口直接调用此业务层对象方法,将数据封装成vo数据返回给前端即可!
业务层实现方法
- 通过条件statis_time(分时统计表最新的数据)最大值的数据,并用count(热门量)倒序分组,获取
分时统计表
内的数据,即可拿到排行榜的数据 - 将数据封装成vo类型数据给前端即可
定时器操作
- 引入
攻略明细表
和分时统计表
的业务层对象 - 将前端页面所需的
攻略主题排行榜
数据,从攻略明细表
拿到后装进分时统计表
内 - 因为是排行榜的数据,所以需要对
攻略明细表
根据对数据的需要对相关字段进行分组和聚合函数操作,按热度量进行倒序排序,并拿前10名数据(即最热门的前10名数据), - 通过cron表达式,设置每隔多久会进行一次操作(更新排行榜数据)
@Component
public class StrategyThemeRankStatisJob {
@Autowired
private IStrategyService strategyService;
@Autowired
private IStrategyThemeRankService strategyThemeRankService;
@Scheduled(cron = "0/10 * * * * ?")
public void doWork() {
strategyThemeRankService.remove(new QueryWrapper<>());
/**
* select theme_id,theme_name,GROUP_CONCAT(dest_name) name,GROUP_CONCAT(dest_id) ids,count(id) count
* from strategy group by theme_id,theme_name
* order by count desc limit 10
*/
Date date = new Date();
List<StrategyThemeRank> rankList = new ArrayList<>();
QueryWrapper<Strategy> wrapper = new QueryWrapper<>();
wrapper.select("theme_id, theme_name, GROUP_CONCAT(DISTINCT dest_name) name,GROUP_CONCAT(dest_id) ids,count(id) count")
.groupBy("theme_id")
.orderByDesc("count")
.last("limit 10");
List<Map<String, Object>> maps = strategyService.listMaps(wrapper);
for (Map<String,Object> map : maps){
StrategyThemeRank rank = new StrategyThemeRank();
rank.setStatisTime(date);
rank.setCount(Long.parseLong(map.get("count").toString()));
rank.setDestIds(map.get("ids").toString());
rank.setDestNames(map.get("name").toString());
rank.setThemeId(Long.parseLong(map.get("theme_id").toString()));
rank.setThemeName(map.get("theme_name").toString());
rankList.add(rank);
}
strategyThemeRankService.saveBatch(rankList);
}
}