基于数据库优点:服务器或者项目重启后,任务会自动重启接续(已错过时间的任务会立即执行)。
一、Quartz基础
建议通读一遍这个教程,熟悉Quartz基本概念。
https://www.w3cschool.cn/quartz_doc/
我们需要明白 Quartz 的几个核心概念,这样理解起 Quartz 的原理就会变得简单了。
- Job 表示一个工作,要执行的具体内容。此接口中只有一个方法,如下:
void execute(JobExecutionContext context)
- JobDetail 表示一个具体的可执行的调度程序,Job 是这个可执行程调度程序所要执行的内容,另外 JobDetail 还包含了这个任务调度的方案和策略。
- Trigger 代表一个调度参数的配置,什么时候去调。
- Scheduler 代表一个调度容器,一个调度容器中可以注册多个 JobDetail 和 Trigger。当 Trigger 与 JobDetail 组合,就可以被 Scheduler 容器调度了。
二、导入依赖(按需导入)
<!-- 必须 定时任务--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> <!-- 根据自己数据导入 mysql依赖--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.28</version> <scope>runtime</scope> </dependency> <!-- 根据自己习惯导入 数据连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <!-- 根据自己需要导入 ORM框架--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.3.2</version> </dependency> <!-- 根据自己需要导入 JSON--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.67</version> </dependency> <!-- 根据自己需要导入 swagger--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> /dependency>
三、配置
配置文件
spring: #数据库配置信息 datasource: url: jdbc:mysql://localhost:3306/xxxx?useUnicode=true&characterEncoding=utf-8 username: xxxxxx type: com.alibaba.druid.pool.DruidDataSource password: xxxxxxx quartz: job-store-type: jdbc #数据库方式 jdbc: initialize-schema: never #不初始化表结构 properties: org: quartz: scheduler: instanceId: AUTO #默认主机名和时间戳生成实例ID,可以是任何字符串,但对于所有调度程序来说,必须是唯一的 对应qrtz_scheduler_state INSTANCE_NAME字段 #instanceName: clusteredScheduler #quartzScheduler jobStore: class: org.quartz.impl.jdbcjobstore.JobStoreTX #持久化配置 driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate #我们仅为数据库制作了特定于数据库的代理 useProperties: false #以指示JDBCJobStore将JobDataMaps中的所有值都作为字符串,因此可以作为名称 - 值对存储而不是在BLOB列中以其序列化形式存储更多复杂的对象。从长远来看,这是更安全的,因为您避免了将非String类序列化为BLOB的类版本问题。 tablePrefix: qrtz_ #数据库表前缀 misfireThreshold: 60000 #在被认为“失火”之前,调度程序将“容忍”一个Triggers将其下一个启动时间通过的毫秒数。默认值(如果您在配置中未输入此属性)为60000(60秒)。 clusterCheckinInterval: 5000 #设置此实例“检入”*与群集的其他实例的频率(以毫秒为单位)。影响检测失败实例的速度。 isClustered: true #打开群集功能 threadPool: #连接池 class: org.quartz.simpl.SimpleThreadPool threadCount: 10 threadPriority: 5 threadsInheritContextClassLoaderOfInitializingThread: true
initialize-schema:
always:使用在这个选项,每次系统重启,软件会自动创建下边的数据表。
never:使用在这个选项,软件不会自动创建上边的数据表。
embedded:目前未知
我的处理方式配置为:always系统创建后,改为never;
手动建表SQL,initialize-schema: 先配置为always运行后会自动创建!!!
/* Navicat Premium Data Transfer Source Server : localhost Source Server Type : MySQL Source Server Version : 50733 Source Host : localhost:3306 Source Schema : fence Target Server Type : MySQL Target Server Version : 50733 File Encoding : 65001 Date: 26/04/2021 16:34:12 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for qrtz_blob_triggers -- ---------------------------- DROP TABLE IF EXISTS `qrtz_blob_triggers`; CREATE TABLE `qrtz_blob_triggers` ( `SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `TRIGGER_NAME` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `BLOB_DATA` blob, PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE, INDEX `SCHED_NAME`(`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE, CONSTRAINT `qrtz_blob_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `qrtz_triggers` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Table structure for qrtz_calendars -- ---------------------------- DROP TABLE IF EXISTS `qrtz_calendars`; CREATE TABLE `qrtz_calendars` ( `SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `CALENDAR_NAME` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `CALENDAR` blob NOT NULL, PRIMARY KEY (`SCHED_NAME`, `CALENDAR_NAME`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Table structure for qrtz_cron_triggers -- ---------------------------- DROP TABLE IF EXISTS `qrtz_cron_triggers`; CREATE TABLE `qrtz_cron_triggers` ( `SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `TRIGGER_NAME` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `CRON_EXPRESSION` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `TIME_ZONE_ID` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE, CONSTRAINT `qrtz_cron_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `qrtz_triggers` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Table structure for qrtz_fired_triggers -- ---------------------------- DROP TABLE IF EXISTS `qrtz_fired_triggers`; CREATE TABLE `qrtz_fired_triggers` ( `SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `ENTRY_ID` varchar(95) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `TRIGGER_NAME` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `INSTANCE_NAME` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `FIRED_TIME` bigint(13) NOT NULL, `SCHED_TIME` bigint(13) NOT NULL, `PRIORITY` int(11) NOT NULL, `STATE` varchar(16) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `JOB_NAME` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `JOB_GROUP` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `IS_NONCONCURRENT` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `REQUESTS_RECOVERY` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, PRIMARY KEY (`SCHED_NAME`, `ENTRY_ID`) USING BTREE, INDEX `IDX_QRTZ_FT_TRIG_INST_NAME`(`SCHED_NAME`, `INSTANCE_NAME`) USING BTREE, INDEX `IDX_QRTZ_FT_INST_JOB_REQ_RCVRY`(`SCHED_NAME`, `INSTANCE_NAME`, `REQUESTS_RECOVERY`) USING BTREE, INDEX `IDX_QRTZ_FT_J_G`(`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) USING BTREE, INDEX `IDX_QRTZ_FT_JG`(`SCHED_NAME`, `JOB_GROUP`) USING BTREE, INDEX `IDX_QRTZ_FT_T_G`(`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE, INDEX `IDX_QRTZ_FT_TG`(`SCHED_NAME`, `TRIGGER_GROUP`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Table structure for qrtz_job_details -- ---------------------------- DROP TABLE IF EXISTS `qrtz_job_details`; CREATE TABLE `qrtz_job_details` ( `SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `JOB_NAME` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `JOB_GROUP` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `DESCRIPTION` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `JOB_CLASS_NAME` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `IS_DURABLE` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `IS_NONCONCURRENT` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `IS_UPDATE_DATA` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `REQUESTS_RECOVERY` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `JOB_DATA` blob, PRIMARY KEY (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) USING BTREE, INDEX `IDX_QRTZ_J_REQ_RECOVERY`(`SCHED_NAME`, `REQUESTS_RECOVERY`) USING BTREE, INDEX `IDX_QRTZ_J_GRP`(`SCHED_NAME`, `JOB_GROUP`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Table structure for qrtz_locks -- ---------------------------- DROP TABLE IF EXISTS `qrtz_locks`; CREATE TABLE `qrtz_locks` ( `SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `LOCK_NAME` varchar(40) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, PRIMARY KEY (`SCHED_NAME`, `LOCK_NAME`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Table structure for qrtz_paused_trigger_grps -- ---------------------------- DROP TABLE IF EXISTS `qrtz_paused_trigger_grps`; CREATE TABLE `qrtz_paused_trigger_grps` ( `SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, PRIMARY KEY (`SCHED_NAME`, `TRIGGER_GROUP`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Table structure for qrtz_scheduler_state -- ---------------------------- DROP TABLE IF EXISTS `qrtz_scheduler_state`; CREATE TABLE `qrtz_scheduler_state` ( `SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `INSTANCE_NAME` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `LAST_CHECKIN_TIME` bigint(13) NOT NULL, `CHECKIN_INTERVAL` bigint(13) NOT NULL, PRIMARY KEY (`SCHED_NAME`, `INSTANCE_NAME`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Table structure for qrtz_simple_triggers -- ---------------------------- DROP TABLE IF EXISTS `qrtz_simple_triggers`; CREATE TABLE `qrtz_simple_triggers` ( `SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `TRIGGER_NAME` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `REPEAT_COUNT` bigint(7) NOT NULL, `REPEAT_INTERVAL` bigint(12) NOT NULL, `TIMES_TRIGGERED` bigint(10) NOT NULL, PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE, CONSTRAINT `qrtz_simple_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `qrtz_triggers` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Table structure for qrtz_simprop_triggers -- ---------------------------- DROP TABLE IF EXISTS `qrtz_simprop_triggers`; CREATE TABLE `qrtz_simprop_triggers` ( `SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `TRIGGER_NAME` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `STR_PROP_1` varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `STR_PROP_2` varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `STR_PROP_3` varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `INT_PROP_1` int(11) DEFAULT NULL, `INT_PROP_2` int(11) DEFAULT NULL, `LONG_PROP_1` bigint(20) DEFAULT NULL, `LONG_PROP_2` bigint(20) DEFAULT NULL, `DEC_PROP_1` decimal(13, 4) DEFAULT NULL, `DEC_PROP_2` decimal(13, 4) DEFAULT NULL, `BOOL_PROP_1` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `BOOL_PROP_2` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE, CONSTRAINT `qrtz_simprop_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `qrtz_triggers` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Table structure for qrtz_triggers -- ---------------------------- DROP TABLE IF EXISTS `qrtz_triggers`; CREATE TABLE `qrtz_triggers` ( `SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `TRIGGER_NAME` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `TRIGGER_GROUP` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `JOB_NAME` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `JOB_GROUP` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `DESCRIPTION` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `NEXT_FIRE_TIME` bigint(13) DEFAULT NULL, `PREV_FIRE_TIME` bigint(13) DEFAULT NULL, `PRIORITY` int(11) DEFAULT NULL, `TRIGGER_STATE` varchar(16) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `TRIGGER_TYPE` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `START_TIME` bigint(13) NOT NULL, `END_TIME` bigint(13) DEFAULT NULL, `CALENDAR_NAME` varchar(190) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `MISFIRE_INSTR` smallint(2) DEFAULT NULL, `JOB_DATA` blob, PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE, INDEX `IDX_QRTZ_T_J`(`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) USING BTREE, INDEX `IDX_QRTZ_T_JG`(`SCHED_NAME`, `JOB_GROUP`) USING BTREE, INDEX `IDX_QRTZ_T_C`(`SCHED_NAME`, `CALENDAR_NAME`) USING BTREE, INDEX `IDX_QRTZ_T_G`(`SCHED_NAME`, `TRIGGER_GROUP`) USING BTREE, INDEX `IDX_QRTZ_T_STATE`(`SCHED_NAME`, `TRIGGER_STATE`) USING BTREE, INDEX `IDX_QRTZ_T_N_STATE`(`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`, `TRIGGER_STATE`) USING BTREE, INDEX `IDX_QRTZ_T_N_G_STATE`(`SCHED_NAME`, `TRIGGER_GROUP`, `TRIGGER_STATE`) USING BTREE, INDEX `IDX_QRTZ_T_NEXT_FIRE_TIME`(`SCHED_NAME`, `NEXT_FIRE_TIME`) USING BTREE, INDEX `IDX_QRTZ_T_NFT_ST`(`SCHED_NAME`, `TRIGGER_STATE`, `NEXT_FIRE_TIME`) USING BTREE, INDEX `IDX_QRTZ_T_NFT_MISFIRE`(`SCHED_NAME`, `MISFIRE_INSTR`, `NEXT_FIRE_TIME`) USING BTREE, INDEX `IDX_QRTZ_T_NFT_ST_MISFIRE`(`SCHED_NAME`, `MISFIRE_INSTR`, `NEXT_FIRE_TIME`, `TRIGGER_STATE`) USING BTREE, INDEX `IDX_QRTZ_T_NFT_ST_MISFIRE_GRP`(`SCHED_NAME`, `MISFIRE_INSTR`, `NEXT_FIRE_TIME`, `TRIGGER_GROUP`, `TRIGGER_STATE`) USING BTREE, CONSTRAINT `qrtz_triggers_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) REFERENCES `qrtz_job_details` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;
四、核心service类、Job类
可以根据自己业务需求,修改Trigger的触发方式实现类Simple
Triggerhttps://www.w3cschool.cn/quartz_doc/quartz_doc-67a52d1f.html CronTriggerhttps://www.w3cschool.cn/quartz_doc/quartz_doc-lwuv2d2a.html
Simple Trigger更适合传入一个具体时间执行,然后设置重复多少次;CronTrigger适合使用Cron表达式执行的重复任务;配合使用startTime和endTime可实现大多数业务。
package com.xxxx.service; import org.quartz.*; import org.quartz.impl.matchers.GroupMatcher; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.quartz.QuartzJobBean; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.util.*; import static org.quartz.TriggerBuilder.newTrigger; @Service public class QuartzService { @Autowired private Scheduler scheduler; @PostConstruct public void startScheduler() { try { scheduler.start(); } catch (SchedulerException e) { e.printStackTrace(); } } /** * 增加一个job * * @param jobClass * 任务实现类 * @param jobName * 任务名称 * @param jobGroupName * 任务组名 * @param jobTime * 时间表达式 * @param jobData * 参数 */ public void addJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, Long jobTime, Map jobData) { try { // 任务名称和组构成任务key JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName) .build(); // 设置job参数 if(jobData!= null && jobData.size()>0){ jobDetail.getJobDataMap().putAll(jobData); } // 使用simpleTrigger规则 Trigger trigger = null; // if (jobTimes < 0) { // trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName) // .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withIntervalInSeconds(jobTime)) // .startNow().build(); // } else { trigger =(SimpleTrigger)newTrigger() .withIdentity(jobName, jobGroupName) .startAt(new Date(jobTime)).build(); // } scheduler.scheduleJob(jobDetail, trigger); } catch (SchedulerException e) { e.printStackTrace(); } } /** * 增加一个job * * @param jobClass * 任务实现类 * @param jobName * 任务名称(建议唯一) * @param jobGroupName * 任务组名 * @param jobTime * 时间表达式 (如:0/5 * * * * ? ) * @param jobData * 参数 */ public void addJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, String jobTime, Map jobData) { try { // 创建jobDetail实例,绑定Job实现类 // 指明job的名称,所在组的名称,以及绑定job类 // 任务名称和组构成任务key JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName) .build(); // 设置job参数 if(jobData!= null && jobData.size()>0){ jobDetail.getJobDataMap().putAll(jobData); } // 定义调度触发规则 // 使用cornTrigger规则 // 触发器key Trigger trigger = newTrigger().withIdentity(jobName, jobGroupName) .startAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.SECOND)) .withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).startNow().build(); // 把作业和触发器注册到任务调度中 scheduler.scheduleJob(jobDetail, trigger); } catch (Exception e) { e.printStackTrace(); } } /** * 修改 一个job的 时间表达式 * * @param jobName * @param jobGroupName * @param jobTime */ public void updateJob(String jobName, String jobGroupName, String jobTime) { try { TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); trigger = trigger.getTriggerBuilder().withIdentity(triggerKey) .withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).build(); // 重启触发器 scheduler.rescheduleJob(triggerKey, trigger); } catch (SchedulerException e) { e.printStackTrace(); } } /** * 修改 一个job的 未来时间 * * @param jobName * @param jobGroupName * @param jobTime */ public void updateJob(String jobName, String jobGroupName,Long jobTime) { try { TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName); // CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); // trigger = trigger.getTriggerBuilder().withIdentity(triggerKey) // .withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).build(); SimpleTrigger trigger =(SimpleTrigger)newTrigger() .withIdentity(jobName, jobGroupName) .startAt(new Date(jobTime)).build(); // 重启触发器 scheduler.rescheduleJob(triggerKey, trigger); } catch (SchedulerException e) { e.printStackTrace(); } } /** * 删除任务一个job * * @param jobName * 任务名称 * @param jobGroupName * 任务组名 */ public void deleteJob(String jobName, String jobGroupName) { try { scheduler.deleteJob(new JobKey(jobName, jobGroupName)); } catch (Exception e) { e.printStackTrace(); } } /** * 暂停一个job * * @param jobName * @param jobGroupName */ public void pauseJob(String jobName, String jobGroupName) { try { JobKey jobKey = JobKey.jobKey(jobName, jobGroupName); scheduler.pauseJob(jobKey); } catch (SchedulerException e) { e.printStackTrace(); } } /** * 恢复一个job * * @param jobName * @param jobGroupName */ public void resumeJob(String jobName, String jobGroupName) { try { JobKey jobKey = JobKey.jobKey(jobName, jobGroupName); scheduler.resumeJob(jobKey); } catch (SchedulerException e) { e.printStackTrace(); } } /** * 立即执行一个job * * @param jobName * @param jobGroupName */ public void runAJobNow(String jobName, String jobGroupName) { try { JobKey jobKey = JobKey.jobKey(jobName, jobGroupName); scheduler.triggerJob(jobKey); } catch (SchedulerException e) { e.printStackTrace(); } } /** * 获取所有计划中的任务列表 * * @return */ public List<Map<String, Object>> queryAllJob() { List<Map<String, Object>> jobList = null; try { GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup(); Set<JobKey> jobKeys = scheduler.getJobKeys(matcher); jobList = new ArrayList<Map<String, Object>>(); for (JobKey jobKey : jobKeys) { List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey); for (Trigger trigger : triggers) { Map<String, Object> map = new HashMap<>(); map.put("jobName", jobKey.getName()); map.put("jobGroupName", jobKey.getGroup()); map.put("description", "触发器:" + trigger.getKey()); Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey()); map.put("jobStatus", triggerState.name()); if (trigger instanceof CronTrigger) { CronTrigger cronTrigger = (CronTrigger) trigger; String cronExpression = cronTrigger.getCronExpression(); map.put("jobTime", cronExpression); } jobList.add(map); } } } catch (SchedulerException e) { e.printStackTrace(); } return jobList; } /** * 获取所有正在运行的job * * @return */ public List<Map<String, Object>> queryRunJob() { List<Map<String, Object>> jobList = null; try { List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs(); jobList = new ArrayList<Map<String, Object>>(executingJobs.size()); for (JobExecutionContext executingJob : executingJobs) { Map<String, Object> map = new HashMap<String, Object>(); JobDetail jobDetail = executingJob.getJobDetail(); JobKey jobKey = jobDetail.getKey(); Trigger trigger = executingJob.getTrigger(); map.put("jobName", jobKey.getName()); map.put("jobGroupName", jobKey.getGroup()); map.put("description", "触发器:" + trigger.getKey()); Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey()); map.put("jobStatus", triggerState.name()); if (trigger instanceof CronTrigger) { CronTrigger cronTrigger = (CronTrigger) trigger; String cronExpression = cronTrigger.getCronExpression(); map.put("jobTime", cronExpression); } jobList.add(map); } } catch (SchedulerException e) { e.printStackTrace(); } return jobList; } }
package com.xxx.quartz.job; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON; import com.xxx.mapper.TaskMapper; import com.xxx.model.Task; import lombok.extern.slf4j.Slf4j; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.quartz.QuartzJobBean; @Slf4j public class Job extends QuartzJobBean { @Autowired TaskMapper taskMapper; @Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { // 获取参数 JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap(); Integer id=jobDataMap.getInt("id"); //业务逻辑 } }
五、控制层业务;
建议:不建议研究quartz数据表及关系,建议自己再新建一个表,控制任务情况。大神忽略即可。
package com.xxxxx.controller; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.lets.common.AjaxJson; import com.lets.mapper.TaskMapper; import com.lets.model.Task; import com.lets.quartz.job.Job; import com.lets.service.DefenceService; import com.lets.service.QuartzService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Range; import org.quartz.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.web.bind.annotation.*; import javax.validation.constraints.Min; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.quartz.SimpleScheduleBuilder.simpleSchedule; import static org.quartz.TriggerBuilder.newTrigger; @RestController @RequestMapping("/admin/task") @Api(tags = "定时任务") public class AdminTaskController { @Autowired QuartzService quartzService; @Autowired TaskMapper taskMapper; @GetMapping("/add") @ApiOperation("添加一个任务") @ApiImplicitParams({ @ApiImplicitParam(name = "name", value = "任务名称", defaultValue = "任务简单描述", required = true), @ApiImplicitParam(name = "type", value = "命令类型 xxxxxx", defaultValue = "", required = true), @ApiImplicitParam(name = "timeType", value = "时间类型1为具体时间 时间戳2为周期任务", defaultValue = "1", required = true), @ApiImplicitParam(name = "timeStr", value = "13位时间戳或cron表达式", required = true), @ApiImplicitParam(name = "parameter", value = "任务为7 8 9时须传递参数", required = false) }) public AjaxJson add(@NotBlank @RequestParam String name, @NotBlank @RequestParam String type, @NotNull @RequestParam Integer timeType, @NotBlank @RequestParam String timeStr, String parameter) { Task task = new Task(); String currentTimeMillis = System.currentTimeMillis() + ""; task.setName(name); task.setJobName("name:" + currentTimeMillis); task.setJobGropName("Group:" + currentTimeMillis); task.setType(type); task.setTimeType(timeType); task.setTime(timeStr); if (parameter != null) { task.setParameter(parameter); } //传入参数校验 boolean check = checkTask(task); if (check) { task.setTastState(1); taskMapper.insert(task); HashMap<String, Object> hm = new HashMap<>(); hm.put("job_name", task.getJobName()); hm.put("job_grop_name", task.getJobGropName()); hm.put("id", task.getId()); if (parameter != null) { hm.put("parameter", task.getParameter()); } if (timeType == 1) { quartzService.addJob(Job.class, task.getJobName(), task.getJobGropName(), Long.valueOf(timeStr), hm); } else if (timeType == 2) { quartzService.addJob(Job.class, task.getJobName(), task.getJobGropName(), timeStr, hm); } return AjaxJson.ok("任务添加成功"); } else { return AjaxJson.fail("传入参数非法"); } } @GetMapping("/edit") @ApiOperation("更新一个任务") @ApiImplicitParams({ @ApiImplicitParam(name = "id", value = "任务id", defaultValue = "1"), @ApiImplicitParam(name = "timeStr", value = "13位时间戳或cron表达式", required = true), }) public AjaxJson edit(Integer id, @NotBlank @RequestParam String timeStr) { System.out.println(timeStr); Task task = taskMapper.selectById(id); task.setTime(timeStr); if (task.getTimeType() == 1) { quartzService.updateJob(task.getJobName(), task.getJobGropName(), Long.valueOf(timeStr)); } else if (task.getTimeType() == 2) { quartzService.updateJob(task.getJobName(), task.getJobGropName(), timeStr); } taskMapper.updateById(task); return AjaxJson.ok("更新任务成功"); } @GetMapping("/push") @ApiOperation("暂停一个任务") @ApiImplicitParam(name = "id", value = "任务id", defaultValue = "1") public AjaxJson push(Integer id) { Task task = taskMapper.selectById(id); System.out.println(JSON.toJSONString(task)); quartzService.pauseJob(task.getJobName(), task.getJobGropName()); task.setTastState(2); taskMapper.updateById(task); return AjaxJson.ok("暂停成功"); } @GetMapping("/pushall") @ApiOperation("暂停所有任务") public AjaxJson pushall() { QueryWrapper<Task> taskQueryWrapper=new QueryWrapper<>(); taskQueryWrapper.ne("tast_state",2); List<Task> tasks = taskMapper.selectList(taskQueryWrapper); if (tasks.isEmpty()) { return AjaxJson.ok("当前可暂停任务为空"); }else { for (int i = 0; i < tasks.size(); i++) { quartzService.pauseJob(tasks.get(i).getJobName(), tasks.get(i).getJobGropName()); tasks.get(i).setTastState(2); taskMapper.updateById(tasks.get(i)); } } return AjaxJson.ok("暂停成功"); } @GetMapping("/resume") @ApiOperation("恢复一个任务") @ApiImplicitParam(name = "id", value = "任务id", defaultValue = "1") public AjaxJson resumeJob(Integer id) { Task task = taskMapper.selectById(id); quartzService.resumeJob(task.getJobName(), task.getJobGropName()); task.setTastState(1); taskMapper.updateById(task); return AjaxJson.ok("恢复成功"); } @GetMapping("/resumeall") @ApiOperation("恢复全部任务") public AjaxJson resumeJob() { QueryWrapper<Task> taskQueryWrapper=new QueryWrapper<>(); taskQueryWrapper.eq("tast_state",2); List<Task> tasks = taskMapper.selectList(taskQueryWrapper); if (tasks.isEmpty()) { return AjaxJson.ok("当前可恢复任务为空"); }else { for (int i = 0; i < tasks.size(); i++) { quartzService.resumeJob(tasks.get(i).getJobName(), tasks.get(i).getJobGropName()); tasks.get(i).setTastState(1); taskMapper.updateById(tasks.get(i)); } } return AjaxJson.ok("恢复全部成功"); } @GetMapping("/delete") @ApiOperation("删除一个任务") @ApiImplicitParam(name = "id", value = "任务id", defaultValue = "1") public AjaxJson deleteJob(Integer id) { Task task = taskMapper.selectById(id); quartzService.deleteJob(task.getJobName(), task.getJobGropName()); taskMapper.deleteById(id); return AjaxJson.ok("删除成功"); } @GetMapping("/deletebyname") @ApiOperation("删除一个任务(按照名称、分组)") @ApiImplicitParams({ @ApiImplicitParam(name = "jobName", value = "任务名称", defaultValue = "name:1619402010684"), @ApiImplicitParam(name = "jobGropName", value = "任务分组", defaultValue = "Group:1619402010684") }) public AjaxJson deleteJobByName(String jobName, String jobGropName) { quartzService.deleteJob(jobName, jobGropName); return AjaxJson.ok("删除成功"); } @GetMapping("/deleteall") @ApiOperation("删除全部任务") public AjaxJson deleteAll() { List<Task> tasks = taskMapper.selectList(null); if (!tasks.isEmpty()) { for (int i = 0; i < tasks.size(); i++) { quartzService.deleteJob(tasks.get(i).getJobName(), tasks.get(i).getJobGropName()); taskMapper.deleteById(tasks.get(i).getId()); } return AjaxJson.ok("删除全部成功"); } return AjaxJson.ok("可删除任务为空"); } @GetMapping("/findall") @ApiOperation("获取所有计划中的任务列表") @ApiImplicitParams({ @ApiImplicitParam(name = "currentpage", value = "页码", defaultValue = "1",required = true), @ApiImplicitParam(name = "pagesize", value = "每页条数", defaultValue = "10",required = true) }) public AjaxJson findAllJob(@Min(value = 1) @RequestParam Integer currentpage, @Min(value = 1) @RequestParam(defaultValue = "10") Integer pagesize) { Page<Task> page = new Page<>(currentpage, pagesize); QueryWrapper<Task> taskQW = new QueryWrapper<>(); taskQW.orderByDesc("id"); Page<Task> pages = taskMapper.selectPage(page, taskQW); return AjaxJson.ok(pages); } //检查参数合法性 private boolean checkTask(Task task) { //检查是否携带参数 String type = task.getType(); if(type.length()!=2){ System.out.println("!=2"); return false; } if (type.equals("07") || type.equals("08") || type.equals("09")) { if (task.getParameter() == null) { return false; } } Integer timeType = task.getTimeType(); if (timeType == 1) { if (task.getTime().length() != 13) { return false; } } if (timeType == 2) { String[] s = task.getTime().split(" "); if (s.length != 6) { System.out.println("!=6"); return false; } } if(timeType != 1 && timeType!= 2){; return false; } return true; } }
封装的返回类
/** * Copyright © 2005-2020 <a href="http://www.jhmis.com/">jhmis</a> All rights reserved. */ package com.xxxx.common; import com.fasterxml.jackson.annotation.JsonIgnore; import java.util.LinkedHashMap; import java.util.List; /** * $.ajax后需要接受的JSON * */ public class AjaxJson { private boolean success = true;// 是否成功 private String errorCode = "-1";//错误代码 private String msg = "操作成功";// 提示信息 private Long count; //返回表格记录数量 private List<?> data; //返回表格数据 private LinkedHashMap<String, Object> body = new LinkedHashMap<String, Object>();//封装json的map public static AjaxJson ok(){ AjaxJson j = new AjaxJson(); return j; } public static AjaxJson ok(String msg){ AjaxJson j = new AjaxJson(); j.setMsg(msg); return j; } public static AjaxJson ok(String msg, Object object){ AjaxJson j = new AjaxJson(); j.setMsg(msg); j.setResult(object); return j; } public static AjaxJson ok(Object object){ AjaxJson j = new AjaxJson(); j.setResult(object); return j; } public static AjaxJson fail(String errorMsg){ AjaxJson j = new AjaxJson(); j.setSuccess(false); j.setErrorCode("999");//默认错误码 j.setMsg(errorMsg); return j; } public static AjaxJson fail(String errorCode,String errorMsg){ AjaxJson j = new AjaxJson(); j.setSuccess(false); j.setErrorCode(errorCode); j.setMsg(errorMsg); return j; } //返回不分页的layui表数据 public static AjaxJson layuiTable(List<?> list){ AjaxJson j = new AjaxJson(); j.setSuccess(true); j.setCount(Long.valueOf(list.size())); j.setData(list); return j; } public LinkedHashMap<String, Object> getBody() { return body; } public void setBody(LinkedHashMap<String, Object> body) { this.body = body; } public void put(String key, Object value){//向json中添加属性,在js中访问,请调用data.map.key body.put(key, value); } public void remove(String key){ body.remove(key); } /** * 直接设置result内容 * @param result */ public void setResult(Object result){ body.put("result", result); } @JsonIgnore//返回对象时忽略此属性 public Object getResult(){ return body.get("result"); } public String getMsg() { return msg; } public void setMsg(String msg) {//向json中添加属性,在js中访问,请调用data.msg this.msg = msg; } public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } public void setErrorCode(String errorCode) { this.errorCode = errorCode; } public String getErrorCode() { return errorCode; } public Long getCount() { return count; } public void setCount(Long count) { this.count = count; } public List<?> getData() { return data; } public void setData(List<?> data) { this.data = data; } }
自定义的任务实体类
package com.xxxx.model; import java.io.Serializable; import java.util.Date; import com.baomidou.mybatisplus.annotation.*; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; /** * task * @author */ @Data public class Task implements Serializable { /** * 自增id */ @TableId(type = IdType.AUTO) private Integer id; /** * 任务名称 */ private String name; /** * 任务创建人员 */ private String nickname; /** * 操作员id */ private Integer userId; /** * 任务创建时间 */ @TableField(fill = FieldFill.INSERT) @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") private Date createTime; /** * 时间类型1具体时间 2为cron表达式周期时间 */ private Integer timeType; /** * 任务执行时间 */ private String time; /** * jobGropName */ private String jobGropName; /** * 参数 */ private String parameter; /** * jobName */ private String jobName; /** * 备注 */ private String remark; /** * 命令类型 */ private String type; /** * 是否删除 */ @TableLogic private Byte isdelete; /** * 1开始 2为暂停 3已执行 */ private Integer tastState; private static final long serialVersionUID = 1L; }
实体类对应的sql
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for task -- ---------------------------- DROP TABLE IF EXISTS `task`; CREATE TABLE `task` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增id', `name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '任务名称', `nickname` varchar(16) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '任务创建人员', `user_id` int(11) DEFAULT NULL COMMENT '操作员id', `create_time` datetime(0) DEFAULT NULL COMMENT '任务创建时间', `time_type` tinyint(2) DEFAULT NULL COMMENT '时间类型1具体时间 2为cron表达式周期时间', `time` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '任务执行时间', `job_grop_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT 'jobGropName', `parameter` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '参数', `job_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT 'jobName', `remark` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '备注', `type` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '命令类型', `isdelete` tinyint(2) DEFAULT 0 COMMENT '是否删除\r\n是否删除', `tast_state` tinyint(2) DEFAULT NULL COMMENT '1开始 2为暂停 3已执行 ', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;
数据库操作
package com.xxxx.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.lets.model.Task; public interface TaskMapper extends BaseMapper<Task> { }
六、写在最后
@Api。。。。。的注解都是Swagger的,有兴趣可以查看:https://blog.csdn.net/qq_29752857/article/details/113390619
欢迎大家一起讨论,如果能帮到你,我很开心。
吐槽一下百度,搜索技术类问题,前几页的答案几乎都是一样的,大家可以试试其他的搜搜。