1. 示例代码 :
quartz-project: quartz
2. 环境信息
springboot 2.5.4
3. 搭建流程
3.1 创建一个springboot项目 (略)
3.2 pom.xml
<dependencies>
<!-- springboot启动文件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--druid数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.20</version>
</dependency>
<!--mybatisplus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<!--mybatisplus 代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!--hutool工具类-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.4</version>
</dependency>
<!--hutool 表情类扩展包-->
<dependency>
<groupId>com.vdurmont</groupId>
<artifactId>emoji-java</artifactId>
<version>4.0.0</version>
</dependency>
</dependencies>
application.yml
server:
port: 9999
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
password: 123456
url: jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowMutiQueries=true
username: root
# 配置数据库连接池
type: com.alibaba.druid.pool.DruidDataSource
druid:
initial-size: 1
min-idle: 1
max-active: 20
max-wait: 60000
test-while-idle: true
time-between-connect-error-millis: 60000
min-evictable-idle-time-millis: 30000
validation-query: select 'x'
test-on-borrow: false
test-on-return: false
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
use-global-data-source-stat: false
filters: stat,wall,slf4j
connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
time-between-log-stats-millis: 300000
username: ${spring.datasource.username}
password: ${spring.datasource.password}
url : ${spring.datasource.url}
driver-class-name: ${spring.datasource.driver-class-name}
quartz.properties
#主要分为scheduler、threadPool、jobStore、dataSource等部分
org.quartz.scheduler.instanceId=AUTO
#如果您希望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
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.hctrl.quartz.config.DruidConnectionProvider
org.quartz.dataSource.qzDS.URL=jdbc:mysql://127.0.0.1:3306/quartz?characterEncoding=utf8&useSSL=false&autoReconnect=true&serverTimezone=UTC
org.quartz.dataSource.qzDS.user=root
org.quartz.dataSource.qzDS.password=123456
org.quartz.dataSource.qzDS.driver=com.mysql.cj.jdbc.Driver
org.quartz.dataSource.qzDS.maxConnection=10
#设置为“true”以打开群集功能。如果您有多个Quartz实例使用同一组数据库表,则此属性必须设置为“true”,否则您将遇到破坏
#org.quartz.jobStore.isClustered=false
quartz.sql (项目启动不会自动创建表, 需要在数据库中手动创建表)
/*
Navicat Premium Data Transfer
Source Server : localhost
Source Server Type : MySQL
Source Server Version : 80025
Source Host : localhost:3306
Source Schema : quartz
Target Server Type : MySQL
Target Server Version : 80025
File Encoding : 65001
Date: 21/04/2022 11:17:50
*/
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 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 '触发器组',
`blob_data` blob,
PRIMARY KEY (`sched_name`, `trigger_name`, `trigger_group`) USING BTREE,
INDEX `sched_name`(`sched_name`) 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 = '以blob 类型存储的触发器' 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 COMMENT '计划名称',
`calendar_name` varchar(200) 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 COMMENT = '日历信息表' 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 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 '触发器组',
`cron_expression` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '时间表达式',
`time_zone_id` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '时区id',
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 = '定时触发器表' 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 COMMENT '计划名称',
`entry_id` varchar(95) 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 '触发器组',
`instance_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '当前实例的名称',
`fired_time` bigint(0) NOT NULL COMMENT '当前执行时间',
`sched_time` bigint(0) NOT NULL COMMENT '计划时间',
`priority` int(0) 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 DEFAULT NULL COMMENT '作业名称',
`job_group` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '作业组',
`is_nonconcurrent` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '是否并行',
`requests_recovery` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '是否要求唤醒',
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 COMMENT = '保存已经触发的触发器状态信息' 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 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 DEFAULT NULL COMMENT '描述',
`job_class_name` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '作业程序类名',
`is_durable` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '是否持久',
`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 '是否要求唤醒',
`job_data` blob COMMENT '作业名称',
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 COMMENT = 'job 详细信息表' 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 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;
-- ----------------------------
-- 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 COMMENT '计划名称',
`trigger_group` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器组',
PRIMARY KEY (`sched_name`, `trigger_group`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '存放暂停掉的触发器表' 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 COMMENT '计划名称',
`instance_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '实例名称',
`last_checkin_time` bigint(0) NOT NULL COMMENT '最后的检查时间',
`checkin_interval` bigint(0) NOT NULL COMMENT '检查间隔',
PRIMARY KEY (`sched_name`, `instance_name`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '调度器状态表' 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 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 '触发器组',
`repeat_count` bigint(0) NOT NULL COMMENT '重复次数',
`repeat_interval` bigint(0) NOT NULL COMMENT '重复间隔',
`times_triggered` bigint(0) 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 = '简单的触发器表' 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 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 '触发器组',
`str_prop_1` varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '计划名称',
`str_prop_2` varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '计划名称',
`str_prop_3` varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '计划名称',
`int_prop_1` int(0) DEFAULT NULL,
`int_prop_2` int(0) DEFAULT NULL,
`long_prop_1` bigint(0) DEFAULT NULL,
`long_prop_2` bigint(0) 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 COMMENT = '存储两种类型的触发器表' 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 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 '作业名称',
`job_group` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '作业组',
`description` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '描述',
`next_fire_time` bigint(0) DEFAULT NULL COMMENT '下次执行时间',
`prev_fire_time` bigint(0) DEFAULT NULL COMMENT '前一次',
`priority` int(0) DEFAULT NULL COMMENT '优先权',
`trigger_state` varchar(16) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器状态',
`trigger_type` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器类型',
`start_time` bigint(0) NOT NULL COMMENT '开始时间',
`end_time` bigint(0) DEFAULT NULL COMMENT '结束时间',
`calendar_name` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '日历名称',
`misfire_instr` smallint(0) DEFAULT NULL COMMENT '失败次数',
`job_data` blob COMMENT '作业数据',
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 COMMENT = '触发器表' ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
3.3 代码
DruidConfig :? druid数据源配置
package com.hctrl.quartz.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.hctrl.quartz.properties.DruidDataSourceProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.sql.SQLException;
/**
* @author hanchao
* @version 1.0
* @date 2022/4/21 9:54
*/
@Configuration
@EnableConfigurationProperties({ DruidDataSourceProperties.class })
public class DruidConfig {
/**
* 此处bean name设置为druidDataSource1 , 防止冲突
* @param properties
* @return
*/
@Bean
@ConditionalOnMissingBean
public DataSource druidDataSource1(DruidDataSourceProperties properties) {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(properties.getDriverClassName());
druidDataSource.setUrl(properties.getUrl());
druidDataSource.setUsername(properties.getUsername());
druidDataSource.setPassword(properties.getPassword());
druidDataSource.setInitialSize(properties.getInitialSize());
druidDataSource.setMinIdle(properties.getMinIdle());
druidDataSource.setMaxActive(properties.getMaxActive());
druidDataSource.setMaxWait(properties.getMaxWait());
druidDataSource.setTimeBetweenEvictionRunsMillis(properties.getTimeBetweenEvictionRunsMillis());
druidDataSource.setMinEvictableIdleTimeMillis(properties.getMinEvictableIdleTimeMillis());
druidDataSource.setValidationQuery(properties.getValidationQuery());
druidDataSource.setTestWhileIdle(properties.isTestWhileIdle());
druidDataSource.setTestOnBorrow(properties.isTestOnBorrow());
druidDataSource.setTestOnReturn(properties.isTestOnReturn());
druidDataSource.setPoolPreparedStatements(properties.isPoolPreparedStatements());
druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(
properties.getMaxPoolPreparedStatementPerConnectionSize());
try {
druidDataSource.init();
} catch (SQLException e) {
e.printStackTrace();
}
return druidDataSource;
}
}
DruidConnectionProvider : 将quartz数据源修改为druid, 类中的属性会自动读取配置文件, 写好get, set方法即可?
package com.hctrl.quartz.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.quartz.SchedulerException;
import org.quartz.utils.ConnectionProvider;
import java.sql.Connection;
import java.sql.SQLException;
/**
* @author hanchao
* @version 1.0
* @date 2022/4/21 9:37
*/
public class DruidConnectionProvider implements ConnectionProvider {
//JDBC驱动
public String driver;
//JDBC连接串
public String URL;
//数据库用户名
public String user;
//数据库用户密码
public String password;
//数据库最大连接数
public int maxConnection;
//数据库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;
private DruidDataSource datasource;
public Connection getConnection() throws SQLException {
return datasource.getConnection();
}
public void shutdown() throws SQLException {
datasource.close();
}
public 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.maxConnection < 0) {
throw new SQLException("DBPool maxConnectins 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 e1) {
}
}
datasource.setUrl(this.URL);
datasource.setUsername(this.user);
datasource.setPassword(this.password);
datasource.setMaxActive(this.maxConnection);
datasource.setMinIdle(1);
datasource.setMaxWait(0);
datasource.setMaxPoolPreparedStatementPerConnectionSize(this.DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION);
if (this.validationQuery != null) {
datasource.setValidationQuery(this.validationQuery);
if(!this.validateOnCheckout)
datasource.setTestOnReturn(true);
else
datasource.setTestOnBorrow(true);
datasource.setValidationQueryTimeout(this.idleConnectionValidationSeconds);
}
}
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* 提供get set方法
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public String getURL() {
return URL;
}
public void setURL(String URL) {
this.URL = URL;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getMaxConnection() {
return maxConnection;
}
public void setMaxConnection(int maxConnection) {
this.maxConnection = maxConnection;
}
public String getValidationQuery() {
return validationQuery;
}
public void setValidationQuery(String validationQuery) {
this.validationQuery = validationQuery;
}
public boolean isValidateOnCheckout() {
return validateOnCheckout;
}
public void setValidateOnCheckout(boolean validateOnCheckout) {
this.validateOnCheckout = validateOnCheckout;
}
public int getIdleConnectionValidationSeconds() {
return idleConnectionValidationSeconds;
}
public void setIdleConnectionValidationSeconds(int idleConnectionValidationSeconds) {
this.idleConnectionValidationSeconds = idleConnectionValidationSeconds;
}
public DruidDataSource getDatasource() {
return datasource;
}
public void setDatasource(DruidDataSource datasource) {
this.datasource = datasource;
}
}
QuartzConfig : quartz配置类
package com.hctrl.quartz.config;
import org.quartz.Scheduler;
import org.quartz.ee.servlet.QuartzInitializerListener;
import org.springframework.beans.factory.annotation.Autowired;
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 javax.annotation.Resource;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;
/**
* @author hanchao
* @version 1.0
* @date 2022/4/21 9:34
*/
@Configuration
public class QuartzConfig implements SchedulerFactoryBeanCustomizer {
@Resource
private DataSource druidDataSource1;
@Bean
public Properties properties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
// 对quartz.properties文件进行读取
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
// 在quartz.properties中的属性被读取并注入后再初始化对象
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
@Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
// schedulerFactoryBean.setQuartzProperties(properties());
//此处一定要配置数据源, 否则创建的任务和触发器不用存入到数据库中
schedulerFactoryBean.setDataSource(druidDataSource1);
return schedulerFactoryBean;
}
/*
* quartz初始化监听器
*/
@Bean
public QuartzInitializerListener executorListener() {
return new QuartzInitializerListener();
}
/*
* 通过SchedulerFactoryBean获取Scheduler的实例
*/
@Bean
public Scheduler scheduler() throws IOException {
return schedulerFactoryBean().getScheduler();
}
/**
* 使用阿里的druid作为数据库连接池
*/
@Override
public void customize(SchedulerFactoryBean schedulerFactoryBean) {
schedulerFactoryBean.setStartupDelay(2);
schedulerFactoryBean.setAutoStartup(true);
schedulerFactoryBean.setOverwriteExistingJobs(true);
}
}
DruidDataSourceProperties : 读取配置文件的配置信息, 创建druid数据源使用
package com.hctrl.quartz.properties;
import lombok.Data;
import lombok.extern.apachecommons.CommonsLog;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author hanchao
* @version 1.0
* @date 2022/4/21 9:52
*/
@ConfigurationProperties(prefix="spring.datasource.druid")
@Data
public class DruidDataSourceProperties {
private String driverClassName;
private String url;
private String username;
private String password;
// jdbc connection pool
private int initialSize;
private int minIdle;
private int maxActive = 100;
private long maxWait;
private long timeBetweenEvictionRunsMillis;
private long minEvictableIdleTimeMillis;
private String validationQuery;
private boolean testWhileIdle;
private boolean testOnBorrow;
private boolean testOnReturn;
private boolean poolPreparedStatements;
private int maxPoolPreparedStatementPerConnectionSize;
// filter
private String filters;
}
controller :?
package com.hctrl.quartz.controller;
import cn.hutool.cron.CronUtil;
import cn.hutool.cron.pattern.CronPattern;
import cn.hutool.cron.pattern.CronPatternUtil;
import com.hctrl.quartz.service.QuartzService;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import result.BaseResult;
import result.CommonResult;
/**
*
*
* 每个任务JobDetail可以绑定多个Trigger,但一个Trigger只能绑定一个任务
*
* @author hanchao
* @version 1.0
* @date 2022/4/21 9:29
*/
@RestController
@RequestMapping("/v1/quartz")
public class QuartzController {
@Autowired
private QuartzService quartzService;
/**
* 新增定时任务
*
* @param jName 任务名称
* @param jGroup 任务组
* @param tName 触发器名称
* @param tGroup 触发器组
* @param cron cron表达式
* @return ResultMap
*/
@PostMapping(path = "/addJob")
public BaseResult addJob(String jName, String jGroup, String tName, String tGroup, String cron) {
try {
if (cron == null){
cron = "*/5 * * * * ?";
}
quartzService.addJob(jName, jGroup, tName, tGroup, cron);
return CommonResult.buildSuccess("添加任务成功");
} catch (Exception e) {
e.printStackTrace();
return CommonResult.buildFailure(500,"添加任务失败");
}
}
/**
* 暂停任务
*
* @param jName 任务名称
* @param jGroup 任务组
* @return ResultMap
*/
@PostMapping(path = "/pauseJob")
public BaseResult pauseJob(String jName, String jGroup) {
try {
quartzService.pauseJob(jName, jGroup);
return CommonResult.buildSuccess("暂停任务成功");
} catch (SchedulerException e) {
e.printStackTrace();
return CommonResult.buildFailure(500,"暂停任务失败");
}
}
/**
* 恢复任务
*
* @param jName 任务名称
* @param jGroup 任务组
* @return ResultMap
*/
@PostMapping(path = "/resumeJob")
public BaseResult resumeJob(String jName, String jGroup) {
try {
quartzService.resumeJob(jName, jGroup);
return CommonResult.buildSuccess("恢复任务成功");
} catch (SchedulerException e) {
e.printStackTrace();
return CommonResult.buildFailure(500,"恢复任务失败");
}
}
/**
* 重启任务
*
* @param jName 任务名称
* @param jGroup 任务组
* @param cron cron表达式
* @return ResultMap
*/
@PostMapping(path = "/reScheduleJob")
public BaseResult rescheduleJob(String jName, String jGroup, String cron) {
try {
quartzService.reScheduleJob(jName, jGroup, cron);
return CommonResult.buildSuccess("重启任务成功");
} catch (SchedulerException e) {
e.printStackTrace();
return CommonResult.buildFailure(500,"重启任务失败");
}
}
/**
* 删除任务
*
* @param jName 任务名称
* @param jGroup 任务组
* @return ResultMap
*/
@PostMapping(path = "/deleteJob")
public BaseResult deleteJob(String jName, String jGroup) {
try {
quartzService.deleteJob(jName, jGroup);
return CommonResult.buildSuccess("删除任务成功");
} catch (SchedulerException e) {
e.printStackTrace();
return CommonResult.buildFailure(500,"删除任务失败");
}
}
}
service :?
package com.hctrl.quartz.service;
import com.hctrl.quartz.Job.HelloJob;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author hanchao
* @version 1.0
* @date 2022/4/21 9:29
*/
@Service
@Slf4j
public class QuartzService {
@Autowired
private Scheduler scheduler;
/**
* 新增定时任务
*
* @param jName 任务名称
* @param jGroup 任务组
* @param tName 触发器名称
* @param tGroup 触发器组
* @param cron cron表达式
*/
public void addJob(String jName, String jGroup, String tName, String tGroup, String cron) {
try {
// 构建JobDetail, 可以设置dataMap, 用于传递参数
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity(jName, jGroup)
.build();
// 按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(tName, tGroup)
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule(cron))
.build();
// 启动调度器
scheduler.start();
scheduler.scheduleJob(jobDetail, trigger);
} catch (Exception e) {
log.info("创建定时任务失败" + e);
}
}
public void pauseJob(String jName, String jGroup) throws SchedulerException {
scheduler.pauseJob(JobKey.jobKey(jName, jGroup));
}
public void resumeJob(String jName, String jGroup) throws SchedulerException {
scheduler.resumeJob(JobKey.jobKey(jName, jGroup));
}
public void reScheduleJob(String jName, String jGroup, String cron) throws SchedulerException {
TriggerKey triggerKey = TriggerKey.triggerKey(jName, jGroup);
// 表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
// 按新的trigger重新设置job执行,重启触发器
scheduler.rescheduleJob(triggerKey, trigger);
}
public void deleteJob(String jName, String jGroup) throws SchedulerException {
scheduler.pauseTrigger(TriggerKey.triggerKey(jName, jGroup));
scheduler.unscheduleJob(TriggerKey.triggerKey(jName, jGroup));
scheduler.deleteJob(JobKey.jobKey(jName, jGroup));
}
}
HelloJob : 执行任务类, 主要逻辑
package com.hctrl.quartz.Job;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.PersistJobDataAfterExecution;
import java.time.LocalDateTime;
/**
*
* @DisallowConcurrentExecution : 防止并发
*
* @PersistJobDataAfterExecution : 默认每次执行都会创建一个新的HelloJob对象, 添加这个注解, 则变成单例
*
* org.quartz.jobStore.misfireThreshold : 重要配置, 当一个任务到了时间执行, 但是上一个任务还没执行完成,
* 并且添加了@DisallowConcurrentExecution注解, 则本次任务会延期执行,
* 延期时间由这个配置决定, 如果配置了5s, 并且延期了5秒还是没有执行, 则本次任务不会执行
*
* @author hanchao
* @version 1.0
* @date 2022/4/21 10:22
*/
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext context) {
String name = context.getJobDetail().getKey().getName();
String format = DateUtil.format(LocalDateTime.now(), DatePattern.NORM_DATETIME_PATTERN);
System.out.println(name + " Hello Job执行时间: " + format);
}
}
结果图 :?
?
|