spring-boot-starter-quartz、mysql、集群动态配置定时任务


本文参考了https://blog.csdn.net/dongdingzhuo/article/details/97110642

gradle配置

plugins {
	id 'org.springframework.boot' version '2.2.1.RELEASE'
	id 'io.spring.dependency-management' version '1.0.8.RELEASE'
	id 'java'
}

group = 'com.learn'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenLocal()
	maven { url "http://maven.aliyun.com/nexus/content/groups/public/"}
	mavenCentral()

}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-quartz'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.1'
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'mysql:mysql-connector-java'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
	compile group: 'com.mchange', name: 'c3p0', version: '0.9.5.2'
}

test {
	useJUnitPlatform()
}

SQL脚本

quartz相关sql脚本

在quartz官网下载的压缩包docs/dbTables中可找到

#
# In your Quartz properties file, you'll need to set 
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#
#
# By: Ron Cordell - roncordell
#  I didn't see this anywhere, so I thought I'd post it here. This is the script from Quartz to create the tables in a MySQL database, modified to use INNODB instead of MYISAM.

DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;

CREATE TABLE QRTZ_JOB_DETAILS(
SCHED_NAME VARCHAR(120) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
JOB_CLASS_NAME VARCHAR(250) NOT NULL,
IS_DURABLE VARCHAR(1) NOT NULL,
IS_NONCONCURRENT VARCHAR(1) NOT NULL,
IS_UPDATE_DATA VARCHAR(1) NOT NULL,
REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
NEXT_FIRE_TIME BIGINT(13) NULL,
PREV_FIRE_TIME BIGINT(13) NULL,
PRIORITY INTEGER NULL,
TRIGGER_STATE VARCHAR(16) NOT NULL,
TRIGGER_TYPE VARCHAR(8) NOT NULL,
START_TIME BIGINT(13) NOT NULL,
END_TIME BIGINT(13) NULL,
CALENDAR_NAME VARCHAR(200) NULL,
MISFIRE_INSTR SMALLINT(2) NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) 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),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_CRON_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
CRON_EXPRESSION VARCHAR(120) NOT NULL,
TIME_ZONE_ID VARCHAR(80),
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SIMPROP_TRIGGERS
  (          
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    STR_PROP_1 VARCHAR(512) NULL,
    STR_PROP_2 VARCHAR(512) NULL,
    STR_PROP_3 VARCHAR(512) NULL,
    INT_PROP_1 INT NULL,
    INT_PROP_2 INT NULL,
    LONG_PROP_1 BIGINT NULL,
    LONG_PROP_2 BIGINT NULL,
    DEC_PROP_1 NUMERIC(13,4) NULL,
    DEC_PROP_2 NUMERIC(13,4) NULL,
    BOOL_PROP_1 VARCHAR(1) NULL,
    BOOL_PROP_2 VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) 
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_BLOB_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
BLOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_CALENDARS (
SCHED_NAME VARCHAR(120) NOT NULL,
CALENDAR_NAME VARCHAR(200) NOT NULL,
CALENDAR BLOB NOT NULL,
PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))
ENGINE=InnoDB;

CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;

CREATE TABLE QRTZ_FIRED_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
ENTRY_ID VARCHAR(95) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
FIRED_TIME BIGINT(13) NOT NULL,
SCHED_TIME BIGINT(13) NOT NULL,
PRIORITY INTEGER NOT NULL,
STATE VARCHAR(16) NOT NULL,
JOB_NAME VARCHAR(200) NULL,
JOB_GROUP VARCHAR(200) NULL,
IS_NONCONCURRENT VARCHAR(1) NULL,
REQUESTS_RECOVERY VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,ENTRY_ID))
ENGINE=InnoDB;

CREATE TABLE QRTZ_SCHEDULER_STATE (
SCHED_NAME VARCHAR(120) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
CHECKIN_INTERVAL BIGINT(13) NOT NULL,
PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))
ENGINE=InnoDB;

CREATE TABLE QRTZ_LOCKS (
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME))
ENGINE=InnoDB;

CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);

CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);

CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);

commit; 

自建quart_task表

