当前位置: 首页 > news >正文

Quartz入门教程

本文参考文章编写

Quartz

官网

Quartz 是 OpenSymphony 开源组织在 Job Scheduling 领域又一个开源项目,是完全由 Java 开发的一个开源任务日程管理系统,“任务进度管理器”就是一个在预先确定(被纳入日程)的时间到达时,负责执行(或者通知)其他软件组件的系统。 Quartz 是一个开源的作业调度框架,它完全由 Java 写成,并设计用于 J2SE 和 J2EE 应用中,它提供了巨大的灵活性而不牺牲简单性

应用场景

在日常的开发过程中,简单的定时任务,比如每天凌晨对数据进行备份,这样的场景,我们使用 Spring 提供的注解 @Schedule 就可以满足业务,但是对于一些复杂的任务调度场景,比如日程提醒,从日程中取消,开始,停止,暂停日程进度,这样复杂一些的场景,@Schedule就无法满足了,此时就需要用到Quartz ,它可以持久性作业(即,保持调度定时的状态)作业管理 (即,对调度作业进行有效的管理)

组成部分

在这里插入图片描述

  • SchedulerQuartz 中的任务调度器,通过 Trigger(触发器)JobDetail(任务详情) 可以用来调度、暂停和删除任务。调度器就相当于一个容器,装载着任务和触发器,该类是一个接口,代表一个 Quartz 的独立运行容器,Trigger(触发器)JobDetail(任务详情) 可以注册到 Scheduler 中,这样当Trigger(触发器)被触发时,对应的Job就会执行,两者在 Scheduler 中拥有各自的组及名称,组及名称是 Scheduler 查找定位容器中某一对象的依据,Trigger(触发器) 的组及名称必须唯一,JobDetail(任务详情) 的组和名称也必须唯一,但二者的组名称可以一致,因为它们是不同类型的
  • TriggerQuartz 中的触发器,是一个类,描述触发 Job(任务) 执行的时间触发规则,主要有 SimpleTrigger CronTrigger这两个子类。当且仅当需调度一次或者以固定时间间隔周期执行调度,SimpleTrigger 是最适合的选择;而 CronTrigger 则可以通过 Cron 表达式定义出各种复杂时间规则的调度方案:如工作日周一到周五的 15:00 ~ 16:00 执行调度等
  • JobDetailQuartz 中需要执行的任务详情,包括了任务的唯一标识和具体要执行的任务,可以通过 JobDataMap 往任务中传递数据,JobDetail绑定指定的Job,每次Scheduler调度执行一个Job的时候,首先会拿到对应的Job,然后创建该Job实例,再去执行Job中的execute()的内容,任务执行结束后,关联的Job对象实例会被释放,且会被JVM GC清除
  • JobQuartz 中具体的任务,包含了执行任务的具体方法。是一个接口,只定义一个方法execute()方法,在实现接口的 execute() 方法中编写业务逻辑
  • JobExecutionContext:JobExecutionContext中包含了Quartz运行时的环境以及Job本身的详细数据信息。当Schedule调度执行一个Job的时候,就会将JobExecutionContext传递给该Job的execute()中,Job就可以通过JobExecutionContext对象获取信息。

Cron 表达式

Cron表达式在线生成

Cron 表达式是一个字符串,包括 6~7 个时间元素,在 Quartz 中可以用于指定任务的执行时间

  • Cron 语法
秒 分 小时 日 月 周 年例:
0/2 * * * * ?   表示每2秒 执行任务
0 0/2 * * * ?    表示每2分钟 执行任务
时间元素可出现的字符有效数值范围
, - * /0-59
, - * /0-59
小时, - * /0-23
, - * / ? L W0-31
, - * /1-12
, - * / ? L #1-7或SUN-SAT
, - * /当前年份-大于等于当前年份(例:2023-2024)
  • Cron 语法中特殊字符说明
字符作用举例
,列出枚举值在分钟域使用5,10,表示在5分和10分各触发一次
-表示触发范围在分钟域使用5-10,表示从5分到10分钟每分钟触发一次
*匹配任意值在分钟域使用*, 表示每分钟都会触发一次
/起始时间开始触发,每隔固定时间触发一次在分钟域使用5/10,表示5分时触发一次,每10分钟再触发一次
?在日和周中,用于匹配任意值在日域使用?,表示每天都触发一次
#在周中,确定第几周星期几1#2 表示第1周 的星期2
L表示最后在周中,4L表示本月最后一个星期4
W表示有效工作日(周一到周五)2W,表示每月 2号最近的那个工作日

Quartz基于数据库存储任务信息

为了保证可以动态的添加,删除任务,Quartz 存储任务信息有两种方式,使用内存或者使用数据库来存储

数据库SQL脚本官网下载地址

在这里插入图片描述

  • 解压下载后的quartz-2.3.0-distribution.tar.gz文件,在quartz-2.3.0-distribution\quartz-2.3.0-SNAPSHOT\src\org\quartz\impl\jdbcjobstore目录下得到各类型数据库的SQL脚本
    在这里插入图片描述

  • Quartz 目前支持的数据库
    cloudscape数据库:一个以 Java 类库形式提供的、轻量级的、可嵌入的关系引擎。与其他的关系数据库系统类似。Cloudscape 也有事务(提交和回滚),并提供了崩溃恢复。与其他数据库一样,它允许多个线程共享同一连接
    cubrid数据库:是一个全面开源,且完全免费的关系数据库管理系统
    db2数据库:DB2就是IBM开发的一种大型关系型数据库平台
    derby数据库:一个完全用java编写的数据库,非常小巧,既可以做为单独的数据库服务器使用,也可以内嵌在应用程序中使用
    firebird数据库:一个真正的关系数据库,支持存储过程、视图、触发器、事务等大型关系数据库的所有特性
    h2数据库:h2是一个开源的、纯java实现的关系数据库。支持网络版和嵌入式版本,另外还提供了内存版,支持相当标准的sql标准,提供了非常友好的基于web的数据库管理界面
    hsqldb数据库:一个开放源代码的JAVA数据库,其具有标准的SQL语法和JAVA接口
    informix数据库:是IBM公司出品的关系数据库管理系统(RDBMS)家族。作为一个集成解决方案,它被定位为作为IBM在线事务处理(OLTP)旗舰级数据服务系统
    mysql数据库:一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,是最流行的关系型数据库管理系统之一
    oracle数据库:是甲骨文公司的一款关系数据库管理系统。它是一种高效率的、可靠性好的、适应高吞吐量的数据库方案。
    pointbase数据库:是由oracle的创始人之一的Bruce Scott开发的一套小型数据库。
    postgresql数据库:是一种特性非常齐全的自由软件的对象-关系型数据库管理系统
    sapdb数据库:sapdb是SAP公司开发的开源数据库,结构和Oracle非常近似
    solid数据库:solid数据库是一款“轻量级”的数据库,是标准的 关系型数据库,支持 SQL / ACID / 事务隔离级别等标准,也支持存储过程、 触发器、事件等。
    SQL Server数据库:美国Microsoft公司推出的一种关系型数据库系统。支持对称多处理器结构、存储过程、ODBC,并具有自主的SQL语言
    sybase数据库:美国Sybase公司研制的一种关系型数据库系统,是一种典型的UNIX或WindowsNT平台上客户机/服务器环境下的大型数据库系统。

  • 使用脚本tables_mysql.sql创建的表概述(此处字段中的注释为后续补充,官网脚本并未提供)

