前言
本文主要是Springboot集成Quartz实现定时任务的数据库存储以及前端页面的可视化管理,所使用的主要工具有:Springboot、Mybatis-Plus、Quartz、Layui、Mysql等
正文
1.准备环境
1.创建数据库
因为本例子中业务数据和定时数据是分开的,所以先创建两个空数据库,一个用于业务,一个用于Quartz,数据库名字任意,我的是:bml_base(业务)、bml_quartz(定时)
2.创建项目
创建一个Springboot项目,勾选一些必需依赖,例如druid、quartz、web、mysql、Mybatis-plus等,创建后删除一些不必要的文件(mvn那些),这些都是基础知识,具体略…
3.application.yml配置文件
注意:
1.这里我把quartz配置与Springboot的配置都放在application.yml一个文件中了,这和网上的分开配置有一些不同。
2.initialize-schema: never,我们第一次启动项目时可以将never改成always,这样Quartz会自动在bml_quartz数据库中创建相关表,我们也不用再导入脚本了
# mybatis-plus
mybatis-plus:
# SQL语句输出在控制台
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 映射文件相关
mapper-locations: classpath*:bml/business/mapper/xml/*.xml
type-aliases-package: bml.business.mapper
spring:
datasource:
# 业务数据源
db1:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/bml_base?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
# Quartz数据源
db2:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/bml_quartz?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
quartz:
properties:
org:
quartz:
scheduler:
# 默认
instanceName: DemoScheduler
# 如果使用集群,instanceId必须唯一,设置成AUTO
instanceId: AUTO
jobStore:
# 存储方式使用JobStoreTX 也就是数据库
class: org.quartz.impl.jdbcjobstore.JobStoreTX
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# 表名前缀
tablePrefix: QRTZ_
# 是否使用集群
isClustered: true
clusterCheckinInterval: 10000
useProperties: false
misfireThreshold: 5000
threadPool:
class: org.quartz.simpl.SimpleThreadPool
threadCount: 20
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
job-store-type: jdbc
jdbc:
# 启动项目时是否初始化表
initialize-schema: never
mvc:
# 静态资源文件过滤
static-path-pattern: /static/**
thymeleaf:
# 禁用缓存
cache: false
4.数据库配置类
使用多数据源时要配置一下,编写如下配置类即可:
@Configuration
public class DataSourceConfig {
@Bean(name = "db1")
@Primary
@ConfigurationProperties("spring.datasource.db1")
public DruidDataSource dataSource1 () {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "db2")
@ConfigurationProperties("spring.datasource.db2")
@QuartzDataSource
public DruidDataSource dataSource2 () {
return DruidDataSourceBuilder.create().build();
}
}
5.截图展示:
自此,搭建环境的工作就告一段落了…
2.业务实现(非重点)
本例子中,业务实现不是重点,这里只是简单地模拟一下实际开发环境,我们在业务数据中创建一个表,然后将表中数据展示在前台即可。
1.创建表
在bml_base(业务)数据库中任意创建一个表,插入一些数据,例如:
REATE TABLE `user` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`username` varchar(32) DEFAULT NULL COMMENT '用户名',
`password` varchar(256) DEFAULT NULL COMMENT '密码',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
2.Mybatis-Plus实现
快速实现:使用Mybatis-plus逆向工程快速生成entity、dao、service等,将业务模块都放在business包下,参考上面的图,编写Controller查询所有数据返回给前台即可,注意数据格式是JSON。
Controller如下:
@Controller
public class UserController {
@Resource
UserService userService;
@ResponseBody
@PostMapping("/admin/user")
public BmlResult<Object> userList() {
Map<String, Object> resultMap = new HashMap<>(100);
resultMap.put("count",userService.count(null));
resultMap.put("data",userService.list(null));
return new BmlResult<>(resultMap,0);
}
}
BmlResult是数据封装类,下文会经常使用,代码如下:
@Data
public class BmlResult<T> implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 封装数据
*/
private T data;
/**
* 返回状态码
*/
private Integer status;
/**
* 返回自定义信息
*/
private String msg;
/**
* 无参构造,不可或缺
*/
public BmlResult() {
}
/**
* 如没有数据返回,则可以自定义指定状态码和提示信息
*/
public BmlResult(Integer status, String msg) {
this.status = status;
this.msg = msg;
}
/**
* 用于返回查询的集合
* @param data 封装数据
* @param status 状态码
*/
public BmlResult(T data,Integer status) {
this.data = data;
this.status = status;
}
}
3.定时任务实现(重点)
1.创建任务
参考项目结构图:创建两个任务
Job1:输出id为1的用户名和当前时间,当然,实际开发远没有这么简单,你也可以做点别的
package bml.quartz.job;
public class OneJob extends QuartzJobBean {
@Resource
UserService userService;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
User user = userService.getById(1);
System.out.println("第一任务输出用户名:"+user.getUsername()+"当前时间:"+dateFormat.format(new Date()));
}
}
Job2:统计一下业务表User中共有多少数据
package bml.quartz.job;
public class TwoJob implements Job {
@Resource
UserService userService;
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
int count = userService.count(null);
System.out.println("模拟午夜统计总人数"+dateFormat.format(new Date())+"用户总数是:"+count);
}
}
注意:job1和job2实现方式不一样,一个是extends QuartzJobBean,一个是implements Job,这两种方式都行
2.创建VO
我们要在前端展示任务列表,就要新建一个用于保存数据的实体类,新建类如下:
package bml.quartz.entity;
@Data
public class QuartzJobVO {
private String jobName;
private String jobGroup;
private String jobClassName;
private String description;
private String triggerName;
private String triggerGroup;
private String triggerState;
private String cronExpression;
private String timeZone;
}
3.实现方法
类似于Service层,实现定时任务的CRUD方法:
package bml.quartz.service;
@Configuration
public class QuartzService {
@Resource
Scheduler scheduler;
public void prepareJob(Scheduler scheduler) throws SchedulerException {
JobDetail jobDetail1 = newJob(OneJob.class)
.withIdentity("job1", "group1")
.withDescription("任务一描述:每隔10秒输出用户一的用户名")
.build();
JobDetail jobDetail2 = newJob(TwoJob.class)
.withIdentity("job2", "group1")
.withDescription("任务二描述: 模拟每天午夜时统计一下总人数")
.build();
CronTrigger cronTrigger1 = newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(
CronScheduleBuilder.cronSchedule("0/10 * 10-23 * * ?")
.withMisfireHandlingInstructionDoNothing())
.build();
CronTrigger cronTrigger2 = newTrigger()
.withIdentity("trigger2", "group1")
.startNow()
.withSchedule(
CronScheduleBuilder.cronSchedule("0/30 * 10-23 * * ?")
.withMisfireHandlingInstructionDoNothing())
.build();
scheduler.scheduleJob(jobDetail1, cronTrigger1);
scheduler.scheduleJob(jobDetail2, cronTrigger2);
}
public void startJob() throws SchedulerException {
prepareJob(scheduler);
scheduler.start();
}
/**
* 获取任务列表 用于前端展示
*/
public List<QuartzJobVO> getList() throws SchedulerException {
List<QuartzJobVO> list = new ArrayList<>();
// 再获取Scheduler下的所有group
List<String> triggerGroupNames = scheduler.getTriggerGroupNames();
for (String groupName : triggerGroupNames) {
// 组装group的匹配,为了模糊获取所有的triggerKey或者jobKey
GroupMatcher groupMatcher = GroupMatcher.groupEquals(groupName);
// 获取所有的triggerKey
Set<TriggerKey> triggerKeySet = scheduler.getTriggerKeys(groupMatcher);
for (TriggerKey triggerKey : triggerKeySet) {
// 通过triggerKey在scheduler中获取trigger对象
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 获取trigger拥有的Job
JobKey jobKey = trigger.getJobKey();
JobDetailImpl jobDetail = (JobDetailImpl) scheduler.getJobDetail(jobKey);
// 组装页面需要显示的数据
QuartzJobVO quartzJobsVO = new QuartzJobVO();
quartzJobsVO.setJobName(jobDetail.getName());
quartzJobsVO.setJobGroup(groupName);
quartzJobsVO.setDescription(jo