-- ----------------------------
-- Table structure for quartz_task
-- ----------------------------
DROP TABLE IF EXISTS `quartz_task`;
CREATE TABLE `quartz_task` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `task_name` varchar(255) DEFAULT NULL COMMENT '任务名称',
  `group_name` varchar(255) DEFAULT NULL COMMENT 'group名称',
  `job_class` varchar(255) DEFAULT NULL COMMENT 'job所在类',
  `params` varchar(255) DEFAULT NULL COMMENT '参数',
  `cron` varchar(255) DEFAULT NULL COMMENT 'cron表达式',
  `status` tinyint(1) DEFAULT NULL COMMENT '状态',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of quartz_task
-- ----------------------------
INSERT INTO `quartz_task` VALUES ('1', 'job1', 'group1', 'com.learn.quartz.job.ScheduledTask01', '参数1', '0 1/2 * * * ?', '1');
INSERT INTO `quartz_task` VALUES ('2', 'job2', 'group2', 'com.learn.quartz.job.ScheduledTask02', '参数2', '0 0/2 * * * ?', '1');
INSERT INTO `quartz_task` VALUES ('3', 'job3', 'group3', 'com.learn.quartz.job.ScheduledTask03', '参数3', '0 0/1 * * * ?', '0');

SchedulerFactoryBean的两种配置方式

1、application.properties方式配置

SchedulerFactoryBean将会通过QuartzAutoConfiguration类自动生成实例,Scheduler也自动生成实例,其中jobFactory默认注入SpringBeanJobFactory(SpringBeanJobFactory支持Job实现类中Service Autowire注入和JobDataMap中的数据以setter方法注入),数据库连接池默认使用c3p0

server.port=8080
#数据库
spring.datasource.url=jdbc:mysql://xxxxxxxxxx
spring.datasource.username=root
spring.datasource.password=xxxxxxxx
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#mapper
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.configuration.map-underscore-to-camel-case=true

logging.level.root=info

# ===========================================================================
# SchedulerFactoryBean基本配置
# ===========================================================================
#启动延迟n秒
spring.quartz.startup-delay=1
#是否重写已存在的job
spring.quartz.overwrite-existing-jobs=true
#是否自动启动
spring.quartz.auto-startup=true
#quartz集群配置
# ===========================================================================
# Configure Main Scheduler Properties 调度器属性
# ===========================================================================
#调度标识名 集群中每一个实例都必须使用相同的名称
spring.quartz.properties.org.quartz.scheduler.instanceName = DefaultQuartzScheduler
#ID设置为自动获取 每一个必须不同
spring.quartz.properties.org.quartz.scheduler.instanceId = AUTO

#============================================================================
# Configure ThreadPool
#============================================================================
#线程池的实现类(一般使用SimpleThreadPool即可满足几乎所有用户的需求)
spring.quartz.properties.org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
#指定线程数,至少为1(无默认值)(一般设置为1-100直接的整数合适)
spring.quartz.properties.org.quartz.threadPool.threadCount = 25
#设置线程的优先级(最大为java.lang.Thread.MAX_PRIORITY 10,最小为Thread.MIN_PRIORITY 1,默认为5)
spring.quartz.properties.org.quartz.threadPool.threadPriority = 5
#============================================================================
# Configure JobStore
#============================================================================
# 触发失败阈值,未超过触发时间60s则直接触发,若超过则失火,log信息Handling 1 trigger(s) that missed their scheduled fire-time.
spring.quartz.properties.org.quartz.jobStore.misfireThreshold = 60000
#数据保存方式为数据库持久化
spring.quartz.properties.org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#数据库代理类,一般org.quartz.impl.jdbcjobstore.StdJDBCDelegate可以满足大部分数据库
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#JobDataMaps是否都为String类型
spring.quartz.properties.org.quartz.jobStore.useProperties = false
#表的前缀,默认QRTZ_
spring.quartz.properties.org.quartz.jobStore.tablePrefix = QRTZ_
#是否加入集群
spring.quartz.properties.org.quartz.jobStore.isClustered = true
#调度实例失效的检查时间间隔
spring.quartz.properties.org.quartz.jobStore.clusterCheckinInterval = 20000
#名称必须与下方org.quartz.dataSource.@Name@.xxx一致
spring.quartz.properties.org.quartz.jobStore.dataSource = myDS
#============================================================================
# Configure Datasources
#============================================================================
#数据库引擎
spring.quartz.properties.org.quartz.dataSource.myDS.driver = com.mysql.cj.jdbc.Driver
#数据库连接
spring.quartz.properties.org.quartz.dataSource.myDS.URL = jdbc:mysql://xxxxxxxxxx
#数据库用户
spring.quartz.properties.org.quartz.dataSource.myDS.user = root
#数据库密码
spring.quartz.properties.org.quartz.dataSource.myDS.password = xxxxxx
#允许最大连接
spring.quartz.properties.org.quartz.dataSource.myDS.maxConnections = 5
#验证查询sql,可以不设置
spring.quartz.properties.org.quartz.dataSource.myDS.validationQuery=select 0 from dual