1.qrtz_blob_triggers表:自定义的triggers使用blog类型进行存储,非自定义的triggers不会存放在此表中,Quartz提供的triggers包括:CronTriggerCalendarIntervalTriggerDailyTimeIntervalTrigger以及SimpleTrigger,这几个trigger信息会保存在后面的几张表中

CREATE TABLE `qrtz_blob_triggers`  (`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '调度器名称',`TRIGGER_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器名称,qrtz_triggers表trigger_name的外键',`TRIGGER_GROUP` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器群组,qrtz_triggers表trigger_group的外键',`BLOB_DATA` blob NULL COMMENT '任务信息,存放持久化job对象',PRIMARY KEY (`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 COMMENT = 'Trigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型,JobStore 并不知道如何存储实例的时候)' ROW_FORMAT = Dynamic;

2.qrtz_calendars表Quartz为我们提供了日历的功能,可以自己定义一个时间段,可以控制触发器在这个时间段内触发或者不触发;现在提供6种类型:AnnualCalendarCronCalendarDailyCalendarHolidayCalendarMonthlyCalendarWeeklyCalendar

CREATE TABLE `qrtz_calendars`  (`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '调度器名称',`CALENDAR_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器名称',`CALENDAR` blob NOT NULL COMMENT '日历信息,存放持久化calendar对象',PRIMARY KEY (`SCHED_NAME`, `CALENDAR_NAME`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '以 Blob 类型存储存放日历信息, quartz可配置一个日历来指定一个时间范围。' ROW_FORMAT = Dynamic;

3.qrtz_cron_triggers表:存放cron类型的触发器

CREATE TABLE `qrtz_cron_triggers`  (`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '调度器名称',`TRIGGER_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器名称,qrtz_triggers表trigger_name的外键',`TRIGGER_GROUP` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器组名称,qrtz_triggers表trigger_group的外键',`CRON_EXPRESSION` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'cron表达式',`TIME_ZONE_ID` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '时区',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 COMMENT = '存储触发器的cron表达式' ROW_FORMAT = Dynamic;

4.qrtz_fired_triggers表:存储已经触发的trigger相关信息,trigger随着时间的推移状态发生变化,直到最后trigger执行完成,从表中被删除;

CREATE TABLE `qrtz_fired_triggers`  (`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '调度器名称',`ENTRY_ID` varchar(95) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '调度器实例id',`TRIGGER_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器名称,qrtz_triggers表trigger_name的外键',`TRIGGER_GROUP` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器组,qrtz_triggers表trigger_group的外键',`INSTANCE_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '调度器实例名',`FIRED_TIME` bigint(13) NOT NULL COMMENT '触发时间',`SCHED_TIME` bigint(13) NOT NULL COMMENT '定时器制定的时间',`PRIORITY` int(11) NOT NULL COMMENT '优先级',`STATE` varchar(16) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '状态',`JOB_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '集群中job的名字,该名字用户自己可以随意定制,无强行要求',`JOB_GROUP` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '集群中job的所属组的名字,该名字用户自己随意定制,无强行要求',`IS_NONCONCURRENT` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '是否并发',`REQUESTS_RECOVERY` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '是否接受恢复执行,默认为false,设置了RequestsRecovery为true,则会被重新执行',PRIMARY KEY (`SCHED_NAME`, `ENTRY_ID`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '存储与已触发的 Trigger 相关的状态信息,以及相联 Job 的执行信息。' ROW_FORMAT = Dynamic;

5.qrtz_job_details表:存储jobDetails信息,相关信息在定义的时候指定

CREATE TABLE `qrtz_job_details`  (`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '调度器名称',`JOB_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '任务名称',`JOB_GROUP` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '任务群组',`DESCRIPTION` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '关于任务的说明',`JOB_CLASS_NAME` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '任务class全路径,quartz就是根据这个路径到classpath找到该job类',`IS_DURABLE` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '是否为持久,把该属性设置为1,quartz会把job持久化到数据库中',`IS_NONCONCURRENT` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '是否并发',`IS_UPDATE_DATA` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '是否更新数据',`REQUESTS_RECOVERY` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '是否接受恢复执行,默认为false,设置了RequestsRecovery为true,则该job会被重新执行',`JOB_DATA` blob NULL COMMENT '任务信息,存放持久化job对象',PRIMARY KEY (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '存储每一个已配置的 jobDetail 的详细信息' ROW_FORMAT = Dynamic;

6.qrtz_locks表Quartz提供的锁表,存储程序的悲观锁的信息(假如使用了悲观锁),为多个节点调度提供分布式锁,实现分布式调度,默认有2个锁;
STATE_ACCESS主要用在scheduler定期检查是否失效的时候,保证只有一个节点去处理已经失效的scheduler
TRIGGER_ACCESS主要用在TRIGGER被调度的时候,保证只有一个节点去执行调度;

CREATE TABLE `qrtz_locks`  (`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '调度器名称',`LOCK_NAME` varchar(40) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '悲观锁名称',PRIMARY KEY (`SCHED_NAME`, `LOCK_NAME`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '存储程序的悲观锁的信息(假如使用了悲观锁)' ROW_FORMAT = Dynamic;

7.qrtz_paused_trigger_grps表:存放暂停掉的触发器

CREATE TABLE `qrtz_paused_trigger_grps`  (`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '调度器名称',`TRIGGER_GROUP` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器组,qrtz_triggers表trigger_group的外键',PRIMARY KEY (`SCHED_NAME`, `TRIGGER_GROUP`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '存储已暂停的 Trigger 组的信息' ROW_FORMAT = Dynamic;

8.qrtz_scheduler_state表:存储所有节点的scheduler,会定期检查scheduler是否失效,记录了最后最新的检查时间,在quartz.properties中设置了CHECKIN_INTERVAL为1000,也就是每秒检查一次;

CREATE TABLE `qrtz_scheduler_state`  (`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '调度器名称',`INSTANCE_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '实例名称,配置文件中org.quartz.scheduler.instanceId配置的名字,会写入该字段',`LAST_CHECKIN_TIME` bigint(13) NOT NULL COMMENT '上次检查时间',`CHECKIN_INTERVAL` bigint(13) NOT NULL COMMENT '检查间隔时间',PRIMARY KEY (`SCHED_NAME`, `INSTANCE_NAME`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '存储集群中note实例信息,quartz会定时读取该表的信息判断集群中每个实例的当前状态' ROW_FORMAT = Dynamic;

9.qrtz_simple_triggers表:存储简单的触发器信息,包括重复次数,间隔,以及已触的次数

CREATE TABLE `qrtz_simple_triggers`  (`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '调度器名称',`TRIGGER_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器名称,qrtz_triggers表trigger_ name的外键',`TRIGGER_GROUP` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器组,qrtz_triggers表trigger_group的外键',`REPEAT_COUNT` bigint(7) NOT NULL COMMENT '重复的次数统计',`REPEAT_INTERVAL` bigint(12) NOT NULL COMMENT '重复的间隔时间',`TIMES_TRIGGERED` bigint(10) NOT NULL COMMENT '已经触发的次数',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 COMMENT = '存储简单的 Trigger,包括重复次数,间隔,以及已触发的次数。' ROW_FORMAT = Dynamic;

10.qrtz_simprop_triggers表:存储CalendarIntervalTriggerDailyTimeIntervalTrigger两种类型的触发器

CREATE TABLE `qrtz_simprop_triggers`  (`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '调度器名称',`TRIGGER_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器名称,qrtz_triggers表trigger_ name的外键',`TRIGGER_GROUP` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器群组,qrtz_triggers表trigger_group的外键',`STR_PROP_1` varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'String类型的trigger的第一个参数',`STR_PROP_2` varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'String类型的trigger的第二个参数',`STR_PROP_3` varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'String类型的trigger的第三个参数',`INT_PROP_1` int(11) NULL DEFAULT NULL COMMENT 'int类型的trigger的第一个参数',`INT_PROP_2` int(11) NULL DEFAULT NULL COMMENT 'int类型的trigger的第二个参数',`LONG_PROP_1` bigint(20) NULL DEFAULT NULL COMMENT 'long类型的trigger的第一个参数',`LONG_PROP_2` bigint(20) NULL DEFAULT NULL COMMENT 'long类型的trigger的第二个参数',`DEC_PROP_1` decimal(13, 4) NULL DEFAULT NULL COMMENT 'decimal类型的trigger的第一个参数',`DEC_PROP_2` decimal(13, 4) NULL DEFAULT NULL COMMENT 'decimal类型的trigger的第二个参数',`BOOL_PROP_1` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Boolean类型的trigger的第一个参数',`BOOL_PROP_2` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'Boolean类型的trigger的第二个参数',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 COMMENT = '存储CalendarIntervalTrigger和DailyTimeIntervalTrigger' ROW_FORMAT = Dynamic;

11.qrtz_triggers表:存储已配置的 Trigger 的信息,一个Job可以被多个Trigger绑定,但是一个Trigger只能绑定一个Job

CREATE TABLE `qrtz_triggers`  (`SCHED_NAME` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '调度器名称',`TRIGGER_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器名称',`TRIGGER_GROUP` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器组名称',`JOB_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '任务名称,qrtz_job_details表job_name的外键',`JOB_GROUP` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '任务群组,qrtz_job_details表job_group的外键',`DESCRIPTION` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '关于触发器的说明信息',`NEXT_FIRE_TIME` bigint(13) NULL DEFAULT NULL COMMENT '下次执行时间,默认为-1,意味不会自动触发',`PREV_FIRE_TIME` bigint(13) NULL DEFAULT NULL COMMENT '上次执行时间(毫秒)',`PRIORITY` int(11) NULL DEFAULT NULL COMMENT '线程优先级',`TRIGGER_STATE` varchar(16) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '当前触发器状态;WAITING-等待, PAUSED-暂停,ACQUIRED-正常执行 ,BLOCKED-阻塞 ERROR-错误',`TRIGGER_TYPE` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器类型,使用cron表达式',`START_TIME` bigint(13) NOT NULL COMMENT '开始时间',`END_TIME` bigint(13) NULL DEFAULT NULL COMMENT '结束时间',`CALENDAR_NAME` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '日程表名称,表qrtz_calendars的calendar_name字段外键',`MISFIRE_INSTR` smallint(2) NULL DEFAULT NULL COMMENT 'misfire处理规则,1代表【以当前时间为触发频率立刻触发一次,然后按照Cron频率依次执行】,\n   2代表【不触发立即执行,等待下次Cron触发频率到达时刻开始按照Cron频率依次执行�】,\n   -1代表【以错过的第一个频率时间立刻开始执行,重做错过的所有频率周期后,当下一次触发频率发生时间大于当前时间后,再按照正常的Cron频率依次执行',`JOB_DATA` blob NULL COMMENT '任务信息,存放持久化job对象',PRIMARY KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) USING BTREE,INDEX `SCHED_NAME`(`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) 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 COMMENT = '存储已配置的触发器 的信息' ROW_FORMAT = Dynamic;

SpringBoot整合Quartz

  • 创建数据库test_quartztables_mysql.sql导入并运行
  • 导入Maven依赖(这里使用 druid 作为数据库连接池,Quartz 默认使用 c3p0
   <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- quartz --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId><version>2.3.5.RELEASE</version></dependency><!--druid连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version></dependency><!-- mybatis-plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3.4</version></dependency><!--胡图工具包--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.2</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>
  • 新建配置文件quartz.properties

默认情况下,Quartz 会加载 classpath 下的 quartz.properties 作为配置文件。如果找不到,则会使用 quartz 框架自己 jar 包下 org/quartz 底下的 quartz.properties 文件

quartz.properties

#定时任务实例的id 默认自动
org.quartz.scheduler.instanceId=AUTO
# 定时任务的线程名,相同集群实例名称必须相同
org.quartz.scheduler.instanceName=DefaultQuartzScheduler
#如果您希望Quartz Scheduler通过RMI作为服务器导出本身,则将“rmi.export”标志设置为true
#在同一个配置文件中为'org.quartz.scheduler.rmi.export'和'org.quartz.scheduler.rmi.proxy'指定一个'true'值是没有意义的,如果你这样做'export'选项将被忽略
org.quartz.scheduler.rmi.export=false
#如果要连接(使用)远程服务的调度程序,则将“org.quartz.scheduler.rmi.proxy”标志设置为true。您还必须指定RMI注册表进程的主机和端口 - 通常是“localhost”端口1099
org.quartz.scheduler.rmi.proxy=false
#如果想使用Quartz在执行一个job前使用UserTransaction,则应该设置该属性为true。job执行完、在JobDataMap改变之后事务会提交。默认值是false。 可以在你的job类中使用 @ExecuteInJTATransaction注解, 可以控制job是否使用事务。
org.quartz.scheduler.wrapJobExecutionInUserTransaction=false#实例化ThreadPool时,使用的线程类为SimpleThreadPool
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
#threadCount和threadPriority将以setter的形式注入ThreadPool实例
#并发个数  如果你只有几个工作每天触发几次 那么1个线程就可以,如果你有成千上万的工作,每分钟都有很多工作 那么久需要50-100之间.
#只有1到100之间的数字是非常实用的
org.quartz.threadPool.threadCount=5
#优先级 默认值为5
org.quartz.threadPool.threadPriority=5
#可以是“true”或“false”,默认为false
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true#在被认为“misfired”(失火)之前,调度程序将“tolerate(容忍)”一个Triggers(触发器)将其下一个启动时间通过的毫秒数。默认值(如果您在配置中未输入此属性)为60000(60秒)
org.quartz.jobStore.misfireThreshold=5000
# 默认存储在内存中,RAMJobStore快速轻便,但是当进程终止时,所有调度信息都会丢失
#org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore#持久化方式,默认存储在内存中,此处使用数据库方式
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
#您需要为JobStore选择一个DriverDelegate才能使用。DriverDelegate负责执行特定数据库可能需要的任何JDBC工作
# StdJDBCDelegate是一个使用“vanilla”JDBC代码(和SQL语句)来执行其工作的委托,用于完全符合JDBC的驱动程序
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#可以将“org.quartz.jobStore.useProperties”配置参数设置为“true”(默认为false),以指示JDBCJobStore将JobDataMaps中的所有值都作为字符串,
#因此可以作为名称 - 值对存储而不是在BLOB列中以其序列化形式存储更多复杂的对象。从长远来看,这是更安全的,因为您避免了将非String类序列化为BLOB的类版本问题
org.quartz.jobStore.useProperties=true
#表前缀
org.quartz.jobStore.tablePrefix=qrtz_
#数据源别名,自定义
org.quartz.jobStore.dataSource=qzDS#使用阿里的druid作为数据库连接池
org.quartz.dataSource.qzDS.connectionProvider.class=com.wf.test_quartz_demo.config.DruidPoolingConnectionProvider
org.quartz.dataSource.qzDS.URL=jdbc:mysql://127.0.0.1:3306/test_quartz?characterEncoding=utf8&useSSL=false&autoReconnect=true&serverTimezone=UTC
org.quartz.dataSource.qzDS.user=root
org.quartz.dataSource.qzDS.password=root
org.quartz.dataSource.qzDS.driver=com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.maxConnections=10
#设置为“true”以打开群集功能。如果您有多个Quartz实例使用同一组数据库表,则此属性必须设置为“true”,否则您将遇到破坏
#org.quartz.jobStore.isClustered=false

注:以上配置中需要修改的地方有
org.quartz.dataSource.qzDS.connectionProvider.class:这是druid连接池的配置类,需要修改为自己的路径
org.quartz.dataSource.qzDS.URL:需要修改为自己的数据库连接
org.quartz.dataSource.qzDS.user:需要修改为自己的数据库用户名
org.quartz.dataSource.qzDS.password:需要修改为自己的数据库密码

application.properties:

server.port=8081
# mysql配置
spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.druid.url=jdbc:mysql://127.0.0.1:3306/test_quartz?characterEncoding=utf8&useSSL=false&autoReconnect=true&serverTimezone=UTC
spring.datasource.druid.username=root
spring.datasource.druid.password=root
#连接池初始化大小,最小,最大
spring.datasource.druid.initial-size=3
spring.datasource.druid.min-idle=3
spring.datasource.druid.max-active=10
#连接池最大等待时间
spring.datasource.druid.max-wait=60000
# mybatis-plus配置
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.mapper-locations=classpath*:/mapper/*.xml
mybatis-plus.type-aliases-package=com.wf.test_quartz_demo.bean
  • 编写Quartz配置类
package com.wf.test_quartz_demo.config;import com.sun.istack.internal.NotNull;
import org.quartz.Scheduler;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;import java.io.IOException;
import java.util.Properties;/*** @Description: quartz 配置类*/
@Configuration
public class QuartzConfig implements SchedulerFactoryBeanCustomizer {@Beanpublic Properties properties() throws IOException {PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();// 对quartz.properties文件进行读取propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));// 在quartz.properties中的属性被读取并注入后再初始化对象propertiesFactoryBean.afterPropertiesSet();return propertiesFactoryBean.getObject();}@Beanpublic SchedulerFactoryBean schedulerFactoryBean() throws IOException {SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();schedulerFactoryBean.setQuartzProperties(properties());return schedulerFactoryBean;}/** 通过SchedulerFactoryBean获取Scheduler的实例*/@Beanpublic Scheduler scheduler() throws IOException {return schedulerFactoryBean().getScheduler();}/*** 使用阿里的druid作为数据库连接池*/@Overridepublic void customize(@NotNull SchedulerFactoryBean schedulerFactoryBean) {schedulerFactoryBean.setStartupDelay(2);schedulerFactoryBean.setAutoStartup(true);schedulerFactoryBean.setOverwriteExistingJobs(true);}}
  • 编写连接池配置类
package com.wf.test_quartz_demo.config;import com.alibaba.druid.pool.DruidDataSource;
import lombok.Data;
import org.quartz.SchedulerException;
import org.quartz.utils.ConnectionProvider;import java.sql.Connection;
import java.sql.SQLException;/*** @Description: druid连接池配置*/
@Data
public class DruidPoolingConnectionProvider implements ConnectionProvider {// JDBC驱动public String driver;// JDBC连接串public String URL;// 数据库用户名public String user;// 数据库用户密码public String password;// 数据库最大连接数public int maxConnections;// 数据库SQL查询每次连接返回执行到连接池,以确保它仍然是有效的。public String validationQuery;private boolean validateOnCheckout;private int idleConnectionValidationSeconds;public String maxCachedStatementsPerConnection;private String discardIdleConnectionsSeconds;public static final int DEFAULT_DB_MAX_CONNECTIONS = 10;public static final int DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION = 120;// Druid连接池private DruidDataSource datasource;@Overridepublic Connection getConnection() throws SQLException {return datasource.getConnection();}@Overridepublic void shutdown() {datasource.close();}@Overridepublic void initialize() throws SQLException {if (this.URL == null) {throw new SQLException("DBPool could not be created: DB URL cannot be null");}if (this.driver == null) {throw new SQLException("DBPool driver could not be created: DB driver class name cannot be null!");}if (this.maxConnections < 0) {throw new SQLException("DBPool maxConnections could not be created: Max connections must be greater than zero!");}datasource = new DruidDataSource();try{datasource.setDriverClassName(this.driver);} catch (Exception e) {try {throw new SchedulerException("Problem setting driver class name on datasource: " + e.getMessage(), e);} catch (SchedulerException ignored) {}}datasource.setUrl(this.URL);datasource.setUsername(this.user);datasource.setPassword(this.password);datasource.setMaxActive(this.maxConnections);datasource.setMinIdle(1);datasource.setMaxWait(0);datasource.setMaxPoolPreparedStatementPerConnectionSize(DEFAULT_DB_MAX_CONNECTIONS);if (this.validationQuery != null) {datasource.setValidationQuery(this.validationQuery);if(!this.validateOnCheckout)datasource.setTestOnReturn(true);elsedatasource.setTestOnBorrow(true);datasource.setValidationQueryTimeout(this.idleConnectionValidationSeconds);}}
}
  • 编写实体类

CodeEnum:用于响应状态码

package com.wf.test_quartz_demo.bean;/*** 状态码枚举*/
public enum CodeEnum {/**操作成功**/SUCCESS(200,"操作成功"),/**服务调用异常**/SERVICE_CALL_EXCEPTION(400,"服务调用异常"),/**操作失败**/ERROR(500,"操作失败"),/**参数不合法**/ILLEGAL_PARAMETER(5001,"参数不合法"),/**验证码已失效**/VERIFICATION_CODE_FAILURE(5002,"验证码已失效"),/**用户昵称重复**/DUPLICATE_NICKNAME(5003,"用户昵称重复"),/**用户名或密码错误**/LOGIN_FAILED(5004,"用户名或密码错误"),/**文件上传失败**/FILE_UPLOAD_FAILED(5005,"文件上传失败"),/**资源不存在*/RESOURCE_DOES_NOT_EXIST(5006,"资源不存在"),/** 当前项目已存在该用户*/DUPLICATE_USER(5007,"用户重复入项"),/**无效签名**/JWT_INVALID(2001,"无效签名"),/**token过期**/JWT_OVERDUE(2002,"token过期"),/**token算法不一致**/JWT_ALGORITHM_INCONSISTENCY(2003,"token算法不一致"),/**token失效**/JWT_LOSE_EFFECT(2004,"token失效"),/**非法请求**/ILLEGAL_REQUEST(2005,"非法请求,请求来源不合法");/*** 自定义状态码**/private Integer code;/**自定义描述**/private String message;CodeEnum(Integer code, String message){this.code = code;this.message = message;}public Integer getCode() {return code;}public String getMessage() {return message;}
}

CommonResult:响应数据结构

package com.wf.test_quartz_demo.bean;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** 请求信息类,用于返回请求是否成功* @param <T>*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T>{/*** 响应状态码*/private int code;/*** 响应结果描述*/private String message;/*** 返回的数据*/private T data;/*** 成功返回* @param data* @param <T>* @return*/public static <T> CommonResult<T> success(T data) {CommonResult<T> response= new CommonResult<>();response.setCode(CodeEnum.SUCCESS.getCode());response.setMessage(CodeEnum.SUCCESS.getMessage());response.setData(data);return response;}/***  失败返回,自定义code* @param code* @param message* @param <T>* @return*/public static <T> CommonResult<T> fail(Integer code, String message) {CommonResult<T> response = new CommonResult<>();response.setCode(code);response.setMessage(message);return response;}/***  失败返回* @param codeEnum* @param <T>* @return*/public static <T> CommonResult<T> fail(CodeEnum codeEnum) {CommonResult<T> response = new CommonResult<>();response.setCode(codeEnum.getCode());response.setMessage(codeEnum.getMessage());return response;}/***  失败返回* @param message* @param <T>* @return*/public static <T> CommonResult<T> fail(String message) {CommonResult<T> response = new CommonResult<>();response.setCode(CodeEnum.ERROR.getCode());response.setMessage(message);return response;}}

QuartzJobVO:任务信息VO

package com.wf.test_quartz_demo.bean;import lombok.Data;
import java.math.BigInteger;/*** @Description: 定时任务信息*/
@Data
public class QuartzJobVO {private String schedName;private String jobName;private String jobGroup;private String description;private String jobClassName;private BigInteger nextFireTime;private BigInteger startTime;private String triggerState;private String cronExpression;
}

RecordsParam:接口请求参数

package com.wf.test_quartz_demo.bean;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.quartz.JobDataMap;/*** 记录查询参数*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RecordsParam {/*** 分页页码*/private Integer page = 1;/*** 单页展示条数*/private Integer size = 10;/*** 任务名称*/private String jobName;/*** 任务组名称*/private String jobGroupName;/*** 触发器名称*/private String triggerName;/*** 触发器组名称*/private String triggerGroupName;/*** corn表达式*/private String corn;/*** 实际执行业务逻辑的job路径*/private  String classPath;/*** 需要向job传递的参数*/private JobDataMap jobDataMap;/*** 备注*/private String  remarks;/*** 分页起始页*/private Integer pageIndex;}
  • 编写Mapper.xml

QuartzJobInfoMapper.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wf.test_quartz_demo.mapper.QuartzJobInfoMapper"><!--    查询任务信息列表--><select id="getJobAndTriggerDetails" resultType="QuartzJobVO" parameterType="RecordsParam">SELECTtqjd.SCHED_NAME as schedName,tqjd.JOB_NAME as jobName,tqjd.JOB_GROUP as jobGroup,tqjd.DESCRIPTION as description,tqjd.JOB_CLASS_NAME as jobClassName,tqt.NEXT_FIRE_TIME as nextFireTime,tqt.START_TIME as startTime,tqt.TRIGGER_STATE as triggerState,tqct.CRON_EXPRESSION as cronExpressionFROMqrtz_job_details tqjdLEFT JOIN qrtz_triggers tqt ON tqjd.SCHED_NAME = tqt.SCHED_NAMEAND tqjd.JOB_NAME = tqt.JOB_NAMEAND tqjd.JOB_GROUP = tqt.JOB_GROUPLEFT JOIN qrtz_cron_triggers tqct ON tqt.SCHED_NAME = tqct.SCHED_NAMEAND tqt.TRIGGER_NAME = tqct.TRIGGER_NAMEAND tqt.TRIGGER_GROUP = tqct.TRIGGER_GROUPWHEREtqt.TRIGGER_TYPE = 'CRON'<if test="pageIndex !=null">LIMIT #{pageIndex},#{size}</if></select></mapper>
  • 编写Mapper接口

QuartzJobInfoMapper

package com.wf.test_quartz_demo.mapper;import com.wf.test_quartz_demo.bean.QuartzJobVO;
import com.wf.test_quartz_demo.bean.RecordsParam;
import org.springframework.stereotype.Repository;import java.util.List;@Repository
public interface QuartzJobInfoMapper  {/*** 查询任务列表* @param param* @return*/List<QuartzJobVO> getJobAndTriggerDetails(RecordsParam param);}
  • 编写Service接口及实现类

TestQuartzJobInfoService

package com.wf.test_quartz_demo.service;import java.util.List;import com.wf.test_quartz_demo.bean.CommonResult;
import com.wf.test_quartz_demo.bean.QuartzJobVO;
import com.wf.test_quartz_demo.bean.RecordsParam;/*** @Description: 定时任务*/
public interface TestQuartzJobInfoService  {CommonResult<String> addJob(RecordsParam param);CommonResult<String> pauseJob(RecordsParam param);CommonResult<String> resumeJob(RecordsParam param);CommonResult<String> updateJob(RecordsParam param);CommonResult<String> deleteJob(RecordsParam param);CommonResult<List<QuartzJobVO>> getJobAndTriggerDetails(RecordsParam param);
}

TestQuartzJobServiceImpl

package com.wf.test_quartz_demo.service.impl;import com.wf.test_quartz_demo.bean.CodeEnum;
import com.wf.test_quartz_demo.bean.CommonResult;
import com.wf.test_quartz_demo.bean.QuartzJobVO;
import com.wf.test_quartz_demo.bean.RecordsParam;
import com.wf.test_quartz_demo.mapper.QuartzJobInfoMapper;
import com.wf.test_quartz_demo.service.TestQuartzJobInfoService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;
import java.util.List;/*** @Description: 任务调度测试service*/
@Slf4j
@Service
public class TestQuartzJobServiceImpl implements TestQuartzJobInfoService {@Resourceprivate QuartzJobInfoMapper quartzJobMapper;@Resourceprivate Scheduler scheduler;/*** 添加任务* @param param* @return*/@Overridepublic CommonResult<String> addJob(RecordsParam param) {try {if(param!=null){//动态控制job,根据job的类路径,创建JobDetailClass clsss = Class.forName(param.getClassPath());//设置job的业务逻辑类路径JobDetail jobDetail = JobBuilder.newJob(clsss)//设置job的名称,组名称.withIdentity(param.getJobName(), param.getJobGroupName())//设置对job的说明描述.withDescription(param.getRemarks())//向job传递参数.usingJobData(param.getJobDataMap()).build();// 创建触发器CronTrigger trigger = TriggerBuilder.newTrigger()//设置触发器的名称,触发器的组名.withIdentity(param.getTriggerName(), param.getTriggerGroupName()).startNow()//设置corn表达式.withSchedule(CronScheduleBuilder.cronSchedule(param.getCorn())).build();// 启动调度器scheduler.start();scheduler.scheduleJob(jobDetail, trigger);return CommonResult.success("添加任务成功");}return CommonResult.fail(CodeEnum.ILLEGAL_PARAMETER);} catch (Exception e) {log.info("创建定时任务失败{}" , e.getMessage());return CommonResult.fail("添加任务失败:"+e.getMessage());}}/*** 暂停任务* @param param* @return*/@Overridepublic CommonResult<String> pauseJob(RecordsParam param)  {try {if(param!=null){//设置任务名称,任务组名称scheduler.pauseJob(JobKey.jobKey(param.getJobName(), param.getJobGroupName()));return CommonResult.success("暂停任务成功");}return CommonResult.fail(CodeEnum.ILLEGAL_PARAMETER);} catch (SchedulerException e) {log.info("暂停任务失败{}" , e.getMessage());return CommonResult.fail("暂停任务失败:"+e.getMessage());}}/*** 恢复任务* @param param* @return*/@Overridepublic CommonResult<String> resumeJob(RecordsParam param) {try {if(param!=null){//设置任务名称,任务组名称scheduler.resumeJob(JobKey.jobKey(param.getJobName(), param.getJobGroupName()));return CommonResult.success("恢复任务成功");}return CommonResult.fail(CodeEnum.ILLEGAL_PARAMETER);} catch (SchedulerException e) {log.info("恢复任务失败{}" , e.getMessage());return CommonResult.fail("恢复任务失败:"+e.getMessage());}}/*** 修改任务* @param param* @return*/@Override@Transactionalpublic CommonResult<String> updateJob(RecordsParam param) {if(param!=null){//删除之前的任务deleteJob(param);//创建新的任务addJob(param);return CommonResult.success("修改任务成功");}return CommonResult.fail(CodeEnum.ILLEGAL_PARAMETER);}/*** 删除任务* @param param* @return*/@Overridepublic CommonResult<String> deleteJob(RecordsParam param) {try {if(param!=null){//设置任务名称,任务组名称scheduler.pauseTrigger(TriggerKey.triggerKey(param.getJobName(), param.getJobGroupName()));scheduler.unscheduleJob(TriggerKey.triggerKey(param.getJobName(), param.getJobGroupName()));scheduler.deleteJob(JobKey.jobKey(param.getJobName(), param.getJobGroupName()));return CommonResult.success("删除任务成功");}return CommonResult.fail(CodeEnum.ILLEGAL_PARAMETER);} catch (SchedulerException e) {log.info("删除任务失败{}" , e.getMessage());return CommonResult.fail("删除任务失败:"+e.getMessage());}}@Overridepublic CommonResult<List<QuartzJobVO>> getJobAndTriggerDetails(RecordsParam param) {param.setPageIndex((param.getPage() - 1) * param.getSize());//查询任务列表return CommonResult.success(quartzJobMapper.getJobAndTriggerDetails(param));}}
  • 编写controller

TestQuartzController

package com.wf.test_quartz_demo.controller;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wf.test_quartz_demo.bean.CommonResult;
import com.wf.test_quartz_demo.bean.QuartzJobVO;
import com.wf.test_quartz_demo.bean.RecordsParam;
import com.wf.test_quartz_demo.service.TestQuartzJobInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.*;import java.util.List;/*** @Description: 测试任务调度*/
@RestController
@RequestMapping("/quartz/")
@Slf4j
public class TestQuartzController {@AutowiredTestQuartzJobInfoService  testQuartzJobInfoService;/*** 添加任务* @param param* @return*/@PostMapping(path = "/add")@ResponseBodypublic CommonResult<String> addJob(@RequestBody RecordsParam param) {return   testQuartzJobInfoService.addJob(param);}/*** 暂停任务* @param param* @return*/@PostMapping(path = "/pause")@ResponseBodypublic CommonResult<String> pauseJob(@RequestBody RecordsParam param) {return   testQuartzJobInfoService.pauseJob(param);}/*** 恢复任务* @param param* @return*/@PostMapping(path = "/resume")@ResponseBodypublic CommonResult<String> resumeJob(@RequestBody  RecordsParam param) {return   testQuartzJobInfoService.resumeJob(param);}/*** 修改任务**/@PostMapping(path = "/update")@ResponseBodypublic CommonResult<String> updateJob(@RequestBody  RecordsParam param) {return   testQuartzJobInfoService.updateJob(param);}/*** 删除任务**/@PostMapping(path = "/delete")@ResponseBodypublic CommonResult<String> deleteJob(@RequestBody  RecordsParam param) {return   testQuartzJobInfoService.deleteJob(param);}/*** 查询任务*/@PostMapping(path = "/query")@ResponseBodypublic CommonResult<List<QuartzJobVO>> queryJob(@RequestBody  RecordsParam param) {return   testQuartzJobInfoService.getJobAndTriggerDetails(param);}}
  • 编写具体任务

MyJobOne

package com.wf.test_quartz_demo.job;import cn.hutool.core.date.DateUtil;import cn.hutool.extra.spring.SpringUtil;
import com.wf.test_quartz_demo.bean.CommonResult;
import com.wf.test_quartz_demo.bean.QuartzJobVO;
import com.wf.test_quartz_demo.bean.RecordsParam;
import com.wf.test_quartz_demo.mapper.QuartzJobInfoMapper;
import com.wf.test_quartz_demo.service.impl.TestQuartzJobServiceImpl;import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;import javax.annotation.Resource;
import java.util.List;/*** @Description: 任务一*/
@Slf4j
public class MyJobOne implements Job {@Overridepublic void execute(JobExecutionContext jobExecutionContext) {//获取传递过来的参数JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();//此处无法通过spring获取mapperQuartzJobInfoMapper quartzJobMapper = SpringUtil.getBean("quartzJobInfoMapper");//查询任务列表List<QuartzJobVO> result = quartzJobMapper.getJobAndTriggerDetails(new RecordsParam());log.info("任务列表总数为:{}" , result.size());log.info("MyJobOne执行时间--->: {}" , DateUtil.now());jobDataMap.forEach((key,value)->{log.info("MyJobOne接收到的参数Key---value--->{}--{}",key,value);});}}

MyJobTwo

package com.wf.test_quartz_demo.job;import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wf.test_quartz_demo.bean.QuartzJobVO;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;/*** @Description: 任务2*/
@Slf4j
public class MyJobTwo implements Job {@Overridepublic void execute(JobExecutionContext jobExecutionContext) {//获取传递过来的参数JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();log.info("MyJobTwo执行时间--->: {}" , DateUtil.now());jobDataMap.forEach((key,value)->{log.info("MyJobTwo接收到的参数Key---value--->{}--{}",key,value);});}
}
  • 测试运行
    在这里插入图片描述

运行效果:
在这里插入图片描述
注:其余接口全部测试过都能正常运行,此处不在测试

  • 完整代码
    Gitee地址

相关文章:

Quartz入门教程

本文参考文章编写 Quartz 官网 Quartz 是 OpenSymphony 开源组织在 Job Scheduling 领域又一个开源项目&#xff0c;是完全由 Java 开发的一个开源任务日程管理系统&#xff0c;“任务进度管理器”就是一个在预先确定&#xff08;被纳入日程&#xff09;的时间到达时&#xff…...

TypeScript 学习之 function

函数可以实现抽象层&#xff0c;模拟类&#xff0c;信息隐藏和模块。 函数有&#xff1a;有名字的函数、匿名函数 在 JavaScript 中的函数 // 有名字的函数 function add(x, y) {return x y; }// 匿名函数 let myAdd function (x, y) {return x y; };函数类型 typescript 可…...

【云计算自学路线】

云计算包含的技术内容和涉及的方向比较多&#xff0c;一定要进行系统化的学习才能更好的掌握这门技术。 云计算作为互联网新技术领域&#xff0c;现阶段也是出于高速发展期&#xff0c;想学习加入云计算行业的小伙伴可以抓紧机会了&#xff0c;跟着小课一起来了解云计算以及它…...

code01 v2黑屏、花屏、死机、断电重启、休眠死机的进来

症状解决 长话简说&#xff0c;症状如下&#xff1a; 使用浏览器、播放视频等&#xff0c;遇到突然死机或花屏死机的情况 关闭硬件加速&#xff0c;如&#xff1a;浏览器中设置关闭硬件加速&#xff0c;出现这种症状的软件都需要设置 开机电流音、播放与暂停时喇叭吱吱想、打…...

分享107个HTML电子商务模板,总有一款适合您

分享107个HTML电子商务模板&#xff0c;总有一款适合您 107个HTML电子商务模板下载链接&#xff1a;https://pan.baidu.com/s/1VW67Wjso1BRpH7O3IlbZwg?pwd0d4s 提取码&#xff1a;0d4s Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 Aplustemplates 购物模板…...

Barra模型因子的构建及应用系列三之Momentum因子

一、摘要 在之前的Barra模型系列文章中&#xff0c;我们已经初步讲解、构建了Size因子和Beta因子&#xff0c;并分别创建了对应的单因子策略。通过回测发现&#xff0c;其中Size因子的小市值效应具有很强的收益能力。而本篇文章将在该系列下进一步构建Momentum因子。 二、模型…...

8.2.1.3 索引合并优化

索引合并访问方法检索具有多个范围扫描的行&#xff0c;并将其结果合并为一个。此访问方法仅合并来自单个表的索引扫描&#xff0c;而不是跨多个表的扫描。合并可以生成其基础扫描的合并、交叉或交叉的合并。 可以使用索引合并的查询示例&#xff1a; SELECT * FROM tbl_name…...

水雨情在线小能手-雨量水位报警站

雨量水位报警站由水位探测器、雨量传感器、报警灯、扩音器、太阳能板和采集传输控制器组成。实时采集水位等级&#xff0c;三个水位探测器对应3个水位等级&#xff0c;当现场水面浸没相应探测器时&#xff0c;本机会实时发出语音报警&#xff0c;同时可发送相应的预警/报警等级…...

【蓝桥杯集训4】双指针专题(6 / 6)

目录 3768. 字符串删减 - 滑动窗口ac 799. 最长连续不重复子序列 - 滑动窗口 800. 数组元素的目标和 - 二分ac 2816. 判断子序列 - 双指针 1238. 日志统计 - 滑动窗口 1240. 完全二叉树的权值 - 双指针 1、前缀和 - 通过了 5/12个数据 2、双指针 3768. 字符串删减 -…...

文件流,gzip解压,压缩

目录 文件画布 写入 &#xff08;空文件Foutnew File(Parent,entry.getName());&#xff09;FileOutputStream outnew FileOutputStream(Fout);BufferedOutputStream Boutnew BufferedOutputStream(out);其他流量基于基础包装文件--文件流---字节流 顺序pbf一般是形成后再压缩目…...

在线开会,来开开圆桌会议吧~

圆桌会议应用场景&#xff1a;适合内部培训、部门会议亦或是头脑风暴等较为轻松的场景&#xff0c;有兴趣的朋友可以联系我来测试哦~~ 上图&#xff1a; 图&#xff1a;圆桌会议应用截图 在圆桌布局之下&#xff0c;企业可以将每一位参会者和座位绑定&#xff0c;1:1模拟线下圆…...

使用营销自动化的 7 大主要优势

对于大多数企业家来说&#xff0c;自动化已成为在数字时代简化业务的必要条件。那么&#xff0c;您可以采取哪些步骤来实施营销自动化呢&#xff1f; 1. 社交媒体整合 拥有吸引人的社交媒体形象是成功的先决条件。您不可能完成所有社交媒体营销任务&#xff0c;使用自动化软件&…...

【图像分类】基于PyTorch搭建GRU实现MNIST手写数字体识别(单/双向GRU,附完整代码和数据集)

写在前面: 首先感谢兄弟们的关注和订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 在https://blog.csdn.net/AugustMe/article/details/128969138文章中,我们使用了基于PyTorch搭建LSTM实现MNIST手…...

day14_oop_抽象_接口

今日内容 上课同步视频:CuteN饕餮的个人空间_哔哩哔哩_bilibili 同步笔记沐沐霸的博客_CSDN博客-Java2301 零、 复习昨日 一、作业 二、抽象 三、接口 零、 复习昨日 多态的好处: 扩展性强.加入新的功能,不需要改动代码降低代码耦合度(解耦合或者松耦合) 一、抽象类 1.1 抽象类…...

模式识别 | MATLAB实现DNN深度神经网络模式分类识别

分类预测 | MATLAB实现DNN全连接神经网络多特征分类预测 目录 分类预测 | MATLAB实现DNN全连接神经网络多特征分类预测基本介绍任务描述程序设计参考资料基本介绍 DNN的结构不固定,一般神经网络包括输入层、隐藏层和输出层,一个DNN结构只有一个输入层,一个输出层,输入层和输…...

【C++】类和对象三大特性--继承

文章目录1.继承的概念及定义1.1继承的概念1.2 继承定义1.2.1定义格式1.2.2继承关系和访问限定符1.2.3继承基类成员访问方式的变化2.基类和派生类对象赋值转换3.继承中的作用域4.派生类的默认成员函数5.继承与友元6. 继承与静态成员7.复杂的菱形继承及菱形虚拟继承虚拟继承解决数…...

MySQL的存储引擎

目录 一.概念 二.分类 操作 修改默认存储引擎 一.概念 数据库存储引擎是数据库底层软件组织&#xff0c;数据库管理系统&#xff08;DBMS&#xff09;使用数据引擎进行创建、查询、更新和删除数据。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能。现在许多不…...

工程项目管理系统源码-简洁+好用+全面-工程项目管理系统

​ ​工程项目管理系统是指从事工程项目管理的企业&#xff08;以下简称工程项目管理企业&#xff09;受业主委托&#xff0c;按照合同约定&#xff0c;代表业主对工程项目的组织实施进行全过程或若干阶段的管理和服务。 ​系统定义 工程项目管理企业不直接与该工程项目的总承…...

什么是STAR原则?

文章目录&#x1f4cb;前言&#x1f525;省流版&#x1f3af;什么是STAR原则&#x1f3af;进行过程&#x1f4cb;前言 对于大部分还在学习阶段的学生们来说&#xff0c;可能并不了解这个原则的含义&#xff0c;这里的star并不是指英文单词星星。这个原则我也是前段时间才认识到…...

前置知识-初值问题、显式隐式龙格库塔方法、Butcher阵列

1.1.4 龙格一库塔法 将向前欧拉法写成式 (1-37) 的形式, 可以看出它实际上利用了 f ( x , u ) f(x, u) f(x,u) 在 x n...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

云原生安全实战:API网关Kong的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关&#xff08;API Gateway&#xff09; API网关是微服务架构中的核心组件&#xff0c;负责统一管理所有API的流量入口。它像一座…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)

目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 &#xff08;1&#xff09;输入单引号 &#xff08;2&#xff09;万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...

【Linux】Linux安装并配置RabbitMQ

目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的&#xff0c;需要先安…...

【Kafka】Kafka从入门到实战:构建高吞吐量分布式消息系统

Kafka从入门到实战:构建高吞吐量分布式消息系统 一、Kafka概述 Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发,后成为Apache顶级项目。它被设计用于高吞吐量、低延迟的消息处理,能够处理来自多个生产者的海量数据,并将这些数据实时传递给消费者。 Kafka核心特…...

嵌入式面试常问问题

以下内容面向嵌入式/系统方向的初学者与面试备考者,全面梳理了以下几大板块,并在每个板块末尾列出常见的面试问答思路,帮助你既能夯实基础,又能应对面试挑战。 一、TCP/IP 协议 1.1 TCP/IP 五层模型概述 链路层(Link Layer) 包括网卡驱动、以太网、Wi‑Fi、PPP 等。负责…...

中科院1区顶刊|IF14+:多组学MR联合单细胞时空分析,锁定心血管代谢疾病的免疫治疗新靶点

中科院1区顶刊|IF14&#xff1a;多组学MR联合单细胞时空分析&#xff0c;锁定心血管代谢疾病的免疫治疗新靶点 当下&#xff0c;免疫与代谢性疾病的关联研究已成为生命科学领域的前沿热点。随着研究的深入&#xff0c;我们愈发清晰地认识到免疫系统与代谢系统之间存在着极为复…...