四、略微复杂的quartz的使用:动态添加修改定时任务
设想一个项目场景:
在一个项目中,我需要动态的向项目中添加或者修改定时任务,还有暂停和启动的操作,相当于动态的添加定时任务,如果有这种需求 的话呢?你打算怎么做?
4.1、quartz的固化(本案例不使用,仅作为学习)
这样的话,我们就需要将定时任务集成到数据库中了,在这方面,quartz给我们做了固化,具体的操作很简单:
在项目的配置文件(application.yml)中加入如下配置;
其中,always,代表每次启动项目都会给数据库添加quartz的表,它里边还有另外两个属性:never、embedded;
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC
username: root
password: 123456
quartz:
job-store-type: jdbc
jdbc:
initialize-schema: always
加入之后,我们再启动项目的时候,会看到:日志中会有这么几行字:具体的意思就是,现在的quartz是以本地持久化的形式来的,不是在内存中的,并且没有集群;
现在的话,你的数据库中,就会多了几张表:分别存储着不同的信息;
https://github.com/wangmaoxiong/quartzjdbc/tree/master/src/main/resources/sql
4.2、自己创建表进行jobDetail和trigger的动态添加修改:
先给大家贴出一个实体 SettleTaskPlan ,后续说明作用:
public class SettleTaskPlan extends EntityBase implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value="SETTLE_TASK_ID", type = IdType.INPUT)
@ExcelProperty(value = "序号",index = 0)
private java.lang.Long settleTaskId;
@TableField("APP_CODE")
@ExcelProperty(value = "应用编码",index = 19)
private java.lang.String appCode;
@TableField("CHECK_REMARK")
@ExcelProperty(value = "审核意见",index = 13)
private java.lang.String checkRemark;
@TableField("CHECK_STATE")
@ExcelProperty(value = "审核状态",index = 14)
private java.lang.String checkState;
@TableField("CHECK_TIME")
@ExcelProperty(value = "审核时间",index = 15)
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private java.util.Date checkTime;
@TableField("CHECK_USER_ID")
@ExcelProperty(value = "审核人id",index = 12)
private java.lang.Long checkUserId;
@TableField("CRON_EXPRESS")
@ExcelProperty(value = "执行表达式",index = 3)
private java.lang.String cronExpress;
@TableField("DEL_FLAG")
@ExcelProperty(value = "删除标记",index = 16)
private java.lang.String delFlag;
@TableField("DISABLE_FLAG")
@ExcelProperty(value = "禁用标记",index = 17)
private java.lang.String disableFlag;
@TableField("EXEC_TIMES")
@ExcelProperty(value = "执行次数",index = 9)
private java.lang.Long execTimes;
@TableField("EXECUTE_FUN")
private java.lang.String executeFun;
@TableField("EXECUTE_JSON_PARAM")
private java.lang.String executeJsonParam;
@TableField("FAILED_TIMES")
@ExcelProperty(value = "失败次数",index = 10)
private java.lang.Long failedTimes;
@TableField("LAST_EXECUTED")
@ExcelProperty(value = "最后一次执行时间",index = 7)
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private java.util.Date lastExecuted;
@TableField("LAST_STATE")
@ExcelProperty(value = "最后一次执行状态",index = 8)
private java.lang.String lastState;
@TableField("REMARK")
@ExcelProperty(value = "备注",index = 18)
private java.lang.String remark;
@TableField("START_EXECUTED")
@ExcelProperty(value = "开始时间",index = 4)
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private java.util.Date startExecuted;
@TableField("SUCCESS_TIMES")
@ExcelProperty(value = "成功次数",index = 11)
private java.lang.Long successTimes;
@TableField("TASK_CODE")
@ExcelProperty(value = "计划编码",index = 1)
private java.lang.String taskCode;
@TableField("TASK_NAME")
@ExcelProperty(value = "计划名称",index = 2)
private java.lang.String taskName;
@TableField("TASK_TYPE")
@ExcelProperty(value = "计划类型",index = 5)
private java.lang.String taskType;
@TableField("TRIGGER_FUN")
@ExcelProperty(value = "模块代码",index = 6)
private java.lang.String triggerFun;
@TableField("SETTLE_OBJECT")
private String settleObject;
前边已经介绍了简单的quartz的应用,相信大家肯定都可以自己手动创建一个定时任务并且跑起来了;
但是想要完成动态的添加修改,呢么应该怎么做呢?
我们可以想一下我们的需求,动态的添加修改;我们再看看前边文章的内容,前边的文章,我们介绍了如何在springboot里边添加一个定时任务,呢么是不是我们只需要再知道如何去修改和删除定时任务,呢么我们的需求就完成了呢?
下边介绍一下如何对程序中的定时任务进行修改和删除:
定时任务的修改:
/**
* 根据核算计划修改定时任务
*
* @param settleTaskPlan
* @return
* @throws SchedulerException
*/
public boolean updateQuartzPlan(SettleTaskPlan settleTaskPlan) throws SchedulerException {
TriggerKey triggerKey = TriggerKey.triggerKey(settleTaskPlan.getSettleTaskId().toString());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
Trigger trigger1 = trigger.getTriggerBuilder()
.withIdentity(triggerKey)
.withSchedule(CronScheduleBuilder.cronSchedule(settleTaskPlan.getCronExpress()))
.build();
scheduler.rescheduleJob(triggerKey, trigger1);
return true;
}
定时任务的添加:
/**
* 根据核算计划新增一个定时任务详情到quartz中
*
* @param settleTaskPlan
* @return
* @throws SchedulerException
*/
public boolean addQuartzPlan(SettleTaskPlan settleTaskPlan) throws SchedulerException {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setJobFactory(new MyAdptableFactory());
//创建一个jobDetail
JobDetail jobDetail = JobBuilder.newJob(QuartzDemo.class)
.withIdentity(settleTaskPlan.getSettleTaskId().toString())
.build();
//创建对应的触发器
Trigger trigger = TriggerBuilder.newTrigger()
.startNow()
.withIdentity(settleTaskPlan.getSettleTaskId().toString())
.usingJobData("settleTaskId", settleTaskPlan.getSettleTaskId().toString())
.withSchedule(CronScheduleBuilder.cronSchedule(settleTaskPlan.getCronExpress()))
.build();
//将触发器和jobDetail联合起来
scheduler.scheduleJob(jobDetail, trigger);
return true;
}
定时任务的删除:
/**
* 按照核算计划主键将定时计划从quartz中剔除掉
*
* @param settleTaskPlans
* @return
*/
public boolean deleteQuartzPlan(List<SettleTaskPlan> settleTaskPlans) throws SchedulerException {
for (SettleTaskPlan settleTaskPlan : settleTaskPlans) {
//将对应的jobdetail删除掉
JobKey jobKey = JobKey.jobKey(settleTaskPlan.getSettleTaskId().toString());
scheduler.deleteJob(jobKey);
}
return true;
}
同时我们应该再去思考一个问题,我们如何将这些定时任务存储起来呢?总不能我添加完在下次启动的时候就没有了吧?这是不合理的。
所以我们要在数据库建立一张表SettleTaskPlan ,将定时任务存储起来,定时任务的分组命名信息就是表的主键,这样我们就可以根据表的主键创建修改删除对应的定时任务了;
表中最主要的信息就是两个:标识符(主键),cron表达式,其余的可以根据自己的需要自行添加;
建立好表之后,我们只需要在系统的每次启动前,循环访问数据库添加对应的定时任务就可以了:
public class QuartzConfig {
//TODO:两种思路,一种是给每一个事件都创建一个jobDetail和一个触发器,另一种是给一个jobDetail创建多个触发器
@Autowired
SettleTaskPlanService settleTaskPlanService;
@Autowired
public Scheduler scheduler;
//在每次启动项目的时候,去循环数据库中的定时任务然后启动这些定时任务
@PostConstruct
public void initJob()throws Exception{
//从数据库中查询所有的定时任务并且一个一个启动
QueryWrapper<SettleTaskPlan> queryWrapper=new QueryWrapper();
queryWrapper.eq("DEL_FLAG","0");
queryWrapper.eq("DISABLE_FLAG","0");
queryWrapper.eq("CHECK_STATE","1");
List<SettleTaskPlan> list = settleTaskPlanService.list(queryWrapper);
//用自己写的jobFactory覆盖本来的factory实现将job加入到spring容器中
SchedulerFactoryBean schedulerFactoryBean=new SchedulerFactoryBean();
schedulerFactoryBean.setJobFactory(new MyAdptableFactory());
for (SettleTaskPlan taskPlan : list) {
//创建一个jobDetail
JobDetail jobDetail= JobBuilder.newJob(QuartzDemo.class)
.withIdentity(taskPlan.getSettleTaskId().toString())
.build();
//创建对应的触发器
Trigger trigger=TriggerBuilder.newTrigger()
.startNow()
.withIdentity(taskPlan.getSettleTaskId().toString())
.usingJobData("settleTaskId",taskPlan.getSettleTaskId().toString())
.withSchedule(CronScheduleBuilder.cronSchedule(taskPlan.getCronExpress()))
.build();
//将触发器和jobDetail联合起来
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
}
}
}