2、@Configuration方式配置

SchedulerFactoryBean 数据源使用spring项目自动生成的,配置文件中不用另配Datasources设置,可设置事务管理(第一种貌似没?)

application.properties配置数据库

server.port=8080
#数据库
spring.datasource.url=jdbc:mysql://xxxxxxxxxx
spring.datasource.username=root
spring.datasource.password=xxxxxxxx
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#mapper
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.configuration.map-underscore-to-camel-case=true

logging.level.root=info

quartz.properties

# ===========================================================================
# Configure Main Scheduler Properties 调度器属性
# ===========================================================================
#调度标识名 集群中每一个实例都必须使用相同的名称
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
#ID设置为自动获取 每一个必须不同
org.quartz.scheduler.instanceId = AUTO
#============================================================================
# Configure ThreadPool
#============================================================================
#线程池的实现类(一般使用SimpleThreadPool即可满足几乎所有用户的需求)
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
#指定线程数,至少为1(无默认值)(一般设置为1-100直接的整数合适)
org.quartz.threadPool.threadCount = 25
#设置线程的优先级(最大为java.lang.Thread.MAX_PRIORITY 10,最小为Thread.MIN_PRIORITY 1,默认为5)
org.quartz.threadPool.threadPriority = 5
#============================================================================
# Configure JobStore
#============================================================================
# 触发失败阈值,未超过触发时间60s则直接触发,若超过则失火,log信息Handling 1 trigger(s) that missed their scheduled fire-time.
org.quartz.jobStore.misfireThreshold = 60000
#数据保存方式为数据库持久化
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#数据库代理类,一般org.quartz.impl.jdbcjobstore.StdJDBCDelegate可以满足大部分数据库
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#JobDataMaps是否都为String类型
org.quartz.jobStore.useProperties = false
#表的前缀,默认QRTZ_
org.quartz.jobStore.tablePrefix = QRTZ_
#是否加入集群
org.quartz.jobStore.isClustered = true
#调度实例失效的检查时间间隔
org.quartz.jobStore.clusterCheckinInterval = 20000

QuartzConfigurer 类

@Configuration
public class QuartzConfigurer {

    @Autowired
    private DataSource dataSource;

    @Autowired
    private PlatformTransactionManager txManager;

    @Bean(name = "quartzScheduler")
    public SchedulerFactoryBean quartzScheduler() throws IOException {
        //创建SchedulerFactoryBean
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        //设置调度器自动运行
        factory.setAutoStartup(true);
        //设置配置文件位置
        factory.setConfigLocation(new ClassPathResource("/quartz.properties"));
        //设置job工厂,使job可以自动注入
        SpringBeanJobFactory jobFactory = new SpringBeanJobFactory();
        factory.setJobFactory(jobFactory);
        //设置数据源
        factory.setDataSource(dataSource);
        //设置事务
        factory.setTransactionManager(txManager);
        //设置重写已存在的Job
        factory.setOverwriteExistingJobs(true);
        return factory;
    }

    @Bean(name = "scheduler")
    public Scheduler scheduler() throws IOException {
        return quartzScheduler().getScheduler();
    }
}

pojo层

自建表quart_task的实体类

QuartzTask 实体类

@Setter
@Getter
@ToString
public class QuartzTask implements Serializable {
    private static final long serialVersionUID = 2605762417952271886L;
    /**
     * 主键
     */
    private Integer id;

    /**
     * 任务名称
     */
    private String taskName;

    /**
     * group名称
     */
    private String groupName;

    /**
     * job所在类
     */
    private String jobClass;

    /**
     * 参数
     */
    private String params;

    /**
     * cron表达式
     */
    private String cron;

    /**
     * 状态
     */
    private Boolean status;

}

dao层

自建表quart_task的mapper

QuartzTaskMapper 接口类

public interface QuartzTaskMapper {

    QuartzTask selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(QuartzTask record);

    List<QuartzTask> getAllTask();
}

QuartzTaskMapper.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.learn.quartz.dao.QuartzTaskMapper">
    <resultMap id="BaseResultMap" type="com.learn.quartz.pojo.QuartzTask">
        <id column="id" jdbcType="INTEGER" property="id" />
        <result column="task_name" jdbcType="VARCHAR" property="taskName" />
        <result column="group_name" jdbcType="VARCHAR" property="groupName" />
        <result column="job_class" jdbcType="VARCHAR" property="jobClass" />
        <result column="params" jdbcType="VARCHAR" property="params" />
        <result column="cron" jdbcType="VARCHAR" property="cron" />
        <result column="status" jdbcType="BIT" property="status" />
    </resultMap>
    <sql id="Base_Column_List">
    id, task_name, group_name, job_class, params, cron, `status`
  </sql>

    <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List" />
        from quartz_task
        where id = #{id,jdbcType=INTEGER}
    </select>

    <update id="updateByPrimaryKeySelective" parameterType="com.learn.quartz.pojo.QuartzTask">
        update quartz_task
        <set>
            <if test="taskName != null">
                task_name = #{taskName,jdbcType=VARCHAR},
            </if>
            <if test="groupName != null">
                group_name = #{groupName,jdbcType=VARCHAR},
            </if>
            <if test="jobClass != null">
                job_class = #{jobClass,jdbcType=VARCHAR},
            </if>
            <if test="params != null">
                params = #{params,jdbcType=VARCHAR},
            </if>
            <if test="cron != null">
                cron = #{cron,jdbcType=VARCHAR},
            </if>
            <if test="status != null">
                `status` = #{status,jdbcType=BIT},
            </if>
        </set>
        where id = #{id,jdbcType=INTEGER}
    </update>

    <select id="getAllTask" resultType="com.learn.quartz.pojo.QuartzTask">
        select
        	<include refid="Base_Column_List"/>
        from
        	quartz_task
    </select>

</mapper>

service接口层

QuartzTaskService接口类

自建表quart_task的service

public interface QuartzTaskService {

    QuartzTask selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(QuartzTask record);

    List<QuartzTask> getAllTask();
}

QuartzService接口类

Quartz服务层接口

public interface QuartzService {
    void initAllTask(List<QuartzTask> scheduledTaskBeanList) throws Exception;

    void addJob(QuartzTask scheduledTask);

    void updateJob(QuartzTask quartzTask);

    void deleteJob(QuartzTask quartzTask);

}

service接口实现层

QuartzTaskService接口实现类

@Service
public class QuartzTaskServiceImpl implements QuartzTaskService {

    @Autowired
    private QuartzTaskMapper quartzTaskMapper;

    @Override
    public QuartzTask selectByPrimaryKey(Integer id) {
        return quartzTaskMapper.selectByPrimaryKey(id);
    }

    @Override
    public int updateByPrimaryKeySelective(QuartzTask record) {
        return quartzTaskMapper.updateByPrimaryKeySelective(record);
    }

    @Override
    public List<QuartzTask> getAllTask() {
        return quartzTaskMapper.getAllTask();
    }

}

QuartzService接口实现类

Quartz服务层接口

package com.learn.quartz.service.impl;

import com.learn.quartz.pojo.QuartzTask;
import com.learn.quartz.service.QuartzService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class QuartzServiceImpl implements QuartzService {

    @Autowired
    private Scheduler scheduler;

    /**
     * 程序启动时初始化 ==> 启动所有正常状态的任务
     */
    @Override
    public void initAllTask(List<QuartzTask> quartzTaskList) throws Exception {
        log.info("程序启动 ==> 初始化所有任务开始 !");
        if (CollectionUtils.isEmpty(quartzTaskList)) {
            scheduler.shutdown();
            return;
        }
        for (QuartzTask quartzTask : quartzTaskList) {
            //判断是否启动状态
            if (quartzTask.getStatus()) {
                this.addJob(quartzTask);
            }
        }
        log.info("程序启动 ==> 初始化所有任务结束 !");
    }

    /**
     * 创建job,可传参
     *
     * @param quartzTask 任务名称
     */
    @Override
    public void addJob(QuartzTask quartzTask) {
        String taskName = quartzTask.getTaskName();
        String groupName = quartzTask.getGroupName();
        String cron = quartzTask.getCron();
        TriggerKey triggerKey = TriggerKey.triggerKey(taskName, groupName);
        try {
            boolean result = scheduler.checkExists(triggerKey);
            //job已存在,直接返回
            log.info("checkExists quartTask = {} , result = {}", quartzTask, result);
            if (result) {
                return;
            }
            Class<?> aClass = Class.forName(quartzTask.getJobClass());
            //构建job信息
            JobDetail jobDetail = JobBuilder.newJob(((Job) aClass.newInstance()).getClass()).withIdentity(taskName, groupName).build();
            //表达式调度构建器(即任务执行的时间)
            /*使用withMisfireHandlingInstructionDoNothing是因为重启项目时之前添加的job都算失火
            ,默认配置withMisfireHandlingInstructionFireAndProceed失火后会立即执行一遍,
            而withMisfireHandlingInstructionDoNothing失火后不会立即执行,而是按照下一个cron执行
            */
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron).withMisfireHandlingInstructionDoNothing();
            //按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().startNow().withIdentity(taskName, groupName).withSchedule(scheduleBuilder).build();
            //获得JobDataMap,写入数据
            Map<String, Object> paramMap = new HashMap<>();
            paramMap.put("id", quartzTask.getId());
            paramMap.put("params", quartzTask.getParams());
            trigger.getJobDataMap().putAll(paramMap);
            scheduler.scheduleJob(jobDetail, trigger);
            log.info("addJob quartTask = {} is success", quartzTask);
        } catch (Exception e) {
            log.error("addJob quartTask = {} is fail, msg = {}", quartzTask, e);
        }
    }


    /**
     * job 更新,更新频率和参数
     *
     * @param quartzTask 任务名称
     */
    @Override
    public void updateJob(QuartzTask quartzTask) {
        String taskName = quartzTask.getTaskName();
        String groupName = quartzTask.getGroupName();
        String cron = quartzTask.getCron();
        TriggerKey triggerKey = TriggerKey.triggerKey(taskName, groupName);
        try {
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            if (cron != null) {
                // 表达式调度构建器
                CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron).withMisfireHandlingInstructionDoNothing();
                // 按新的cronExpression表达式重新构建trigger
                trigger = trigger.getTriggerBuilder().startNow().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            }
            //获得JobDataMap,写入数据
            Map<String, Object> paramMap = new HashMap<>();
            paramMap.put("id", quartzTask.getId());
            paramMap.put("params", quartzTask.getParams());
            trigger.getJobDataMap().putAll(paramMap);
            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);
            log.info("updateJob quartTask = {} is success", quartzTask);
        } catch (SchedulerException e) {
            log.error("updateJob quartTask = {} is fail, msg = {}", quartzTask, e);
        }
    }

    /**
     * job 删除
     *
     * @param quartzTask 任务名称
     */
    @Override
    public void deleteJob(QuartzTask quartzTask) {
        String taskName = quartzTask.getTaskName();
        String groupName = quartzTask.getGroupName();
        try {
            scheduler.pauseTrigger(TriggerKey.triggerKey(taskName, groupName));
            scheduler.unscheduleJob(TriggerKey.triggerKey(taskName, groupName));
            scheduler.deleteJob(JobKey.jobKey(taskName, groupName));
            log.info("deleteJob quartTask = {} is success", quartzTask);
        } catch (SchedulerException e) {
            log.error("deleteJob quartTask = {} is fail, msg = {}", quartzTask, e);
        }
    }
}

需要执行的定时任务Job

ScheduledTask01(02、03类似)

此类全类名必须与quart_task表中的class_name一致。
QuartzTaskService和id、params通过set方法注入(jobFactory必须注入SpringBeanJobFactory才生效)

@Slf4j
@Setter
public class ScheduledTask01 implements Job {

    private QuartzTaskService quartzTaskService;

    private Integer id;

    private String params;

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        if (quartzTaskService!=null){
            QuartzTask quartzTask = quartzTaskService.selectByPrimaryKey(id);
            log.info("ScheduledTask => 01 quartzTask = {}", quartzTask);
        }
        log.info("ScheduledTask => 01 run 当前线程名称 {}, Data = {}", Thread.currentThread().getName(), params);
    }
}

自启动定时任务

项目启动后会先查询全部QuartzTask,然后status为true的持久化进job(quartz官方提供的表里 QRTZ_TRIGGERS、QRTZ_CRON_TRIGGERS、QRTZ_JOB_DETAILS),而原本就已经持久化过的无操作(重新添加已存在的job会报错)

/**
 * @see @Order注解的执行优先级是按value值从小到大顺序。
 * 项目启动完毕后开启需要自启的任务
 */
@Component
@Order(value = 1)
@Slf4j
public class ScheduledTaskRunner implements ApplicationRunner {

    @Autowired
    private QuartzTaskService quartzTaskService;

    @Autowired
    private QuartzService quartzService;

    /**
     * 程序启动完毕后,需要自启的任务
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info(" >>>>>> 项目启动完毕, 开启 => 需要自启的任务 开始!");
        List<QuartzTask> activatedTaskList = quartzTaskService.getAllTask();
        quartzService.initAllTask(activatedTaskList);
        log.info(" >>>>>> 项目启动完毕, 开启 => 需要自启的任务 结束!");
    }
}

Controller层

主要修改状态和cron

@RestController
@Slf4j
public class JobController {

    @Autowired
    private QuartzService quartzService;

    @Autowired
    private QuartzTaskService quartzTaskService;

    /**
     * 修改任务的cron
     *
     * @return
     */
    @RequestMapping("/updateJobCron")
    public Object updateJobCron(Integer id, String cron) {
        QuartzTask quartzTask = quartzTaskService.selectByPrimaryKey(id);
        Map<String, String> resultMap = new HashMap<>();
        //如果存在quartzTask对象
        if (quartzTask != null) {
            //修改任务的cron
            quartzTask.setCron(cron);
            //更新quart_task
            quartzTaskService.updateByPrimaryKeySelective(quartzTask);
            //如果启用状态则修改当前已持久化的job
            if (quartzTask.getStatus()) {
                quartzService.updateJob(quartzTask);
            }
            resultMap.put("status", "0");
            resultMap.put("msg", "修改cron成功");
            return resultMap;
        }
        //不存在quartzTask对象
        resultMap.put("status", "1");
        resultMap.put("msg", "修改cron失败");
        return resultMap;
    }

    /**
     * 是否启用接口
     *
     * @return
     */
    @RequestMapping("/updateJobStatus")
    public Object updateJobStatus(Integer id, Boolean status) {
        QuartzTask quartzTask = quartzTaskService.selectByPrimaryKey(id);

        Map<String, String> resultMap = new HashMap<>();
        if (quartzTask != null) {
            //修改任务的启用状态
            quartzTask.setStatus(status);
            //更新quart_task
            quartzTaskService.updateByPrimaryKeySelective(quartzTask);
            //根据status判断是新增job还是删除job
            if (status) {
                quartzService.addJob(quartzTask);
            } else {
                quartzService.deleteJob(quartzTask);
            }
            resultMap.put("status", "0");
            resultMap.put("msg", "修改状态成功");
            return resultMap;
        }
        resultMap.put("status", "1");
        resultMap.put("msg", "修改状态失败");
        return resultMap;
    }
}

最后启动项目即可

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值